diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..2e05db2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +*.n +*.php +config.xml +www/plugin/* +Makefile +tmp/* +*node_modules* +*.map +.haxelib +html +.temp +*.tar + +lang/master/tpl/plugin/* + +lang/fr/tpl/* +lang/en/tpl/* +lang/de/tpl/* +lang/es/tpl/* + +lang/fr/tmp/*.n +lang/en/tmp/*.n +lang/de/tmp/*.n +lang/es/tmp/*.n + +www/lang/*.mo + +www/file/* + +www/js/app.js +www/js/libs.js + + +www/css/.sass-cache +www/css/*.css + + diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..d6a9326 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + diff --git a/Makefile.dist b/Makefile.dist new file mode 100644 index 0000000..9a121c3 --- /dev/null +++ b/Makefile.dist @@ -0,0 +1,93 @@ +ROPTS=-zav --no-p --chmod=u=rw,g=rw,o=r --delete --exclude=www/.htaccess --exclude=.svn --exclude=.git --exclude=*.mtt --exclude=tpl/css --exclude=www/file --exclude=*node_modules* --exclude=*.php --exclude=tests.n +LANG=fr +PLUGINS?=0 +ENV?="dev" + +#To compile project with plugins, add PLUGINS=1 or type `export PLUGINS=1` + +install: + #copy config file from template + cp config.xml.dist config.xml + @if [ $(ENV) = "dev" ]; then \ + make install_dev; \ + fi + +#setup dev environnement from source +install_dev: + #set config file to debug=1 + @sed -e 's?debug=.*?debug="1"?g' --in-place config.xml + #install haxe dependencies in .haxelib + haxelib newrepo + haxelib -always install cagette.hxml + haxelib -always install cagetteJs.hxml + #template tools + haxelib run templo + sudo mv temploc2 /usr/bin + #extra plugins + @if [ $(PLUGINS) = 1 ]; then \ + haxelib git cagette-pro git@bitbucket.org:bubar/cagette-pro.git; \ + haxelib git cagette-hosted git@bitbucket.org:bubar/cagette-hosted.git; \ + haxelib git cagette-wholesale-order git@github.com:bablukid/cagette-wholesale-order.git; \ + fi + #install npm dependencies + npm install + npm run libs:dev + npm run build:js + #compile + @make css + @make compile + @make frontend + @echo "Well, it looks like everything is fine : librairies are installed, backend and frontend has been compiled !" + + +#compile backend to Neko +compile: + @if [ $(PLUGINS) = 1 ]; then \ + echo "compile Cagette.net with plugins"; \ + haxe cagetteAllPlugins.hxml; \ + else \ + echo "compile Cagette.net core"; \ + haxe cagette.hxml; \ + fi + + +#compile SASS files to CSS +css: + npm run build:sass + +#compile frontend to JS +frontend: + @if [ $(PLUGINS) = 1 ]; then \ + haxe cagetteAllPluginsJs.hxml; \ + else \ + haxe cagetteJs.hxml; \ + fi + +#update POT file from source +i18n: + haxe potGeneration.hxml + +#compile templates in each language, required for production env. +templates: + haxe cagette.hxml -D i18n_generation + @make LANG=fr ctemplates + @make LANG=en ctemplates + @make LANG=de ctemplates + +ctemplates: + (cd lang/$(LANG)/tpl; temploc2 -macros macros.mtt -output ../tmp/ *.mtt */*.mtt */*/*.mtt */*/*/*.mtt */*/*/*/*.mtt) + +#Compile and run unit tests +tests: + @if [ $(PLUGINS) = 1 ]; then \ + haxe testsPlugins.hxml; \ + else \ + haxe tests.hxml; \ + fi + @docker-compose run --workdir="/var/www/www" neko neko tests.n mysql://root:root@mysql/tests + +cp_plugin: + cp -R .haxelib/cagette-hosted/git/tpl/* lang/master/tpl/plugin/hosted/ + cp -R .haxelib/cagette-pro/git/tpl/* lang/master/tpl/plugin/pro/ + cp -R .haxelib/cagette-wholesale-order/git/tpl/* lang/master/tpl/plugin/who/ + diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 67e863e..422b4dd --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ -CAGETTE from Artcode -==================== +# Cagette.net +Le logiciel libre des circuits courts. + +[![Build Status](https://travis-ci.org/bablukid/cagette.svg?branch=master)](https://travis-ci.org/bablukid/cagette) + +http://www.cagette.net + +Documentation : http://www.cagette.net/wiki + +English : + +Cagette.net is an open source web app to help people build a better and sustainable food system. +Some people call it a "foodhub" : A mix between a groupware and a marketplace, helping consumers to order food from local farmers and producers. + +## Build & install + +Read the Developer page on the wiki to know more about this : http://wiki.cagette.net/dev:index diff --git a/build/travis-ci-apache b/build/travis-ci-apache new file mode 100644 index 0000000..c6ebf18 --- /dev/null +++ b/build/travis-ci-apache @@ -0,0 +1,11 @@ + + ServerName cagette + DirectoryIndex index.n + DocumentRoot %TRAVIS_BUILD_DIR%/www + + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + \ No newline at end of file diff --git a/cagette-php.hxml b/cagette-php.hxml new file mode 100644 index 0000000..28ed845 --- /dev/null +++ b/cagette-php.hxml @@ -0,0 +1,22 @@ +-lib dbadmin +-lib templo +-lib weblog +-lib sugoi +-lib hxevents +-lib cagette-hosted +-lib hscript +-lib ufront-mail +-cp common +-cp src +-php www/ +-D spod_macro +-main App +-D debugip=localhost:18080 +-D weblogid=cagette +--next +-dce no +-cp common +-cp js +-lib sugoi +-main App +-js www/js/app.js diff --git a/cagette.hxml b/cagette.hxml new file mode 100644 index 0000000..aecc8ef --- /dev/null +++ b/cagette.hxml @@ -0,0 +1,18 @@ +-lib templo:3.0.0 +-lib hxevents:0.4.0 +-lib thx.semver:0.2.2 +-lib thx.csv:0.2.0 +-lib datetime:3.1.1 +-lib hscript:2.3.0 +-lib smtpmailer:0.4.0 +-lib tink_json:0.9.0 + +-lib sugoi:git:git@artcode.re:cagette/sugoi.git +-lib record-macros:git:git@artcode.re:cagette/record-macros.git +-lib dbadmin:git:git@artcode.re:cagette/dbadmin.git + +-cp common +-cp src +-cp lang/master/tpl +-neko www/index.n +-main App diff --git a/cagetteAllPlugins.hxml b/cagetteAllPlugins.hxml new file mode 100755 index 0000000..6762dc0 --- /dev/null +++ b/cagetteAllPlugins.hxml @@ -0,0 +1,26 @@ +-lib templo +-lib hxevents +-lib thx.semver +-lib thx.csv +-lib datetime +-lib hscript +-lib smtpmailer +-lib tink_json + +-lib sugoi:git:https://github.com/bablukid/sugoi#master +-lib record-macros:git:https://github.com/HaxeFoundation/record-macros#master +-lib dbadmin:git:https://github.com/ncannasse/dbadmin.git#master + +-lib cagette-pro:git:https://bubar@bitbucket.org/bubar/cagette-pro.git#master +-lib cagette-hosted:git:https://bubar@bitbucket.org/bubar/cagette-hosted.git#master +-lib cagette-wholesale-order:git:https://github.com/bablukid/cagette-wholesale-order.git#master + +-cp common +-cp src +-cp lang/master/tpl + +-D plugins + + +-neko www/index.n +-main App \ No newline at end of file diff --git a/cagetteAllPlugins.hxproj b/cagetteAllPlugins.hxproj new file mode 100755 index 0000000..f6f1362 --- /dev/null +++ b/cagetteAllPlugins.hxproj @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cagetteAllPluginsJs.hxml b/cagetteAllPluginsJs.hxml new file mode 100644 index 0000000..0e7469f --- /dev/null +++ b/cagetteAllPluginsJs.hxml @@ -0,0 +1,17 @@ +-dce no +-cp common +-cp js +-lib sugoi:git:https://github.com/bablukid/sugoi#i18n +-lib hxleaflet:git:https://github.com/bablukid/hxleaflet +-lib cagette-hosted +-lib react +-lib react-router-4 +-lib modular +-lib tink_json + +-D plugins +-D no-deprecation-warnings + +-main App +-js www/js/app.js + diff --git a/cagetteJs.hxml b/cagetteJs.hxml new file mode 100644 index 0000000..2484546 --- /dev/null +++ b/cagetteJs.hxml @@ -0,0 +1,13 @@ +-dce no +-cp common +-cp js +-lib sugoi:git:git@artcode.re:cagette/sugoi.git +-lib hxleaflet:git:git@artcode.re:cagette/hxleaflet.git +-lib react:1.4.0 +-lib react-router-4:0.2.3 +-lib modular:0.10.15 +-lib tink_json:0.9.0 +-main App +-debug +-js www/js/app.js +-D no-deprecation-warnings diff --git a/common/Common.hx b/common/Common.hx new file mode 100755 index 0000000..7c1849d --- /dev/null +++ b/common/Common.hx @@ -0,0 +1,472 @@ +/** + * Common.hx : Shared entities between server and client + */ + +@:keep +typedef OrderSimple = { + products: Array<{ + product:ProductInfo, + quantity:Int + }>, + total:Float +} + +//A temporary order, waiting for being paid and definitely recorded. +@:keep +typedef OrderInSession = { + products:Array <{ + productId:Int, + quantity:Float, + #if !js + ?product:db.Product, + ?distributionId:Int, + #end + } > , + ?userId:Int, + total:Float, //price to pay + ?paymentOp:Int, //payment operation ID +} + +@:keep +typedef ProductInfo = { + id : Int, + name : String, + ref : Null, + image : Null, + contractId : Int, + price : Float, + vat : Null, //VAT rate + vatValue : Null, //amount of VAT included in the price + contractTax : Null, //pourcentage de commission défini dans le contrat + contractTaxName : Null, //label pour la commission : ex: "frais divers" + desc : Null, + categories : Null>, //used in old shop + subcategories : Null>, //used in new shop + orderable : Bool, //can be currently ordered + stock: Null, //available stock + hasFloatQt : Bool, + qt:Null, + unitType:Null, + organic:Bool, + variablePrice:Bool, + wholesale:Bool, + active:Bool, + ?distributionId:Null, //in the context of a distrib +} + +@:keep +typedef ProductWithQuantity = { + product: ProductInfo, + quantity: Int +} + +enum Unit{ + Piece; + Kilogram; + Gram; + Litre; + Centilitre; +} + +typedef CategoryInfo = { + id:Int, + name:String, + ?subcategories:Array +} + +/** + * datas used with the "tagger" ajax class + */ +@:keep +typedef TaggerInfos = { + products:Array<{product:ProductInfo,categories:Array}>,//tagged products + categories : Array<{id:Int,categoryGroupName:String,color:String,tags:Array<{id:Int,name:String}>}>, //groupe de categories + tags +} + +/** + * Links in navbars for plugins + */ +typedef Link = { + id:String, + link:String, + name:String, + ?icon:String, +} + +typedef Block = { + id:String, + title:String, + ?icon:String, + html:String +} + +typedef UserOrder = { + id:Int, + userId:Int, + userName:String, + ?userEmail : String, + + ?userId2:Int, + ?userName2:String, + ?userEmail2:String, + + //deprecated + ?productId:Int, + ?productRef:String, + ?productName:String, + ?productPrice:Float, + ?productImage:String, + ?productQt:Float, + ?productUnit:Unit, + + ?productHasFloatQt:Bool, + ?productHasVariablePrice:Bool, + + //new way + ?product:ProductInfo, + + quantity:Float, + smartQt:String, + subTotal:Float, + + fees:Null, + percentageName:Null, + percentageValue:Null, + total:Float, + + //flags + paid:Bool, + invertSharedOrder:Bool, + canceled:Bool, + canModify:Bool, + + contractId:Int, + contractName:String, +} + + + +typedef PlaceInfos = { + id:Int, + name:String, + address1:String, + address2:String, + zipCode:String, + city:String, + latitude:Float, + longitude:Float +} + +typedef UserInfo = { + id:Int, + name:String, + email:String, + +} + + +typedef GroupOnMap = { + id:Int, + name:String, + image:String, + place:PlaceInfos, +} + +enum OrderFlags { + InvertSharedOrder; //invert order when there is a shared/alternated order + //Canceled; //flag for cancelled orders, qt should always be 0 +} + + +typedef OrderByProduct = { + quantity:Float, + smartQt:String, + pid:Int, + pname:String, + ref:String, + priceHT:Float, + priceTTC:Float, + vat:Float, + totalHT:Float, + totalTTC:Float, + weightOrVolume:String, +}; +typedef OrderByEndDate = {date: String,contracts: Array}; + +/** + Event enum used for plugins. + + As in most CMS event systems, + the events (or "triggers") can be caught by plugins + to perform an action or modifiy data carried by the event. + +**/ +enum Event { + + Page(uri:String); //a page is displayed + Nav(nav:Array, name:String, ?id:Int); //a navigation is displayed, optionnal object id if needed + Blocks(blocks:Array, name:String, ?context:Dynamic); //HTML blocks that can be displayed on a page + + #if sys + SendEmail(message : sugoi.mail.Mail); //an email is sent + NewMember(user:db.User,group:db.Amap); //a new member is added to a group + NewGroup(group:db.Amap, author:db.User); //a new group is created + + //Distributions + PreNewDistrib(contract:db.Contract); //when displaying the insert distribution form + NewDistrib(distrib:db.Distribution); //when a new distrinbution is created + PreEditDistrib(distrib:db.Distribution); + EditDistrib(distrib:db.Distribution); + DeleteDistrib(distrib:db.Distribution); + PreNewDistribCycle(cycle:db.DistributionCycle); + NewDistribCycle(cycle:db.DistributionCycle); + MultiDistribEvent(md:MultiDistrib); + + //Products + PreNewProduct(contract:db.Contract); //when displaying the insert distribution form + NewProduct(product:db.Product); //when a new product is created + PreEditProduct(product:db.Product); + EditProduct(product:db.Product); + DeleteProduct(product:db.Product); + BatchEnableProducts(data:{pids:Array,enable:Bool}); + ProductInfosEvent(p:ProductInfo,?d:db.Distribution); //when infos about a product are displayed + + //Contracts + EditContract(contract:db.Contract,form:sugoi.form.Form); + DuplicateContract(contract:db.Contract); + DeleteContract(contract:db.Contract); + + //crons + DailyCron; + HourlyCron; + MinutelyCron; + + //orders + MakeOrder(orders:Array); + StockMove(order:{product:db.Product, move:Float}); //when a stock is modified + + //payments + GetPaymentTypes(data:{types:Array}); + NewOperation(op:db.Operation); + + #end + +} + + +/* + * Product Taxonomy structure + */ +typedef TxpDictionnary = { + products:Map, + categories:Map, + subCategories:Map, + +} + + +/* + * Tutorials + */ +enum TutoAction { + TAPage(uri:String); + TANext; + +} +enum TutoPlacement { + TPTop; + TPBottom; + TPLeft; + TPRight; +} + +class TutoDatas { + + public static var TUTOS; + + #if js + //async + public static function get(tuto:String, callback:Dynamic->Void){ + #if !test + sugoi.i18n.Locale.init(App.instance.LANG, function(t:sugoi.i18n.GetText){ + App.instance.t = t; + init(t); + var tuto = TUTOS.get(tuto); + callback(tuto); + }); + #end + } + #else + //sync + public static function get(tuto:String):{name:String, steps:Array<{element:String,text:String,action:TutoAction,placement:TutoPlacement}>} + { + sugoi.i18n.Locale.init(App.current.getLang()); + init(sugoi.i18n.Locale.texts); + return TUTOS.get(tuto); + } + #end + + static function init(t:sugoi.i18n.GetText){ + + TUTOS = [ + "intro" => { + name:t._("Guided tour for the coordinator"), + steps:[ + { + element:null, + text:t._("

In order to better discover Cagette.net, we propose to do a guided tour of the user interface of the software.
You will then have a global overview on the different tools that are available to you.

You will be able to stop and start again this tutorial whenever you want.

"), + action: TANext, + placement : null + }, + { + element:"ul.nav.navbar-left", + text:t._( "This part of the navigation bar is visible by all members.
It allows to access to the three main pages:
  • The home page which displays orders and the delivery planning.
  • On the My account page, you can update your personal information and check your orders history
  • On the Farmers page, you can see all farmers and coordinators of the group
"), + action: TANext, + placement : TPBottom + }, + { + element:"ul.nav.navbar-right", + text:t._("This part is for coordinators only.Here you will be able to manage the register of members, orders, products, etc.

Now click on the Members section

"), + action: TAPage("/member"), + placement : TPBottom + + },{ + element:".article .table td:first", + text:t._("The purpose of this section is to administrate the list of your members.
Every time that you register a new membrer, an account will be created for him/her. Now the member can join you at Cagette.net and order or consult the planning of the deliveries.

Now click on a member in the list

"), + action: TAPage("/member/view/*"), + placement : TPRight + },{ + element:".article:first", + text:t._("This is the page of a member. Here you can :
  • see and change their contact details
  • manage the membership fee of your group
  • see a summary of their orders
"), + action: TANext, + placement : TPRight + },{ + element:"ul.nav #contractadmin", + text:t._("Now let's have a look at the contracts section which is very important for coordinators."), + action: TAPage("/contractAdmin"), + placement : TPBottom + },{ + element:"#contracts", + text:t._("Here you find the list of contracts.They inculde a start date, a end date, and represent your relationship with a farmer.

Here you can manage :

  • the list of products of this farmer
  • the orders of members for this farmer
  • and plan the delivery schedule

"), + action: TANext, + placement : TPBottom + + },{ + element:"#places", + text:t._("Here you can manage the list of delivery places.
Don't forget to key-in the complete address as a map will be displayed based on this address"), + action: TANext, + placement : TPTop + + },{ + element:"#contracts table .btn:first", + text:t._("Let's look closer at how to manage a contract. Click on this button"), + action: TAPage("/contractAdmin/view/*"), + placement : TPBottom + + },{ + element:".table.table-bordered:first", + text:t._("Here is a summary of the contract.
There are two types of contracts:
  • Constant contracts: the member commits on buying the same products during the whole duration of the contract
  • Variable contracts: the member can choose what he buys for each delivery.
"), + action: TANext, + placement : TPRight + + },{ + element:"#subnav #products", + text:t._("Let's see now the page Products"), + action : TAPage("/contractAdmin/products/*"), + placement:TPRight + },{ + element:".article .table", + text:t._("On this page, you can manage the list of products offered by this supplier.
Define at least the name and the price of products. It is also possible to add a description and a picture."), + action: TANext, + placement : TPTop + + },{ + element:"#subnav #deliveries", + text:t._("Let's see the deliveries page"), + action : TAPage("/contractAdmin/distributions/*"), + placement:TPRight + },{ + element:".article .table", + text:t._("Here we can manage the list of deliveries for this supplier.
In the software, a delivery has a date, a start time, and an end time. The location of the delivery must also be defined, by using the list that we have already seen."), + action: TANext, + placement : TPLeft + + },{ + element:"#subnav #orders", + text:t._("Let's see now the Orders page"), + action : TAPage("/contractAdmin/orders/*"), //can fail if the contract is variable because the uri is different + placement:TPRight + },{ + element:".article .table", + text:t._("Here we can manage the list of orders for this supplier.
If you choose to \"open orders\" to members, they will be able to make their orders online themselves.
This page will centralize automatically the orders for this supplier. Otherwise, as a coordinator, you will be able to enter orders on behalf of a member."), + action: TANext, + placement : TPLeft + + },{ + element:"ul.nav #messages", + text:t._("

We have seen the main features related to contracts.

Let's see the messaging section.

"), + action: TAPage("/messages"), + placement : TPBottom + + },{ + element:null, + text:t._("

The messaging section allows you to send e-mails to different lists of members. It is not necessary anymore to maintain a lot of lists of e-mails depending on contracts, as all these lists are automatically generated.

E-mails are sent with your e-mail address as sender, so you will receive answers in your own mailbox.

"), + action: TANext, + placement : null + + },{ + element:"ul.nav #amapadmin", + text:t._("Click here now on this page"), + action : TAPage("/amapadmin"), + placement : TPBottom, + },{ + element:"#subnav", + text:t._("

In this last page, you can configure everything that is related to your group.

The page Access rights is important as it is where you can define other coordinators among members. They will then be able to manage one or many contracts, send emails, etc.

"), + action:TANext, + placement : TPBottom + },{ + element:"#footer", + text:t._("

This is the last step of this tutorial. I hope that it gave you a good overview of this software.
To go further, do not hesitate to look at the documentation. The link is always available at the bottom of the screen.

"), + action:TANext, + placement : TPBottom + } + ] + }, + ]; + + } + + + +} + +/** + * Order Reports + */ +enum OrdersReportGroupOption{ + ByMember; + ByProduct; +} + +enum OrdersReportFormatOption{ + Table; + Csv; + PrintableList; //list de distrib ? +} + + +//Report Options : should be usable in an URL, an API call... +typedef OrdersReportOptions = { + //time scope + startDate:Date, + endDate:Date, + + //formatting + ?groupBy:OrdersReportGroupOption, //group order by... + ?format:OrdersReportFormatOption, //table , csv ? + + //filters : + ?groups:Array, + ?contracts:Array, //which contracts + ?distributions:Array, +} diff --git a/common/Formatting.hx b/common/Formatting.hx new file mode 100644 index 0000000..4465dcb --- /dev/null +++ b/common/Formatting.hx @@ -0,0 +1,73 @@ +package; +import Common; + +/** + * Formatting tools used on both client and server side. + * + * @author fbarbut + */ +class Formatting +{ + + /** smart quantity filter : display easier-to-read quantity when it's floats + * + * 0.33 x Lemon 12kg => 2kg Lemon + */ + public static function smartQt(orderQt:Float,productQt:Float,unit:Unit):String{ + return formatNum(orderQt * productQt) + " " + Formatting.unit(unit); + } + + public static function formatNum(n:Float):String { + if (n == null) return ""; + + //arrondi a 2 apres virgule + var out = Std.string(roundTo(n, 2)); + + //ajout un zéro, 1,8-->1,80 + if (out.indexOf(".")!=-1 && out.split(".")[1].length == 1) out = out +"0"; + + //virgule et pas point + return out.split(".").join(","); + } + + + /** + * Round a number to r digits after coma. + */ + public static function roundTo(n:Float, r:Int):Float { + return Math.round(n * Math.pow(10,r)) / Math.pow(10,r) ; + } + + public static function parseFloat(s:String):Float{ + if(s.indexOf(",")>0){ + return Std.parseFloat(StringTools.replace(s,",",".")); + }else{ + return Std.parseFloat(s); + } + } + + /** + * Display a unit + * @param u - + */ + public static function unit(u:Unit):String{ + /*t = sugoi.i18n.Locale.texts; + if(u==null) return t._("piece||unit of a product)"); + return switch(u){ + case Kilogram: t._("Kg.||kilogramms"); + case Gram: t._("g.||gramms"); + case Piece: t._("piece||unit of a product)"); + case Litre: t._("L.||liter"); + case Centilitre: t._("cl.||centiliter"); + }*/ + if(u==null) return "pièce(s)"; + return switch(u){ + case Kilogram: "Kg."; + case Gram: "g."; + case Piece: "pièce(s)"; + case Litre: "L."; + case Centilitre:"cl."; + } + + } +} \ No newline at end of file diff --git a/data/productTaxonomy.json b/data/productTaxonomy.json new file mode 100644 index 0000000..c500760 --- /dev/null +++ b/data/productTaxonomy.json @@ -0,0 +1,4322 @@ +{ + "products": [ + { + "name": "Vin Rouge", + "id": 447, + "subCategory": 11, + "category": 9 + }, + { + "name": "Vin Rosé", + "id": 446, + "subCategory": 11, + "category": 9 + }, + { + "name": "Vin Blanc", + "id": 445, + "subCategory": 11, + "category": 9 + }, + { + "name": "Pain à l'épeautre", + "id": 444, + "subCategory": 32, + "category": 7 + }, + { + "name": "Pain à l'engrain", + "id": 443, + "subCategory": 32, + "category": 7 + }, + { + "name": "Pain tournesol", + "id": 442, + "subCategory": 32, + "category": 7 + }, + { + "name": "Palet breton", + "id": 441, + "subCategory": 9, + "category": 7 + }, + { + "name": "Meringue", + "id": 440, + "subCategory": 9, + "category": 7 + }, + { + "name": "Vin mousseux ou Crémant", + "id": 439, + "subCategory": 11, + "category": 9 + }, + { + "name": "Sauternes", + "id": 438, + "subCategory": 11, + "category": 9 + }, + { + "name": "Avocat", + "id": 437, + "subCategory": 33, + "category": 6 + }, + { + "name": "Salade", + "id": 436, + "subCategory": 33, + "category": 6 + }, + { + "name": "Ratatouille", + "id": 435, + "subCategory": 30, + "category": 6 + }, + { + "name": "Cake", + "id": 434, + "subCategory": 9, + "category": 7 + }, + { + "name": "Soupe (rascasse, vive, etc)", + "id": 433, + "subCategory": 44, + "category": 1 + }, + { + "name": "Biscuits", + "id": 432, + "subCategory": 9, + "category": 7 + }, + { + "name": "Conserves de légumes", + "id": 431, + "subCategory": 30, + "category": 6 + }, + { + "name": "Soupe de tomates", + "id": 430, + "subCategory": 30, + "category": 6 + }, + { + "name": "Soupe de légumes", + "id": 429, + "subCategory": 30, + "category": 6 + }, + { + "name": "Soupe", + "id": 428, + "subCategory": 30, + "category": 6 + }, + { + "name": "Biscuit rose", + "id": 427, + "subCategory": 9, + "category": 7 + }, + { + "name": "Spécialités fromagères", + "id": 426, + "subCategory": 26, + "category": 8 + }, + { + "name": "Crème fraîche", + "id": 425, + "subCategory": 10, + "category": 8 + }, + { + "name": "Haricot beurre", + "id": 424, + "subCategory": 33, + "category": 6 + }, + { + "name": "Curcuma", + "id": 423, + "subCategory": 38, + "category": 14 + }, + { + "name": "Huile d'arachide", + "id": 422, + "subCategory": 51, + "category": 14 + }, + { + "name": "Cookie", + "id": 421, + "subCategory": 9, + "category": 7 + }, + { + "name": "Sirop", + "id": 420, + "subCategory": 55, + "category": 9 + }, + { + "name": "Sardine", + "id": 419, + "subCategory": 1, + "category": 1 + }, + { + "name": "Huile de noisettes", + "id": 418, + "subCategory": 51, + "category": 14 + }, + { + "name": "Huile de lin", + "id": 417, + "subCategory": 51, + "category": 14 + }, + { + "name": "Huile de colza", + "id": 416, + "subCategory": 51, + "category": 14 + }, + { + "name": "Huile d'argan", + "id": 415, + "subCategory": 51, + "category": 14 + }, + { + "name": "Pollen", + "id": 414, + "subCategory": 42, + "category": 14 + }, + { + "name": "Pétillant de pomme", + "id": 413, + "subCategory": 53, + "category": 9 + }, + { + "name": "Nectar", + "id": 412, + "subCategory": 28, + "category": 9 + }, + { + "name": "Limonade", + "id": 411, + "subCategory": 53, + "category": 9 + }, + { + "name": "Jus de Raisin", + "id": 410, + "subCategory": 28, + "category": 9 + }, + { + "name": "Jus de Griotte", + "id": 409, + "subCategory": 28, + "category": 9 + }, + { + "name": "Jus de Fruits rouges", + "id": 408, + "subCategory": 28, + "category": 9 + }, + { + "name": "Vanille", + "id": 407, + "subCategory": 38, + "category": 14 + }, + { + "name": "Vaisselle", + "id": 406, + "subCategory": 6, + "category": 5 + }, + { + "name": "Parfum d'intérieur", + "id": 405, + "subCategory": 6, + "category": 5 + }, + { + "name": "Huile de noix", + "id": 404, + "subCategory": 51, + "category": 14 + }, + { + "name": "Autres huiles", + "id": 403, + "subCategory": 51, + "category": 14 + }, + { + "name": "Lait de jument", + "id": 402, + "subCategory": 54, + "category": 8 + }, + { + "name": "Bouquet des champs", + "id": 401, + "subCategory": 7, + "category": 5 + }, + { + "name": "Lait d'ânesse (alimentaire)", + "id": 400, + "subCategory": 54, + "category": 8 + }, + { + "name": "Lait", + "id": 399, + "subCategory": 54, + "category": 8 + }, + { + "name": "Haricot", + "id": 398, + "subCategory": 52, + "category": 6 + }, + { + "name": "Jus d'Abricot", + "id": 397, + "subCategory": 28, + "category": 9 + }, + { + "name": "Cola", + "id": 396, + "subCategory": 53, + "category": 9 + }, + { + "name": "Boisson végétale", + "id": 395, + "subCategory": 53, + "category": 9 + }, + { + "name": "Jus de Kiwi", + "id": 394, + "subCategory": 28, + "category": 9 + }, + { + "name": "Pickles", + "id": 393, + "subCategory": 38, + "category": 14 + }, + { + "name": "Pesto", + "id": 392, + "subCategory": 47, + "category": 14 + }, + { + "name": "Câpres", + "id": 391, + "subCategory": 38, + "category": 14 + }, + { + "name": "Mayonnaise", + "id": 390, + "subCategory": 47, + "category": 14 + }, + { + "name": "Distillat (aromatique)", + "id": 389, + "subCategory": 38, + "category": 14 + }, + { + "name": "Vinaigrette", + "id": 388, + "subCategory": 51, + "category": 14 + }, + { + "name": "Jus d'Orange", + "id": 387, + "subCategory": 28, + "category": 9 + }, + { + "name": "Jus de Tomate", + "id": 386, + "subCategory": 28, + "category": 9 + }, + { + "name": "Jus de Pomme", + "id": 385, + "subCategory": 28, + "category": 9 + }, + { + "name": "Jus de Poire", + "id": 384, + "subCategory": 28, + "category": 9 + }, + { + "name": "Jus de Fraise", + "id": 383, + "subCategory": 28, + "category": 9 + }, + { + "name": "Savon", + "id": 382, + "subCategory": 3, + "category": 3 + }, + { + "name": "Huile de tournesol oléique", + "id": 381, + "subCategory": 51, + "category": 14 + }, + { + "name": "Moutarde", + "id": 380, + "subCategory": 38, + "category": 14 + }, + { + "name": "Paprika", + "id": 379, + "subCategory": 38, + "category": 14 + }, + { + "name": "Crème de cuisine végétale (à l'avoine)", + "id": 378, + "subCategory": 38, + "category": 14 + }, + { + "name": "Huile d'olive", + "id": 377, + "subCategory": 51, + "category": 14 + }, + { + "name": "Petit-beurre", + "id": 376, + "subCategory": 9, + "category": 7 + }, + { + "name": "Fleur alimentaire", + "id": 375, + "subCategory": 8, + "category": 6 + }, + { + "name": "Eau florale (non alimentaire)", + "id": 374, + "subCategory": 5, + "category": 3 + }, + { + "name": "Cive", + "id": 373, + "subCategory": 33, + "category": 6 + }, + { + "name": "Ortie", + "id": 372, + "subCategory": 8, + "category": 6 + }, + { + "name": "Myrtillier", + "id": 371, + "subCategory": 8, + "category": 6 + }, + { + "name": "Romarin", + "id": 370, + "subCategory": 8, + "category": 6 + }, + { + "name": "Potiron", + "id": 369, + "subCategory": 33, + "category": 6 + }, + { + "name": "Persil tubéreux", + "id": 368, + "subCategory": 33, + "category": 6 + }, + { + "name": "Pâtisson", + "id": 367, + "subCategory": 33, + "category": 6 + }, + { + "name": "Manioc", + "id": 366, + "subCategory": 33, + "category": 6 + }, + { + "name": "Madère", + "id": 365, + "subCategory": 33, + "category": 6 + }, + { + "name": "Sauge", + "id": 364, + "subCategory": 8, + "category": 6 + }, + { + "name": "Cébette", + "id": 363, + "subCategory": 33, + "category": 6 + }, + { + "name": "Salsifis", + "id": 362, + "subCategory": 33, + "category": 6 + }, + { + "name": "Cresson", + "id": 361, + "subCategory": 33, + "category": 6 + }, + { + "name": "Poireau", + "id": 360, + "subCategory": 33, + "category": 6 + }, + { + "name": "Piment végétarien", + "id": 359, + "subCategory": 33, + "category": 6 + }, + { + "name": "Patate douce", + "id": 358, + "subCategory": 33, + "category": 6 + }, + { + "name": "Panais", + "id": 357, + "subCategory": 33, + "category": 6 + }, + { + "name": "Oignon", + "id": 356, + "subCategory": 33, + "category": 6 + }, + { + "name": "Lentilles", + "id": 355, + "subCategory": 52, + "category": 6 + }, + { + "name": "Céleri rave", + "id": 354, + "subCategory": 33, + "category": 6 + }, + { + "name": "Huile de cameline", + "id": 353, + "subCategory": 51, + "category": 14 + }, + { + "name": "Coriandre", + "id": 352, + "subCategory": 8, + "category": 6 + }, + { + "name": "Chou palmiste", + "id": 351, + "subCategory": 33, + "category": 6 + }, + { + "name": "Panier de fruits", + "id": 350, + "subCategory": 37, + "category": 6 + }, + { + "name": "Huile de pépins de raisins", + "id": 349, + "subCategory": 51, + "category": 14 + }, + { + "name": "Huile de chanvre", + "id": 348, + "subCategory": 51, + "category": 14 + }, + { + "name": "Huile aromatisée", + "id": 347, + "subCategory": 51, + "category": 14 + }, + { + "name": "Sel au céleri", + "id": 346, + "subCategory": 38, + "category": 14 + }, + { + "name": "Confits", + "id": 345, + "subCategory": 34, + "category": 14 + }, + { + "name": "Plat cuisiné à consommer sur place", + "id": 344, + "subCategory": 4, + "category": 4 + }, + { + "name": "Angélique", + "id": 343, + "subCategory": 8, + "category": 6 + }, + { + "name": "Barquette au marrons", + "id": 342, + "subCategory": 9, + "category": 7 + }, + { + "name": "Aloe vera", + "id": 341, + "subCategory": 8, + "category": 6 + }, + { + "name": "Camomille romaine", + "id": 340, + "subCategory": 8, + "category": 6 + }, + { + "name": "Camomille matricaire", + "id": 339, + "subCategory": 8, + "category": 6 + }, + { + "name": "Bruyère", + "id": 338, + "subCategory": 8, + "category": 6 + }, + { + "name": "Pied de sapin", + "id": 337, + "subCategory": 7, + "category": 5 + }, + { + "name": "Lombricomposteur", + "id": 336, + "subCategory": 7, + "category": 5 + }, + { + "name": "Oeufs", + "id": 335, + "subCategory": 50, + "category": 8 + }, + { + "name": "Panier composé avec contenant", + "id": 334, + "subCategory": 20, + "category": 13 + }, + { + "name": "Thon", + "id": 333, + "subCategory": 1, + "category": 1 + }, + { + "name": "Sorbet", + "id": 332, + "subCategory": 19, + "category": 8 + }, + { + "name": "Sole", + "id": 331, + "subCategory": 1, + "category": 1 + }, + { + "name": "Purée de fruit ou compote", + "id": 330, + "subCategory": 34, + "category": 14 + }, + { + "name": "Pâte à tartiner", + "id": 329, + "subCategory": 34, + "category": 14 + }, + { + "name": "Truite fumée", + "id": 328, + "subCategory": 46, + "category": 1 + }, + { + "name": "Soin du linge", + "id": 327, + "subCategory": 6, + "category": 5 + }, + { + "name": "Tarte salée", + "id": 326, + "subCategory": 4, + "category": 4 + }, + { + "name": "Terreau", + "id": 325, + "subCategory": 7, + "category": 5 + }, + { + "name": "Saumon fumé", + "id": 324, + "subCategory": 46, + "category": 1 + }, + { + "name": "Pizza", + "id": 323, + "subCategory": 4, + "category": 4 + }, + { + "name": "Autres", + "id": 322, + "subCategory": 27, + "category": 12 + }, + { + "name": "Kit tarte", + "id": 321, + "subCategory": 4, + "category": 4 + }, + { + "name": "Aide à la pâtisserie", + "id": 320, + "subCategory": 4, + "category": 4 + }, + { + "name": "Serviette hygiénique", + "id": 319, + "subCategory": 3, + "category": 3 + }, + { + "name": "Atelier de fabrication", + "id": 318, + "subCategory": 22, + "category": 12 + }, + { + "name": "Atelier", + "id": 317, + "subCategory": 22, + "category": 12 + }, + { + "name": "Seiche", + "id": 316, + "subCategory": 1, + "category": 1 + }, + { + "name": "Saurel", + "id": 315, + "subCategory": 1, + "category": 1 + }, + { + "name": "Saumon de fontaine", + "id": 314, + "subCategory": 1, + "category": 1 + }, + { + "name": "Saumon", + "id": 313, + "subCategory": 1, + "category": 1 + }, + { + "name": "Artisanat et culture", + "id": 312, + "subCategory": 49, + "category": 12 + }, + { + "name": "Activité pédagogique et culturelle", + "id": 311, + "subCategory": 22, + "category": 12 + }, + { + "name": "Seiche", + "id": 310, + "subCategory": 12, + "category": 1 + }, + { + "name": "Bois et dérivés de bois", + "id": 309, + "subCategory": 6, + "category": 5 + }, + { + "name": "Truffe", + "id": 308, + "subCategory": 33, + "category": 6 + }, + { + "name": "Abricot séché", + "id": 307, + "subCategory": 40, + "category": 6 + }, + { + "name": "Tamarin des Indes", + "id": 306, + "subCategory": 39, + "category": 6 + }, + { + "name": "Autres fruits séchés", + "id": 305, + "subCategory": 40, + "category": 6 + }, + { + "name": "Nèfle", + "id": 304, + "subCategory": 39, + "category": 6 + }, + { + "name": "Engrais", + "id": 303, + "subCategory": 7, + "category": 5 + }, + { + "name": "Carambole", + "id": 302, + "subCategory": 39, + "category": 6 + }, + { + "name": "Conte", + "id": 301, + "subCategory": 21, + "category": 12 + }, + { + "name": "Pitaya", + "id": 300, + "subCategory": 39, + "category": 6 + }, + { + "name": "Jacque", + "id": 299, + "subCategory": 39, + "category": 6 + }, + { + "name": "Bougie naturelle", + "id": 298, + "subCategory": 18, + "category": 12 + }, + { + "name": "Tourte", + "id": 297, + "subCategory": 4, + "category": 4 + }, + { + "name": "Icaque", + "id": 296, + "subCategory": 39, + "category": 6 + }, + { + "name": "Grenadille", + "id": 295, + "subCategory": 39, + "category": 6 + }, + { + "name": "Grenadelle", + "id": 294, + "subCategory": 39, + "category": 6 + }, + { + "name": "Céramique", + "id": 293, + "subCategory": 18, + "category": 12 + }, + { + "name": "Cacao (fèves)", + "id": 292, + "subCategory": 48, + "category": 14 + }, + { + "name": "Goyavier", + "id": 291, + "subCategory": 39, + "category": 6 + }, + { + "name": "Litière et foin", + "id": 290, + "subCategory": 17, + "category": 11 + }, + { + "name": "Graines pour volaille", + "id": 289, + "subCategory": 17, + "category": 11 + }, + { + "name": "Cire d'abeille", + "id": 288, + "subCategory": 18, + "category": 12 + }, + { + "name": "Cassoulet", + "id": 287, + "subCategory": 4, + "category": 4 + }, + { + "name": "Aliment pour Chiens", + "id": 286, + "subCategory": 17, + "category": 11 + }, + { + "name": "Aliment pour Chats", + "id": 285, + "subCategory": 17, + "category": 11 + }, + { + "name": "Bougie", + "id": 284, + "subCategory": 18, + "category": 12 + }, + { + "name": "Ardoise", + "id": 283, + "subCategory": 18, + "category": 12 + }, + { + "name": "Turbot", + "id": 282, + "subCategory": 1, + "category": 1 + }, + { + "name": "Truite", + "id": 281, + "subCategory": 1, + "category": 1 + }, + { + "name": "Miel fleur d'été", + "id": 280, + "subCategory": 34, + "category": 14 + }, + { + "name": "Origan", + "id": 279, + "subCategory": 8, + "category": 6 + }, + { + "name": "Nashis", + "id": 278, + "subCategory": 39, + "category": 6 + }, + { + "name": "Rhubarbe", + "id": 277, + "subCategory": 39, + "category": 6 + }, + { + "name": "Nectarine", + "id": 276, + "subCategory": 39, + "category": 6 + }, + { + "name": "Litchi", + "id": 275, + "subCategory": 39, + "category": 6 + }, + { + "name": "Kiwano", + "id": 274, + "subCategory": 39, + "category": 6 + }, + { + "name": "Figue", + "id": 273, + "subCategory": 40, + "category": 6 + }, + { + "name": "Banane", + "id": 272, + "subCategory": 39, + "category": 6 + }, + { + "name": "Miel fleur de printemps", + "id": 271, + "subCategory": 34, + "category": 14 + }, + { + "name": "Myrtilles", + "id": 270, + "subCategory": 39, + "category": 6 + }, + { + "name": "Abricot", + "id": 269, + "subCategory": 39, + "category": 6 + }, + { + "name": "Framboise", + "id": 268, + "subCategory": 39, + "category": 6 + }, + { + "name": "Sablé", + "id": 267, + "subCategory": 9, + "category": 7 + }, + { + "name": "Rochers à la noix de coco", + "id": 266, + "subCategory": 9, + "category": 7 + }, + { + "name": "Miel de sapin", + "id": 265, + "subCategory": 34, + "category": 14 + }, + { + "name": "Ketchup", + "id": 264, + "subCategory": 47, + "category": 14 + }, + { + "name": "Fraise", + "id": 263, + "subCategory": 39, + "category": 6 + }, + { + "name": "Figue", + "id": 262, + "subCategory": 39, + "category": 6 + }, + { + "name": "Lotion", + "id": 261, + "subCategory": 5, + "category": 3 + }, + { + "name": "Sapin de Noël", + "id": 260, + "subCategory": 7, + "category": 5 + }, + { + "name": "Gommage", + "id": 259, + "subCategory": 5, + "category": 3 + }, + { + "name": "Chérimole", + "id": 258, + "subCategory": 39, + "category": 6 + }, + { + "name": "Cerise", + "id": 257, + "subCategory": 39, + "category": 6 + }, + { + "name": "Lingettes", + "id": 256, + "subCategory": 3, + "category": 3 + }, + { + "name": "Coupe menstruelle", + "id": 255, + "subCategory": 3, + "category": 3 + }, + { + "name": "Couches", + "id": 254, + "subCategory": 3, + "category": 3 + }, + { + "name": "Compote", + "id": 253, + "subCategory": 39, + "category": 6 + }, + { + "name": "Mandarine", + "id": 252, + "subCategory": 39, + "category": 6 + }, + { + "name": "Arbousier", + "id": 251, + "subCategory": 39, + "category": 6 + }, + { + "name": "Kaki", + "id": 250, + "subCategory": 39, + "category": 6 + }, + { + "name": "Murex (coquillage)", + "id": 249, + "subCategory": 12, + "category": 1 + }, + { + "name": "Moule", + "id": 248, + "subCategory": 12, + "category": 1 + }, + { + "name": "Groseille", + "id": 247, + "subCategory": 39, + "category": 6 + }, + { + "name": "Longani", + "id": 246, + "subCategory": 39, + "category": 6 + }, + { + "name": "Carpe", + "id": 245, + "subCategory": 1, + "category": 1 + }, + { + "name": "Brochet", + "id": 244, + "subCategory": 1, + "category": 1 + }, + { + "name": "Nettoyant sols et surfaces", + "id": 243, + "subCategory": 6, + "category": 5 + }, + { + "name": "Papier toilette", + "id": 242, + "subCategory": 6, + "category": 5 + }, + { + "name": "Pomme de lait", + "id": 241, + "subCategory": 39, + "category": 6 + }, + { + "name": "Amandes", + "id": 240, + "subCategory": 40, + "category": 6 + }, + { + "name": "Bonite", + "id": 239, + "subCategory": 1, + "category": 1 + }, + { + "name": "Acérola", + "id": 238, + "subCategory": 39, + "category": 6 + }, + { + "name": "Feijoa", + "id": 237, + "subCategory": 39, + "category": 6 + }, + { + "name": "Combava", + "id": 236, + "subCategory": 39, + "category": 6 + }, + { + "name": "Coing", + "id": 235, + "subCategory": 39, + "category": 6 + }, + { + "name": "Attier", + "id": 234, + "subCategory": 39, + "category": 6 + }, + { + "name": "Mangue", + "id": 233, + "subCategory": 39, + "category": 6 + }, + { + "name": "Anguille", + "id": 232, + "subCategory": 46, + "category": 1 + }, + { + "name": "Homard", + "id": 231, + "subCategory": 12, + "category": 1 + }, + { + "name": "Écrevisse", + "id": 230, + "subCategory": 12, + "category": 1 + }, + { + "name": "Fleur de sel", + "id": 229, + "subCategory": 38, + "category": 14 + }, + { + "name": "Crevettes", + "id": 228, + "subCategory": 12, + "category": 1 + }, + { + "name": "Flocons d'avoine", + "id": 227, + "subCategory": 35, + "category": 14 + }, + { + "name": "Farine", + "id": 226, + "subCategory": 35, + "category": 14 + }, + { + "name": "Produits à tartiner", + "id": 225, + "subCategory": 41, + "category": 14 + }, + { + "name": "Crabe", + "id": 224, + "subCategory": 12, + "category": 1 + }, + { + "name": "Coquilles Saint-Jacques", + "id": 223, + "subCategory": 12, + "category": 1 + }, + { + "name": "Mangoustan", + "id": 222, + "subCategory": 39, + "category": 6 + }, + { + "name": "Prune de Cythère", + "id": 221, + "subCategory": 39, + "category": 6 + }, + { + "name": "Tamarin", + "id": 220, + "subCategory": 39, + "category": 6 + }, + { + "name": "Clémentines", + "id": 219, + "subCategory": 39, + "category": 6 + }, + { + "name": "Corossol", + "id": 218, + "subCategory": 39, + "category": 6 + }, + { + "name": "Cassis", + "id": 217, + "subCategory": 39, + "category": 6 + }, + { + "name": "Mûre", + "id": 216, + "subCategory": 39, + "category": 6 + }, + { + "name": "Raisin", + "id": 215, + "subCategory": 39, + "category": 6 + }, + { + "name": "Myrobolan", + "id": 214, + "subCategory": 39, + "category": 6 + }, + { + "name": "Mombin", + "id": 213, + "subCategory": 39, + "category": 6 + }, + { + "name": "Prune", + "id": 212, + "subCategory": 39, + "category": 6 + }, + { + "name": "Ramboutan", + "id": 211, + "subCategory": 39, + "category": 6 + }, + { + "name": "Raisins", + "id": 210, + "subCategory": 40, + "category": 6 + }, + { + "name": "Barbadine", + "id": 658, + "subCategory": 39, + "category": 6 + }, + { + "name": "Pomme de lait", + "id": 209, + "subCategory": 39, + "category": 6 + }, + { + "name": "Durian", + "id": 657, + "subCategory": 39, + "category": 6 + }, + { + "name": "Noix de coco", + "id": 208, + "subCategory": 39, + "category": 6 + }, + { + "name": "Fruits rouges", + "id": 656, + "subCategory": 39, + "category": 6 + }, + { + "name": "Tomate arbuste (Tamarillo)", + "id": 207, + "subCategory": 39, + "category": 6 + }, + { + "name": "Datte", + "id": 655, + "subCategory": 40, + "category": 6 + }, + { + "name": "Pamplemousse", + "id": 206, + "subCategory": 39, + "category": 6 + }, + { + "name": "Châtaigne", + "id": 654, + "subCategory": 39, + "category": 6 + }, + { + "name": "Ananas", + "id": 205, + "subCategory": 39, + "category": 6 + }, + { + "name": "Mirabelle", + "id": 653, + "subCategory": 39, + "category": 6 + }, + { + "name": "Pomme", + "id": 204, + "subCategory": 39, + "category": 6 + }, + { + "name": "Pomme de cajou", + "id": 652, + "subCategory": 39, + "category": 6 + }, + { + "name": "Pruneau", + "id": 203, + "subCategory": 40, + "category": 6 + }, + { + "name": "Noix de cajou", + "id": 651, + "subCategory": 40, + "category": 6 + }, + { + "name": "Pomme liane", + "id": 202, + "subCategory": 39, + "category": 6 + }, + { + "name": "Noix", + "id": 650, + "subCategory": 40, + "category": 6 + }, + { + "name": "Physalis", + "id": 201, + "subCategory": 39, + "category": 6 + }, + { + "name": "Achards", + "id": 649, + "subCategory": 30, + "category": 6 + }, + { + "name": "Cachiman", + "id": 200, + "subCategory": 39, + "category": 6 + }, + { + "name": "Fruit de la passion", + "id": 648, + "subCategory": 39, + "category": 6 + }, + { + "name": "Poire", + "id": 199, + "subCategory": 39, + "category": 6 + }, + { + "name": "Orange", + "id": 647, + "subCategory": 39, + "category": 6 + }, + { + "name": "Cerise de Cayenne", + "id": 198, + "subCategory": 39, + "category": 6 + }, + { + "name": "Melon", + "id": 646, + "subCategory": 39, + "category": 6 + }, + { + "name": "Sapotille", + "id": 197, + "subCategory": 39, + "category": 6 + }, + { + "name": "Pastèque", + "id": 645, + "subCategory": 39, + "category": 6 + }, + { + "name": "Terrine", + "id": 196, + "subCategory": 36, + "category": 10 + }, + { + "name": "Abricot des Antilles", + "id": 644, + "subCategory": 39, + "category": 6 + }, + { + "name": "Rillettes et Pâtés", + "id": 195, + "subCategory": 45, + "category": 10 + }, + { + "name": "Pêche", + "id": 643, + "subCategory": 39, + "category": 6 + }, + { + "name": "Rillettes de canard", + "id": 194, + "subCategory": 36, + "category": 10 + }, + { + "name": "Pistache", + "id": 642, + "subCategory": 40, + "category": 6 + }, + { + "name": "Rillettes de truite", + "id": 193, + "subCategory": 44, + "category": 1 + }, + { + "name": "Papaye", + "id": 641, + "subCategory": 39, + "category": 6 + }, + { + "name": "Rillettes de saumon", + "id": 192, + "subCategory": 44, + "category": 1 + }, + { + "name": "Quenette", + "id": 640, + "subCategory": 39, + "category": 6 + }, + { + "name": "Rillettes", + "id": 191, + "subCategory": 36, + "category": 10 + }, + { + "name": "Mélange", + "id": 639, + "subCategory": 40, + "category": 6 + }, + { + "name": "Rillette", + "id": 190, + "subCategory": 36, + "category": 10 + }, + { + "name": "Prêle", + "id": 638, + "subCategory": 8, + "category": 6 + }, + { + "name": "Assortiment de viandes", + "id": 189, + "subCategory": 14, + "category": 10 + }, + { + "name": "Rougail", + "id": 637, + "subCategory": 30, + "category": 6 + }, + { + "name": "Infusion", + "id": 188, + "subCategory": 43, + "category": 14 + }, + { + "name": "Eau florale (alimentaire)", + "id": 636, + "subCategory": 8, + "category": 6 + }, + { + "name": "Miel de Tilleul", + "id": 187, + "subCategory": 34, + "category": 14 + }, + { + "name": "Ciboulette", + "id": 635, + "subCategory": 8, + "category": 6 + }, + { + "name": "Guarana", + "id": 186, + "subCategory": 42, + "category": 14 + }, + { + "name": "Choux", + "id": 634, + "subCategory": 33, + "category": 6 + }, + { + "name": "Miel de Forêt", + "id": 185, + "subCategory": 34, + "category": 14 + }, + { + "name": "Oseille", + "id": 633, + "subCategory": 8, + "category": 6 + }, + { + "name": "Oeufs de poisson", + "id": 184, + "subCategory": 1, + "category": 1 + }, + { + "name": "Millepertuis", + "id": 632, + "subCategory": 8, + "category": 6 + }, + { + "name": "Sapote", + "id": 183, + "subCategory": 39, + "category": 6 + }, + { + "name": "Mélisse", + "id": 631, + "subCategory": 8, + "category": 6 + }, + { + "name": "Citron vert", + "id": 182, + "subCategory": 39, + "category": 6 + }, + { + "name": "Mélange", + "id": 630, + "subCategory": 8, + "category": 6 + }, + { + "name": "Citrons", + "id": 181, + "subCategory": 39, + "category": 6 + }, + { + "name": "Marjolaine", + "id": 629, + "subCategory": 8, + "category": 6 + }, + { + "name": "Kumquat", + "id": 180, + "subCategory": 39, + "category": 6 + }, + { + "name": "Coriandre", + "id": 628, + "subCategory": 8, + "category": 6 + }, + { + "name": "Kiwi", + "id": 179, + "subCategory": 39, + "category": 6 + }, + { + "name": "Pomme de terre", + "id": 627, + "subCategory": 33, + "category": 6 + }, + { + "name": "Brugnon", + "id": 178, + "subCategory": 39, + "category": 6 + }, + { + "name": "Achillée", + "id": 626, + "subCategory": 8, + "category": 6 + }, + { + "name": "Bigarade", + "id": 177, + "subCategory": 39, + "category": 6 + }, + { + "name": "Consoude", + "id": 625, + "subCategory": 8, + "category": 6 + }, + { + "name": "Produits vétérinaires", + "id": 176, + "subCategory": 17, + "category": 11 + }, + { + "name": "Cassis feuille", + "id": 624, + "subCategory": 8, + "category": 6 + }, + { + "name": "Miel de chataîgnier", + "id": 175, + "subCategory": 34, + "category": 14 + }, + { + "name": "Baies de Genévrier", + "id": 623, + "subCategory": 8, + "category": 6 + }, + { + "name": "Chips", + "id": 174, + "subCategory": 41, + "category": 14 + }, + { + "name": "Galette végétale", + "id": 622, + "subCategory": 33, + "category": 6 + }, + { + "name": "Poivre", + "id": 173, + "subCategory": 38, + "category": 14 + }, + { + "name": "Ronce", + "id": 621, + "subCategory": 8, + "category": 6 + }, + { + "name": "Piment", + "id": 172, + "subCategory": 38, + "category": 14 + }, + { + "name": "Céleri vivace (livèche)", + "id": 620, + "subCategory": 8, + "category": 6 + }, + { + "name": "Noisette", + "id": 171, + "subCategory": 40, + "category": 6 + }, + { + "name": "Cardon", + "id": 619, + "subCategory": 33, + "category": 6 + }, + { + "name": "Poulpe", + "id": 170, + "subCategory": 12, + "category": 1 + }, + { + "name": "Navets", + "id": 618, + "subCategory": 33, + "category": 6 + }, + { + "name": "Nèfle du Japon", + "id": 169, + "subCategory": 39, + "category": 6 + }, + { + "name": "Lavande", + "id": 617, + "subCategory": 8, + "category": 6 + }, + { + "name": "Epeautre", + "id": 168, + "subCategory": 35, + "category": 14 + }, + { + "name": "Curry nain (helichrysum)", + "id": 616, + "subCategory": 8, + "category": 6 + }, + { + "name": "Perche", + "id": 167, + "subCategory": 1, + "category": 1 + }, + { + "name": "Framboisier", + "id": 615, + "subCategory": 8, + "category": 6 + }, + { + "name": "Goyave", + "id": 166, + "subCategory": 39, + "category": 6 + }, + { + "name": "Igname", + "id": 614, + "subCategory": 33, + "category": 6 + }, + { + "name": "Glace individuelle à l'unité < 200ml", + "id": 165, + "subCategory": 19, + "category": 8 + }, + { + "name": "Butternut", + "id": 613, + "subCategory": 33, + "category": 6 + }, + { + "name": "Graines moulues", + "id": 164, + "subCategory": 35, + "category": 14 + }, + { + "name": "Houblon", + "id": 612, + "subCategory": 8, + "category": 6 + }, + { + "name": "Graines germées", + "id": 163, + "subCategory": 35, + "category": 14 + }, + { + "name": "Huile essentielle alimentaire", + "id": 611, + "subCategory": 8, + "category": 6 + }, + { + "name": "Graines à germer", + "id": 162, + "subCategory": 35, + "category": 14 + }, + { + "name": "Bonbon", + "id": 610, + "subCategory": 48, + "category": 14 + }, + { + "name": "Aigre-doux", + "id": 161, + "subCategory": 38, + "category": 14 + }, + { + "name": "Cerfeuil", + "id": 609, + "subCategory": 8, + "category": 6 + }, + { + "name": "Miel de Montagne", + "id": 160, + "subCategory": 34, + "category": 14 + }, + { + "name": "Coquelicot", + "id": 608, + "subCategory": 8, + "category": 6 + }, + { + "name": "Soupe déshydratée", + "id": 159, + "subCategory": 30, + "category": 6 + }, + { + "name": "Pois cassés", + "id": 607, + "subCategory": 52, + "category": 6 + }, + { + "name": "Panier de légumes", + "id": 158, + "subCategory": 37, + "category": 6 + }, + { + "name": "Lamier blanc", + "id": 606, + "subCategory": 8, + "category": 6 + }, + { + "name": "Panier de fruits et légumes", + "id": 157, + "subCategory": 37, + "category": 6 + }, + { + "name": "Fenugrec", + "id": 605, + "subCategory": 8, + "category": 6 + }, + { + "name": "Pâté de canard", + "id": 156, + "subCategory": 36, + "category": 10 + }, + { + "name": "Fenouil", + "id": 604, + "subCategory": 8, + "category": 6 + }, + { + "name": "Pâté", + "id": 155, + "subCategory": 36, + "category": 10 + }, + { + "name": "Estragon", + "id": 603, + "subCategory": 8, + "category": 6 + }, + { + "name": "Sels de bain", + "id": 154, + "subCategory": 5, + "category": 3 + }, + { + "name": "Citronnelle", + "id": 602, + "subCategory": 8, + "category": 6 + }, + { + "name": "Baume de beauté précieux visage", + "id": 153, + "subCategory": 5, + "category": 3 + }, + { + "name": "Fève", + "id": 601, + "subCategory": 33, + "category": 6 + }, + { + "name": "Baume", + "id": 152, + "subCategory": 5, + "category": 3 + }, + { + "name": "Fève", + "id": 600, + "subCategory": 52, + "category": 6 + }, + { + "name": "Argile", + "id": 151, + "subCategory": 5, + "category": 3 + }, + { + "name": "Fenouil", + "id": 599, + "subCategory": 33, + "category": 6 + }, + { + "name": "Tiramisu", + "id": 150, + "subCategory": 9, + "category": 7 + }, + { + "name": "Brocolis", + "id": 598, + "subCategory": 33, + "category": 6 + }, + { + "name": "Viennoiserie", + "id": 149, + "subCategory": 13, + "category": 7 + }, + { + "name": "Pois chiches", + "id": 597, + "subCategory": 52, + "category": 6 + }, + { + "name": "Tous pains", + "id": 148, + "subCategory": 32, + "category": 7 + }, + { + "name": "Autres plantes sauvages", + "id": 596, + "subCategory": 8, + "category": 6 + }, + { + "name": "Crêpe", + "id": 147, + "subCategory": 31, + "category": 7 + }, + { + "name": "Aneth", + "id": 595, + "subCategory": 8, + "category": 6 + }, + { + "name": "Boulghour", + "id": 146, + "subCategory": 35, + "category": 14 + }, + { + "name": "Chocolat", + "id": 594, + "subCategory": 48, + "category": 14 + }, + { + "name": "Germe de blé", + "id": 145, + "subCategory": 35, + "category": 14 + }, + { + "name": "Confiserie", + "id": 593, + "subCategory": 48, + "category": 14 + }, + { + "name": "Pâté en croute", + "id": 144, + "subCategory": 4, + "category": 4 + }, + { + "name": "Sureau", + "id": 592, + "subCategory": 8, + "category": 6 + }, + { + "name": "Riz", + "id": 143, + "subCategory": 35, + "category": 14 + }, + { + "name": "Gombo", + "id": 591, + "subCategory": 33, + "category": 6 + }, + { + "name": "Madeleine", + "id": 142, + "subCategory": 9, + "category": 7 + }, + { + "name": "Endives", + "id": 590, + "subCategory": 33, + "category": 6 + }, + { + "name": "Gelée royale", + "id": 141, + "subCategory": 34, + "category": 14 + }, + { + "name": "Échalote", + "id": 589, + "subCategory": 33, + "category": 6 + }, + { + "name": "Gelée", + "id": 140, + "subCategory": 34, + "category": 14 + }, + { + "name": "Champignon", + "id": 588, + "subCategory": 33, + "category": 6 + }, + { + "name": "Dessert", + "id": 139, + "subCategory": 4, + "category": 4 + }, + { + "name": "Confiserie à base de chocolat", + "id": 587, + "subCategory": 48, + "category": 14 + }, + { + "name": "Confiture", + "id": 138, + "subCategory": 34, + "category": 14 + }, + { + "name": "Tablette de chocolat", + "id": 586, + "subCategory": 48, + "category": 14 + }, + { + "name": "Miel", + "id": 137, + "subCategory": 34, + "category": 14 + }, + { + "name": "Crosne", + "id": 585, + "subCategory": 33, + "category": 6 + }, + { + "name": "Yaourt", + "id": 136, + "subCategory": 29, + "category": 8 + }, + { + "name": "Courgettes", + "id": 584, + "subCategory": 33, + "category": 6 + }, + { + "name": "Brèdes", + "id": 135, + "subCategory": 33, + "category": 6 + }, + { + "name": "Giraumon", + "id": 583, + "subCategory": 33, + "category": 6 + }, + { + "name": "Lapin", + "id": 134, + "subCategory": 14, + "category": 10 + }, + { + "name": "Fruit à pain", + "id": 582, + "subCategory": 33, + "category": 6 + }, + { + "name": "Palourde", + "id": 133, + "subCategory": 12, + "category": 1 + }, + { + "name": "Banane Poyo", + "id": 581, + "subCategory": 33, + "category": 6 + }, + { + "name": "Soin du visage", + "id": 132, + "subCategory": 5, + "category": 3 + }, + { + "name": "Banane plantain", + "id": 580, + "subCategory": 33, + "category": 6 + }, + { + "name": "Oursin", + "id": 131, + "subCategory": 12, + "category": 1 + }, + { + "name": "Nougat", + "id": 579, + "subCategory": 48, + "category": 14 + }, + { + "name": "Marbré", + "id": 130, + "subCategory": 1, + "category": 1 + }, + { + "name": "Vigne rouge", + "id": 578, + "subCategory": 8, + "category": 6 + }, + { + "name": "Pain demi-complet", + "id": 129, + "subCategory": 32, + "category": 7 + }, + { + "name": "Verveine citronnelle", + "id": 577, + "subCategory": 8, + "category": 6 + }, + { + "name": "Pain de campagne", + "id": 128, + "subCategory": 32, + "category": 7 + }, + { + "name": "Tilleul", + "id": 576, + "subCategory": 8, + "category": 6 + }, + { + "name": "Pain complet", + "id": 127, + "subCategory": 32, + "category": 7 + }, + { + "name": "Pois gourmands", + "id": 575, + "subCategory": 33, + "category": 6 + }, + { + "name": "Quatre-quarts", + "id": 126, + "subCategory": 9, + "category": 7 + }, + { + "name": "Chocolat en poudre", + "id": 574, + "subCategory": 48, + "category": 14 + }, + { + "name": "Pain aux raisins", + "id": 125, + "subCategory": 32, + "category": 7 + }, + { + "name": "Petits pois", + "id": 573, + "subCategory": 33, + "category": 6 + }, + { + "name": "Pain aux graines de courge", + "id": 124, + "subCategory": 32, + "category": 7 + }, + { + "name": "Poivron", + "id": 572, + "subCategory": 33, + "category": 6 + }, + { + "name": "Pain au noix", + "id": 123, + "subCategory": 32, + "category": 7 + }, + { + "name": "Carottes", + "id": 571, + "subCategory": 33, + "category": 6 + }, + { + "name": "Marbré", + "id": 122, + "subCategory": 9, + "category": 7 + }, + { + "name": "Betterave", + "id": 570, + "subCategory": 33, + "category": 6 + }, + { + "name": "Pain sésame", + "id": 121, + "subCategory": 32, + "category": 7 + }, + { + "name": "Epinards", + "id": 569, + "subCategory": 33, + "category": 6 + }, + { + "name": "Pain nature", + "id": 120, + "subCategory": 32, + "category": 7 + }, + { + "name": "Cornichons", + "id": 568, + "subCategory": 33, + "category": 6 + }, + { + "name": "Pain multigraines", + "id": 119, + "subCategory": 32, + "category": 7 + }, + { + "name": "Aubergine", + "id": 567, + "subCategory": 33, + "category": 6 + }, + { + "name": "Pain lin pavot", + "id": 118, + "subCategory": 32, + "category": 7 + }, + { + "name": "Asperge", + "id": 566, + "subCategory": 33, + "category": 6 + }, + { + "name": "Muffin", + "id": 117, + "subCategory": 9, + "category": 7 + }, + { + "name": "Artichaut", + "id": 565, + "subCategory": 33, + "category": 6 + }, + { + "name": "Macarons", + "id": 116, + "subCategory": 9, + "category": 7 + }, + { + "name": "Concombres", + "id": 564, + "subCategory": 33, + "category": 6 + }, + { + "name": "Gaufre", + "id": 115, + "subCategory": 9, + "category": 7 + }, + { + "name": "Blette", + "id": 563, + "subCategory": 33, + "category": 6 + }, + { + "name": "Galette de blé noir", + "id": 114, + "subCategory": 31, + "category": 7 + }, + { + "name": "Ail", + "id": 562, + "subCategory": 33, + "category": 6 + }, + { + "name": "Dessert végétal", + "id": 113, + "subCategory": 9, + "category": 7 + }, + { + "name": "Sarriette", + "id": 561, + "subCategory": 8, + "category": 6 + }, + { + "name": "Compote", + "id": 112, + "subCategory": 9, + "category": 7 + }, + { + "name": "Topinambour", + "id": 560, + "subCategory": 33, + "category": 6 + }, + { + "name": "Cannelé", + "id": 111, + "subCategory": 9, + "category": 7 + }, + { + "name": "Tomate", + "id": 559, + "subCategory": 33, + "category": 6 + }, + { + "name": "Brownie", + "id": 110, + "subCategory": 9, + "category": 7 + }, + { + "name": "Potimarron", + "id": 558, + "subCategory": 33, + "category": 6 + }, + { + "name": "Financier", + "id": 109, + "subCategory": 9, + "category": 7 + }, + { + "name": "Reine des prés", + "id": 557, + "subCategory": 8, + "category": 6 + }, + { + "name": "Biscuit de Savoie", + "id": 108, + "subCategory": 9, + "category": 7 + }, + { + "name": "Pensée sauvage", + "id": 556, + "subCategory": 8, + "category": 6 + }, + { + "name": "Fruits au sirop", + "id": 107, + "subCategory": 30, + "category": 6 + }, + { + "name": "Courges", + "id": 555, + "subCategory": 33, + "category": 6 + }, + { + "name": "Dessert glacé", + "id": 106, + "subCategory": 19, + "category": 8 + }, + { + "name": "Céleri branche", + "id": 554, + "subCategory": 33, + "category": 6 + }, + { + "name": "Lait fermenté", + "id": 105, + "subCategory": 29, + "category": 8 + }, + { + "name": "Persil", + "id": 553, + "subCategory": 8, + "category": 6 + }, + { + "name": "Fromage blanc", + "id": 104, + "subCategory": 29, + "category": 8 + }, + { + "name": "Radis", + "id": 552, + "subCategory": 33, + "category": 6 + }, + { + "name": "Faisselle", + "id": 103, + "subCategory": 29, + "category": 8 + }, + { + "name": "Olive", + "id": 551, + "subCategory": 33, + "category": 6 + }, + { + "name": "Essaim", + "id": 102, + "subCategory": 17, + "category": 11 + }, + { + "name": "Haricot vert", + "id": 550, + "subCategory": 33, + "category": 6 + }, + { + "name": "Jus de fruit à consommation immédiate TVA 10%", + "id": 101, + "subCategory": 28, + "category": 9 + }, + { + "name": "Menthe", + "id": 549, + "subCategory": 8, + "category": 6 + }, + { + "name": "Eau de vie", + "id": 100, + "subCategory": 11, + "category": 9 + }, + { + "name": "Maïs", + "id": 548, + "subCategory": 33, + "category": 6 + }, + { + "name": "Huître", + "id": 99, + "subCategory": 12, + "category": 1 + }, + { + "name": "Aspérule Odorante", + "id": 547, + "subCategory": 8, + "category": 6 + }, + { + "name": "Tarte sucrée", + "id": 98, + "subCategory": 9, + "category": 7 + }, + { + "name": "Ail des ours", + "id": 546, + "subCategory": 8, + "category": 6 + }, + { + "name": "Tartelette aux fruits", + "id": 97, + "subCategory": 9, + "category": 7 + }, + { + "name": "Hysope", + "id": 545, + "subCategory": 8, + "category": 6 + }, + { + "name": "Vannerie", + "id": 96, + "subCategory": 18, + "category": 12 + }, + { + "name": "Souci", + "id": 544, + "subCategory": 8, + "category": 6 + }, + { + "name": "Visite à la ferme", + "id": 95, + "subCategory": 22, + "category": 12 + }, + { + "name": "Eglantier", + "id": 543, + "subCategory": 8, + "category": 6 + }, + { + "name": "Vêtement pour enfant", + "id": 94, + "subCategory": 27, + "category": 12 + }, + { + "name": "Frêne", + "id": 542, + "subCategory": 8, + "category": 6 + }, + { + "name": "Tissu et textile", + "id": 93, + "subCategory": 18, + "category": 12 + }, + { + "name": "Thym", + "id": 541, + "subCategory": 8, + "category": 6 + }, + { + "name": "Tee-shirt", + "id": 92, + "subCategory": 27, + "category": 12 + }, + { + "name": "Christophine (Chouchou, Chayotte)", + "id": 540, + "subCategory": 33, + "category": 6 + }, + { + "name": "Jouet en tissu", + "id": 91, + "subCategory": 25, + "category": 12 + }, + { + "name": "Rutabaga", + "id": 539, + "subCategory": 33, + "category": 6 + }, + { + "name": "Mouton", + "id": 90, + "subCategory": 14, + "category": 10 + }, + { + "name": "chocolat blanc", + "id": 538, + "subCategory": 48, + "category": 14 + }, + { + "name": "Crème de cassis", + "id": 89, + "subCategory": 11, + "category": 9 + }, + { + "name": "Sel", + "id": 537, + "subCategory": 38, + "category": 14 + }, + { + "name": "Cidre", + "id": 88, + "subCategory": 11, + "category": 9 + }, + { + "name": "Semoule de blé", + "id": 536, + "subCategory": 35, + "category": 14 + }, + { + "name": "Telline", + "id": 87, + "subCategory": 12, + "category": 1 + }, + { + "name": "Aromates", + "id": 535, + "subCategory": 38, + "category": 14 + }, + { + "name": "Champagne", + "id": 86, + "subCategory": 11, + "category": 9 + }, + { + "name": "Sauce tomate", + "id": 534, + "subCategory": 47, + "category": 14 + }, + { + "name": "Bière triple", + "id": 85, + "subCategory": 11, + "category": 9 + }, + { + "name": "Safran", + "id": 533, + "subCategory": 38, + "category": 14 + }, + { + "name": "Pageot", + "id": 84, + "subCategory": 1, + "category": 1 + }, + { + "name": "Sauce pour salade", + "id": 532, + "subCategory": 47, + "category": 14 + }, + { + "name": "Fromage", + "id": 83, + "subCategory": 26, + "category": 8 + }, + { + "name": "Sauce d'accompagnement", + "id": 531, + "subCategory": 47, + "category": 14 + }, + { + "name": "Muge", + "id": 82, + "subCategory": 1, + "category": 1 + }, + { + "name": "Rouille", + "id": 530, + "subCategory": 47, + "category": 14 + }, + { + "name": "Jouet en bois", + "id": 81, + "subCategory": 25, + "category": 12 + }, + { + "name": "Biscuit apéritif", + "id": 529, + "subCategory": 41, + "category": 14 + }, + { + "name": "Information", + "id": 80, + "subCategory": 24, + "category": 12 + }, + { + "name": "Poudre de noix", + "id": 528, + "subCategory": 38, + "category": 14 + }, + { + "name": "Chevreau", + "id": 79, + "subCategory": 14, + "category": 10 + }, + { + "name": "Poudre de noisettes", + "id": 527, + "subCategory": 38, + "category": 14 + }, + { + "name": "Cheval", + "id": 78, + "subCategory": 14, + "category": 10 + }, + { + "name": "Propolis", + "id": 526, + "subCategory": 42, + "category": 14 + }, + { + "name": "Merlan", + "id": 77, + "subCategory": 1, + "category": 1 + }, + { + "name": "Tapenade", + "id": 525, + "subCategory": 38, + "category": 14 + }, + { + "name": "Lamproie", + "id": 76, + "subCategory": 1, + "category": 1 + }, + { + "name": "Spiruline", + "id": 524, + "subCategory": 42, + "category": 14 + }, + { + "name": "Daurade", + "id": 75, + "subCategory": 1, + "category": 1 + }, + { + "name": "Confit d'échalote", + "id": 523, + "subCategory": 38, + "category": 14 + }, + { + "name": "Cabillaud", + "id": 74, + "subCategory": 1, + "category": 1 + }, + { + "name": "abats", + "id": 522, + "subCategory": 14, + "category": 10 + }, + { + "name": "Bar", + "id": 73, + "subCategory": 1, + "category": 1 + }, + { + "name": "Confit d'oignons", + "id": 521, + "subCategory": 58, + "category": 10 + }, + { + "name": "Cognac", + "id": 72, + "subCategory": 11, + "category": 9 + }, + { + "name": "Veau", + "id": 520, + "subCategory": 62, + "category": 10 + }, + { + "name": "Boissons sans alcool", + "id": 71, + "subCategory": 23, + "category": 9 + }, + { + "name": "Saucisson", + "id": 519, + "subCategory": 60, + "category": 10 + }, + { + "name": "Cours de cuisine", + "id": 70, + "subCategory": 22, + "category": 12 + }, + { + "name": "Saucisse", + "id": 518, + "subCategory": 60, + "category": 10 + }, + { + "name": "Cours de jardinage", + "id": 69, + "subCategory": 22, + "category": 12 + }, + { + "name": "Rosette", + "id": 517, + "subCategory": 60, + "category": 10 + }, + { + "name": "Meubles et décoration", + "id": 68, + "subCategory": 18, + "category": 12 + }, + { + "name": "Pancetta", + "id": 516, + "subCategory": 60, + "category": 10 + }, + { + "name": "Livres", + "id": 67, + "subCategory": 21, + "category": 12 + }, + { + "name": "Jambonneau", + "id": 515, + "subCategory": 60, + "category": 10 + }, + { + "name": "Plat cuisiné", + "id": 66, + "subCategory": 4, + "category": 4 + }, + { + "name": "Jambon cru", + "id": 514, + "subCategory": 60, + "category": 10 + }, + { + "name": "CD", + "id": 65, + "subCategory": 21, + "category": 12 + }, + { + "name": "Jambon", + "id": 513, + "subCategory": 60, + "category": 10 + }, + { + "name": "Palmier", + "id": 64, + "subCategory": 9, + "category": 7 + }, + { + "name": "Coppa", + "id": 512, + "subCategory": 60, + "category": 10 + }, + { + "name": "Bouquet ou composition florale", + "id": 63, + "subCategory": 7, + "category": 5 + }, + { + "name": "Boudin", + "id": 511, + "subCategory": 60, + "category": 10 + }, + { + "name": "Plant", + "id": 62, + "subCategory": 7, + "category": 5 + }, + { + "name": "Andouille", + "id": 510, + "subCategory": 60, + "category": 10 + }, + { + "name": "Panier de produits du terroir", + "id": 61, + "subCategory": 20, + "category": 13 + }, + { + "name": "Pâté", + "id": 509, + "subCategory": 58, + "category": 10 + }, + { + "name": "Panier de condiments", + "id": 60, + "subCategory": 20, + "category": 13 + }, + { + "name": "Mousse de foie gras", + "id": 508, + "subCategory": 58, + "category": 10 + }, + { + "name": "Condiments", + "id": 59, + "subCategory": 20, + "category": 13 + }, + { + "name": "Poulet", + "id": 507, + "subCategory": 56, + "category": 10 + }, + { + "name": "Pâtisserie au chocolat", + "id": 58, + "subCategory": 9, + "category": 7 + }, + { + "name": "Poule", + "id": 506, + "subCategory": 56, + "category": 10 + }, + { + "name": "Bière ambrée", + "id": 57, + "subCategory": 11, + "category": 9 + }, + { + "name": "Poularde", + "id": 505, + "subCategory": 56, + "category": 10 + }, + { + "name": "Apéritif", + "id": 56, + "subCategory": 11, + "category": 9 + }, + { + "name": "Porc", + "id": 504, + "subCategory": 61, + "category": 10 + }, + { + "name": "Ustensile de cuisine", + "id": 55, + "subCategory": 6, + "category": 5 + }, + { + "name": "Cerfs & Biches", + "id": 503, + "subCategory": 56, + "category": 10 + }, + { + "name": "Glace", + "id": 54, + "subCategory": 19, + "category": 8 + }, + { + "name": "Poitrine fumée", + "id": 502, + "subCategory": 60, + "category": 10 + }, + { + "name": "Pain d'épices", + "id": 53, + "subCategory": 9, + "category": 7 + }, + { + "name": "Magret", + "id": 501, + "subCategory": 58, + "category": 10 + }, + { + "name": "Bière blonde", + "id": 52, + "subCategory": 11, + "category": 9 + }, + { + "name": "Agneau", + "id": 500, + "subCategory": 59, + "category": 10 + }, + { + "name": "Bière brune", + "id": 51, + "subCategory": 11, + "category": 9 + }, + { + "name": "Dinde", + "id": 499, + "subCategory": 56, + "category": 10 + }, + { + "name": "Champagne rosé", + "id": 50, + "subCategory": 11, + "category": 9 + }, + { + "name": "Pintade", + "id": 498, + "subCategory": 56, + "category": 10 + }, + { + "name": "Pommeau", + "id": 49, + "subCategory": 11, + "category": 9 + }, + { + "name": "Perdrix", + "id": 497, + "subCategory": 56, + "category": 10 + }, + { + "name": "Loup", + "id": 48, + "subCategory": 1, + "category": 1 + }, + { + "name": "Foie gras entier", + "id": 496, + "subCategory": 58, + "category": 10 + }, + { + "name": "Poiré", + "id": 47, + "subCategory": 11, + "category": 9 + }, + { + "name": "Oie", + "id": 495, + "subCategory": 56, + "category": 10 + }, + { + "name": "Lieu Noir", + "id": 46, + "subCategory": 1, + "category": 1 + }, + { + "name": "Foie gras", + "id": 494, + "subCategory": 58, + "category": 10 + }, + { + "name": "Hareng", + "id": 45, + "subCategory": 1, + "category": 1 + }, + { + "name": "Chapon", + "id": 493, + "subCategory": 56, + "category": 10 + }, + { + "name": "Galet", + "id": 44, + "subCategory": 1, + "category": 1 + }, + { + "name": "Géline", + "id": 492, + "subCategory": 56, + "category": 10 + }, + { + "name": "Sar", + "id": 43, + "subCategory": 1, + "category": 1 + }, + { + "name": "Faisan", + "id": 491, + "subCategory": 56, + "category": 10 + }, + { + "name": "Rouget", + "id": 42, + "subCategory": 1, + "category": 1 + }, + { + "name": "Coquelet", + "id": 490, + "subCategory": 56, + "category": 10 + }, + { + "name": "Colin", + "id": 41, + "subCategory": 1, + "category": 1 + }, + { + "name": "Canard", + "id": 489, + "subCategory": 56, + "category": 10 + }, + { + "name": "Pétillant de raisin", + "id": 40, + "subCategory": 11, + "category": 9 + }, + { + "name": "Confit d'oie", + "id": 488, + "subCategory": 58, + "category": 10 + }, + { + "name": "Hydromel", + "id": 39, + "subCategory": 11, + "category": 9 + }, + { + "name": "Confit de canard", + "id": 487, + "subCategory": 58, + "category": 10 + }, + { + "name": "Morue", + "id": 38, + "subCategory": 1, + "category": 1 + }, + { + "name": "Confit", + "id": 486, + "subCategory": 58, + "category": 10 + }, + { + "name": "Digestif ou liqueur", + "id": 37, + "subCategory": 11, + "category": 9 + }, + { + "name": "Caille", + "id": 485, + "subCategory": 56, + "category": 10 + }, + { + "name": "Maquereau", + "id": 36, + "subCategory": 1, + "category": 1 + }, + { + "name": "Boeuf", + "id": 484, + "subCategory": 57, + "category": 10 + }, + { + "name": "Lotte", + "id": 35, + "subCategory": 1, + "category": 1 + }, + { + "name": "Autruche", + "id": 483, + "subCategory": 56, + "category": 10 + }, + { + "name": "Vaisselle en bois", + "id": 34, + "subCategory": 18, + "category": 12 + }, + { + "name": "Pigeon", + "id": 482, + "subCategory": 56, + "category": 10 + }, + { + "name": "Graines pour oiseaux", + "id": 33, + "subCategory": 17, + "category": 11 + }, + { + "name": "Miel de Lavande", + "id": 481, + "subCategory": 34, + "category": 14 + }, + { + "name": "Limande", + "id": 32, + "subCategory": 1, + "category": 1 + }, + { + "name": "Miel d'acacia", + "id": 480, + "subCategory": 34, + "category": 14 + }, + { + "name": "Féra (corégone)", + "id": 31, + "subCategory": 1, + "category": 1 + }, + { + "name": "Café", + "id": 479, + "subCategory": 43, + "category": 14 + }, + { + "name": "Eglefin", + "id": 30, + "subCategory": 1, + "category": 1 + }, + { + "name": "Baies roses", + "id": 478, + "subCategory": 38, + "category": 14 + }, + { + "name": "Escargots", + "id": 29, + "subCategory": 16, + "category": 10 + }, + { + "name": "Algues", + "id": 477, + "subCategory": 42, + "category": 14 + }, + { + "name": "Germoir", + "id": 28, + "subCategory": 7, + "category": 5 + }, + { + "name": "Laurier", + "id": 476, + "subCategory": 8, + "category": 6 + }, + { + "name": "Panier de poissons", + "id": 27, + "subCategory": 1, + "category": 1 + }, + { + "name": "Pâtes", + "id": 475, + "subCategory": 35, + "category": 14 + }, + { + "name": "Caviar", + "id": 26, + "subCategory": 1, + "category": 1 + }, + { + "name": "Muesli", + "id": 474, + "subCategory": 35, + "category": 14 + }, + { + "name": "Seitan", + "id": 25, + "subCategory": 4, + "category": 4 + }, + { + "name": "Chips de légume", + "id": 473, + "subCategory": 41, + "category": 14 + }, + { + "name": "Poissons et crustacés", + "id": 24, + "subCategory": 15, + "category": 1 + }, + { + "name": "Macération solaire (alimentaire)", + "id": 472, + "subCategory": 51, + "category": 14 + }, + { + "name": "Bison", + "id": 23, + "subCategory": 14, + "category": 10 + }, + { + "name": "Lait de coco", + "id": 471, + "subCategory": 38, + "category": 14 + }, + { + "name": "Brioche", + "id": 22, + "subCategory": 13, + "category": 7 + }, + { + "name": "Coulis de tomates", + "id": 470, + "subCategory": 47, + "category": 14 + }, + { + "name": "Bulot", + "id": 21, + "subCategory": 12, + "category": 1 + }, + { + "name": "Coulis de fruits rouges", + "id": 469, + "subCategory": 38, + "category": 14 + }, + { + "name": "Aubépine", + "id": 20, + "subCategory": 8, + "category": 6 + }, + { + "name": "Coulis de fruit", + "id": 468, + "subCategory": 38, + "category": 14 + }, + { + "name": "Bouleau", + "id": 19, + "subCategory": 8, + "category": 6 + }, + { + "name": "Vinaigre balsamique", + "id": 467, + "subCategory": 38, + "category": 14 + }, + { + "name": "Calvados", + "id": 18, + "subCategory": 11, + "category": 9 + }, + { + "name": "Vinaigre alcoolisé", + "id": 466, + "subCategory": 38, + "category": 14 + }, + { + "name": "Bière", + "id": 17, + "subCategory": 11, + "category": 9 + }, + { + "name": "Vinaigre", + "id": 465, + "subCategory": 38, + "category": 14 + }, + { + "name": "Beurre", + "id": 16, + "subCategory": 10, + "category": 8 + }, + { + "name": "Aloe vera", + "id": 464, + "subCategory": 42, + "category": 14 + }, + { + "name": "Pâtes", + "id": 15, + "subCategory": 9, + "category": 7 + }, + { + "name": "Huile d'olive", + "id": 463, + "subCategory": 51, + "category": 14 + }, + { + "name": "Gâteau", + "id": 14, + "subCategory": 9, + "category": 7 + }, + { + "name": "Huile de tournesol", + "id": 462, + "subCategory": 51, + "category": 14 + }, + { + "name": "Basilic", + "id": 13, + "subCategory": 8, + "category": 6 + }, + { + "name": "Huile de soja", + "id": 461, + "subCategory": 51, + "category": 14 + }, + { + "name": "Semences", + "id": 12, + "subCategory": 7, + "category": 5 + }, + { + "name": "Huile de ricin", + "id": 460, + "subCategory": 51, + "category": 14 + }, + { + "name": "Linge de Maison", + "id": 11, + "subCategory": 6, + "category": 5 + }, + { + "name": "Gingembre", + "id": 459, + "subCategory": 38, + "category": 14 + }, + { + "name": "Démaquillant", + "id": 10, + "subCategory": 5, + "category": 3 + }, + { + "name": "Graines à cuire", + "id": 458, + "subCategory": 35, + "category": 14 + }, + { + "name": "Soin du corps", + "id": 9, + "subCategory": 5, + "category": 3 + }, + { + "name": "Graines à croquer", + "id": 457, + "subCategory": 35, + "category": 14 + }, + { + "name": "Soin des cheveux", + "id": 8, + "subCategory": 5, + "category": 3 + }, + { + "name": "Lin", + "id": 456, + "subCategory": 35, + "category": 14 + }, + { + "name": "Macération solaire (non alimentaire)", + "id": 7, + "subCategory": 5, + "category": 3 + }, + { + "name": "Quinoa", + "id": 455, + "subCategory": 35, + "category": 14 + }, + { + "name": "Huile de soin", + "id": 6, + "subCategory": 5, + "category": 3 + }, + { + "name": "Mélange d'épices", + "id": 454, + "subCategory": 38, + "category": 14 + }, + { + "name": "Lait d'ânesse (cosmétique)", + "id": 5, + "subCategory": 5, + "category": 3 + }, + { + "name": "Chutney", + "id": 453, + "subCategory": 38, + "category": 14 + }, + { + "name": "Tofu", + "id": 4, + "subCategory": 4, + "category": 4 + }, + { + "name": "Tisane", + "id": 452, + "subCategory": 43, + "category": 14 + }, + { + "name": "Savon liquide", + "id": 3, + "subCategory": 3, + "category": 3 + }, + { + "name": "Thé", + "id": 451, + "subCategory": 43, + "category": 14 + }, + { + "name": "Livraison à domicile", + "id": 2, + "subCategory": 2, + "category": 2 + }, + { + "name": "Sucre", + "id": 450, + "subCategory": 38, + "category": 14 + }, + { + "name": "Carrelet", + "id": 1, + "subCategory": 1, + "category": 1 + }, + { + "name": "Huile essentielle", + "id": 449, + "subCategory": 8, + "category": 6 + }, + { + "name": "Sauge", + "id": 448, + "subCategory": 8, + "category": 6 + } + ], + "categories": [ + { + "name": "Traiteur", + "id": 4 + }, + { + "name": "Maison & Jardin", + "id": 5 + }, + { + "name": "Boulangerie & Pâtisserie", + "id": 7 + }, + { + "name": "Viandes", + "id": 10 + }, + { + "name": "Artisanat & Loisirs", + "id": 12 + }, + { + "name": "Produits de la mer", + "id": 1 + }, + { + "name": "Beauté & soins", + "id": 3 + }, + { + "name": "Animaux", + "id": 11 + }, + { + "name": "Épicerie", + "id": 14 + }, + { + "name": "Fruits & Légumes", + "id": 6 + }, + { + "name": "Crèmerie", + "id": 8 + }, + { + "name": "Paniers et Coffrets", + "id": 13 + }, + { + "name": "Livraison à domicile", + "id": 2 + }, + { + "name": "Boissons", + "id": 9 + } + ], + "subCategories": [ + { + "name": "Traiteur", + "id": 4, + "category": 4 + }, + { + "name": "Boissons alcoolisées", + "id": 11, + "category": 9 + }, + { + "name": "Porc", + "id": 61, + "category": 10 + }, + { + "name": "Hygiène", + "id": 3, + "category": 3 + }, + { + "name": "Veau", + "id": 62, + "category": 10 + }, + { + "name": "Habillement", + "id": 27, + "category": 12 + }, + { + "name": "Boeuf", + "id": 57, + "category": 10 + }, + { + "name": "Escargots", + "id": 16, + "category": 10 + }, + { + "name": "Confiseries & Chocolat", + "id": 48, + "category": 14 + }, + { + "name": "Glaces & Sorbets", + "id": 19, + "category": 8 + }, + { + "name": "Fruits", + "id": 39, + "category": 6 + }, + { + "name": "Pains", + "id": 32, + "category": 7 + }, + { + "name": "Poissons", + "id": 1, + "category": 1 + }, + { + "name": "Atelier, Cours et Visite", + "id": 22, + "category": 12 + }, + { + "name": "Jus de Fruits", + "id": 28, + "category": 9 + }, + { + "name": "Livraison à domicile", + "id": 2, + "category": 2 + }, + { + "name": "Autres Viandes", + "id": 14, + "category": 10 + }, + { + "name": "Viennoiseries", + "id": 13, + "category": 7 + }, + { + "name": "Sirop", + "id": 55, + "category": 9 + }, + { + "name": "Jouets", + "id": 25, + "category": 12 + }, + { + "name": "Artisanat et culture", + "id": 49, + "category": 12 + }, + { + "name": "Information", + "id": 24, + "category": 12 + }, + { + "name": "Maison et Entretien", + "id": 6, + "category": 5 + }, + { + "name": "Boissons fraîches", + "id": 53, + "category": 9 + }, + { + "name": "Céréales / Farines / Graines", + "id": 35, + "category": 14 + }, + { + "name": "Rillettes et Pâtés", + "id": 36, + "category": 10 + }, + { + "name": "Rillettes et Pâtés", + "id": 45, + "category": 10 + }, + { + "name": "Fruits de mer", + "id": 12, + "category": 1 + }, + { + "name": "Fruits secs", + "id": 40, + "category": 6 + }, + { + "name": "Artisanat", + "id": 18, + "category": 12 + }, + { + "name": "Lait", + "id": 54, + "category": 8 + }, + { + "name": "Poissons fumés", + "id": 46, + "category": 1 + }, + { + "name": "Charcuterie", + "id": 60, + "category": 10 + }, + { + "name": "Jardin & Potager", + "id": 7, + "category": 5 + }, + { + "name": "Légumes", + "id": 33, + "category": 6 + }, + { + "name": "Condiments, épices, sucres", + "id": 38, + "category": 14 + }, + { + "name": "Objets culturels", + "id": 21, + "category": 12 + }, + { + "name": "Yaourts & Fromages Frais", + "id": 29, + "category": 8 + }, + { + "name": "Soupes et Rillettes", + "id": 44, + "category": 1 + }, + { + "name": "Poissons et crustacés", + "id": 15, + "category": 1 + }, + { + "name": "Boissons sans alcool", + "id": 23, + "category": 9 + }, + { + "name": "Confitures, miel, compotes", + "id": 34, + "category": 14 + }, + { + "name": "Fromage", + "id": 26, + "category": 8 + }, + { + "name": "Café, thé et infusions", + "id": 43, + "category": 14 + }, + { + "name": "Légumes secs", + "id": 52, + "category": 6 + }, + { + "name": "Œufs", + "id": 50, + "category": 8 + }, + { + "name": "Foie Gras et Confits", + "id": 58, + "category": 10 + }, + { + "name": "Animaux", + "id": 17, + "category": 11 + }, + { + "name": "Beurre & Crème Fraîche", + "id": 10, + "category": 8 + }, + { + "name": "Herbes et Plantes", + "id": 8, + "category": 6 + }, + { + "name": "Compléments alimentaires", + "id": 42, + "category": 14 + }, + { + "name": "Paniers composés", + "id": 20, + "category": 13 + }, + { + "name": "Pâtisserie", + "id": 9, + "category": 7 + }, + { + "name": "Soupes et Conserves", + "id": 30, + "category": 6 + }, + { + "name": "Sauces", + "id": 47, + "category": 14 + }, + { + "name": "Galettes et Crêpes", + "id": 31, + "category": 7 + }, + { + "name": "Cosmétiques", + "id": 5, + "category": 3 + }, + { + "name": "Paniers composés", + "id": 37, + "category": 6 + }, + { + "name": "Huiles", + "id": 51, + "category": 14 + }, + { + "name": "Volailles et gibiers", + "id": 56, + "category": 10 + }, + { + "name": "Apéritif", + "id": 41, + "category": 14 + }, + { + "name": "Agneau", + "id": 59, + "category": 10 + } + ] +} \ No newline at end of file diff --git a/js/App.hx b/js/App.hx new file mode 100755 index 0000000..0b1372a --- /dev/null +++ b/js/App.hx @@ -0,0 +1,262 @@ +//React lib +import react.ReactMacro.jsx; +import react.ReactDOM; +import react.*; +import react.router.*; +//custom components +import react.order.*; +import react.product.*; +import react.store.*; +import react.map.*; +import react.user.*; + +//require bootstrap JS since it's bundled with browserify +//@:jsRequire('bootstrap') extern class Bootstrap{} +//@:jsRequire('jquery') extern class JQ extends js.jquery.JQuery{} + +class App { + + public static var instance : App; + public var LANG : String; + public var currency : String; //currency symbol like € or $ + public var t : sugoi.i18n.GetText;//gettext translator + + //i dont want to use redux now... saved state from react.OrderBox + public static var SAVED_ORDER_STATE : Dynamic; + + function new(?lang="fr",?currency="€") { + //singleton + instance = this; + if(lang!=null) this.LANG = lang; + this.currency = currency; + } + + /** + * Returns a jquery object like $() in javascript + * @deprecated + */ + public static inline function j(r:Dynamic):js.JQuery { + return new js.JQuery(r); + } + + public static inline function jq(r:Dynamic):js.jquery.JQuery{ + return new js.jquery.JQuery(r); + } + + /** + * The JS App will be available as "_" in the document. + */ + public static function main() { + + //untyped js.Browser.window.$ = js.Lib.require("jQuery"); + untyped js.Browser.window._ = new App(); + } + + public function getCart() { + return new ShopCart(); + } + + public function getTagger(cid:Int ) { + return new Tagger(cid); + } + + public function getTuto(name:String, step:Int) { + new Tuto(name,step); + } + + /** + * remove method for IE compat + */ + public function remove(el:js.html.Element){ + if (el == null) return; + el.parentElement.removeChild(el); + } + + public function getVATBox(ttcprice:Float,currency:String,rates:String,vat:Float,formName:String){ + + var input = js.Browser.document.querySelector('form input[name="${formName}_price"]'); + + remove( js.Browser.document.querySelector('form input[name="${formName}_vat"]').parentElement.parentElement ); + + ReactDOM.render(jsx('<$VATBox ttc="$ttcprice" currency="$currency" vatRates="$rates" vat="$vat" formName="$formName"/>'), input.parentElement); + + //remove(input); + + } + + /*public function getProductComposer(){ + //js.Browser.document.addEventListener("DOMContentLoaded", function(event) { + //ReactDOM.render(jsx('<$ComposerApp/>'), js.Browser.document.getElementById("app")); + //}); + }*/ + + /** + * Removes the form element and replace it by a react js component + * @param divId + * @param productName + * @param txpProductId + * @param formName + */ + public function getProductInput(divId:String, productName:String, txpProductId:String, formName:String ){ + + js.Browser.document.addEventListener("DOMContentLoaded", function(event) { + + //dirty stuff to remove "real" input, and replace it by the react one + App.j("form input[name='"+formName+"_name']").parent().parent().remove(); + App.j("form select[name='" + formName+"_txpProductId']").parent().parent().remove(); + + if (txpProductId == null) txpProductId = ""; + + ReactDOM.render(jsx('<$ProductInput productName="$productName" txpProductId="$txpProductId" formName="$formName"/>'), js.Browser.document.getElementById(divId)); + }); + } + + public function initReportHeader(){ + ReactDOM.render(jsx('<$ReportHeader />'), js.Browser.document.querySelector('div.reportHeaderContainer')); + } + + public function initOrderBox(userId:Int, distributionId:Int, contractId:Int, contractType:Int, date:String, place:String, userName:String, currency:String, hasPayments:Bool,callbackUrl:String){ + + untyped App.j("#myModal").modal(); + var onValidate = function() js.Browser.location.href = callbackUrl; + var node = js.Browser.document.querySelector('#myModal .modal-body'); + ReactDOM.unmountComponentAtNode(node); //the previous modal DOM element is still there, so we need to destroy it + ReactDOM.render(jsx('<$OrderBox userId="$userId" distributionId="$distributionId" + contractId="$contractId" contractType="$contractType" date="$date" place="$place" userName="$userName" + onValidate=$onValidate currency=$currency hasPayments=$hasPayments />'),node,postReact); + + } + + function postReact(){ + trace("post react"); + haxe.Timer.delay(function(){ + untyped jq('[data-toggle="tooltip"]').tooltip(); + untyped jq('[data-toggle="popover"]').popover(); + },500); + + } + + public static function roundTo(n:Float, r:Int):Float { + return Math.round(n * Math.pow(10,r)) / Math.pow(10,r) ; + } + + + /** + * Ajax loads a page and display it in a modal window + * @param url + * @param title + */ + public function overlay(url:String,?title,?large=true) { + + if (title != null) title = StringTools.urlDecode(title); + + var r = new haxe.Http(url); + r.onData = function(data) { + + //setup body and title + var m = App.j("#myModal"); + m.find(".modal-body").html(data); + if (title != null) m.find(".modal-title").html(title); + + if (!large) m.find(".modal-dialog").removeClass("modal-lg"); + + + untyped App.j('#myModal').modal(); //bootstrap 3 modal window + + } + r.request(); + } + + /** + * Displays a login box + */ + public function loginBox(redirectUrl:String,?message:String,?phoneRequired=false) { + var m = App.j("#myModal"); + m.find(".modal-title").html("S'identifier"); + m.find(".modal-dialog").removeClass("modal-lg"); + untyped m.modal(); + ReactDOM.render(jsx('<$LoginBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'), js.Browser.document.querySelector('#myModal .modal-body')); + return false; + } + + /** + * Displays a sign up box + */ + public function registerBox(redirectUrl:String,?message:String,?phoneRequired=false) { + var m = App.j("#myModal"); + m.find(".modal-title").html("S'inscrire"); + m.find(".modal-dialog").removeClass("modal-lg"); + untyped m.modal(); + ReactDOM.render(jsx('<$RegisterBox redirectUrl="$redirectUrl" message=$message phoneRequired="$phoneRequired"/>'), js.Browser.document.querySelector('#myModal .modal-body')); + return false; + } + + public function shop(place:Int, date:String) { + ReactDOM.render(jsx('<$Store date=$date place=$place/>'), js.Browser.document.querySelector('#shop')); + } + + public function groupMap(lat:String,lng:String,address:String) { + ReactDOM.render(jsx('<$GroupMapRoot lat="$lat" lng="$lng" address="$address"/>'), js.Browser.document.querySelector('#map')); + } + + /** + * Helper to get values of a bunch of checked checkboxes + * @param formSelector + */ + public function getCheckboxesId(formSelector:String):Array{ + var out = []; + var checkboxes = js.Browser.document.querySelectorAll(formSelector + " input[type=checkbox]"); + for ( input in checkboxes ){ + var input : js.html.InputElement = cast input; + if ( input.checked ) out.push(input.value); + } + return out; + } + + + #if plugins + public function getHostedPlugin(){ + return new hosted.js.App(); + } + #end + + /** + * set up a warning message when leaving the page + */ + public function setWarningOnUnload(active:Bool, ?msg:String){ + if (active){ + js.Browser.window.addEventListener("beforeunload", warn); + }else{ + js.Browser.window.removeEventListener("beforeunload", warn); + } + + } + + function warn(e:js.html.Event) { + var msg = "Voulez vous vraiment quitter cette page ?"; + //js.Browser.window.confirm(msg); + untyped e.returnValue = msg; //Gecko + IE + e.preventDefault(); + return msg; //Gecko + Webkit, Safari, Chrome etc. + } + + /** + * Anti Doubleclick with btn elements. + * Can be bypassed by adding a .btn-noAntiDoubleClick class + */ + public function antiDoubleClick(){ + + for( n in js.Browser.document.querySelectorAll(".btn:not(.btn-noAntiDoubleClick)") ){ + n.addEventListener("click",function(e:js.html.MouseEvent){ + var x = untyped e.target; + x.classList.add("disabled"); + haxe.Timer.delay(function(){ + x.classList.remove("disabled"); + },1000); + }); + } + + } +} + + diff --git a/js/ShopCart.hx b/js/ShopCart.hx new file mode 100644 index 0000000..9013a18 --- /dev/null +++ b/js/ShopCart.hx @@ -0,0 +1,445 @@ +import Common; +import js.JQuery; +/** + * JS Shopping Cart + * + * @author fbarbut + */ +class ShopCart +{ + + public var products : Map; //product db + public var productsArray : Array; //to keep order of products + public var categories : Array<{name:String,pinned:Bool,categs:Array}>; //categ db + public var pinnedCategories : Array<{name:String,pinned:Bool,categs:Array}>; //categ db + public var order : OrderInSession; + + var loader : JQuery; //ajax loader gif + + //for scroll mgmt + var cartTop : Int; + var cartLeft : Int; + var cartWidth : Int; + var jWindow : JQuery; + var cartContainer : JQuery; + + var date : String; + var place : Int; + + + public function new() + { + products = new Map(); + productsArray = []; + + order = cast { products:[] }; + categories = []; + pinnedCategories = []; + } + + + + public function add(pid:Int) { + loader.show(); + + var q = App.j('#productQt' + pid).val(); + var qt = 0.0; + var p = this.products.get(pid); + if (p.hasFloatQt) { + q = StringTools.replace(q, ",", "."); + qt = Std.parseFloat(q); + }else { + qt = Std.parseInt(q); + } + + if (qt == null) { + qt = 1; + } + //trace("qté : "+qt); + + //add server side + var r = new haxe.Http('/shop/add/$pid/$qt'); + + r.onData = function(data:String) { + + loader.hide(); + + var d = haxe.Json.parse(data); + if (!d.success) js.Browser.alert("Erreur : "+d); + + //add locally + subAdd(pid, qt); + render(); + + + } + r.request(); + + } + + + function subAdd(pid, qt:Float ) { + + for ( p in order.products) { + if (p.productId == pid) { + + p.quantity += qt; + render(); + return; + } + } + + order.products.push( { productId:pid, quantity:qt } ); + } + + /** + * Render the shopping cart and total + */ + function render() { + var c = App.j("#cart"); + c.empty(); + + //render items in shopping cart + c.append( Lambda.map(order.products, function( x ) { + var p = this.products.get(x.productId); + if (p == null) { + //the product may have been disabled by an admin + return ""; + } + + var btn = " "; + return "
+
" + x.quantity + " x " + p.name+"
+
"+btn+"
+
"; + }).join("\n") ); + + + //compute total price + var total = 0.0; + for (p in order.products) { + var pinfo = products.get(p.productId); + if (pinfo == null) continue; + total += p.quantity * pinfo.price; + } + var ffilter = new sugoi.form.filters.FloatFilter(); + + var total = ffilter.filterString(Std.string(App.roundTo(total,2))); + c.append("
TOTAL : " + total + "
"); + + + if (order.products.length > 0){ + App.instance.setWarningOnUnload(true,"Vous avez une commande en cours. Si vous quittez cette page sans confirmer, votre commande sera perdue."); + }else{ + App.instance.setWarningOnUnload(false); + } + } + + + function findCategoryName(cid:Int):String{ + + for ( cg in this.categories ){ + for (c in cg.categs){ + if (cid == c.id) { + return c.name; + } + } + } + for ( cg in this.pinnedCategories ){ + for (c in cg.categs){ + if (cid == c.id) { + return c.name; + } + } + } + return null; + } + + /** + * Dynamically sort products by categories + */ + public function sortProductsBy(){ + + //store products by groups + var groups = new Map}>(); + var pinned = new Map}>(); + + var firstCategGroup = this.categories[0].categs; + + //trace(firstCategGroup); + //trace(pinnedCategories); + + var pList = this.productsArray.copy(); + + //for ( p in pList) trace(p.name+" : " + p.categories); + //trace("----------------"); + + //sort by categs + for ( p in pList.copy() ){ + //trace(p.name+" : " + p.categories); + untyped p.element.remove(); + + for ( categ in p.categories){ + + if (Lambda.find(firstCategGroup, function(c) return c.id == categ) != null){ + + //is in this category group + var g = groups.get(categ); + if ( g == null){ + var name = findCategoryName(categ); + g = {name:name,products:[]}; + } + g.products.push(p); + //trace("remove " + p.name); + pList.remove(p); + groups.set(categ, g); + + } + else{ + // is in pinned group ? + var isInPinnedCateg = false; + for ( cg in pinnedCategories){ + if (Lambda.find(cg.categs, function(c) return c.id == categ) != null){ + isInPinnedCateg = true; + break; + } + } + + if (isInPinnedCateg){ + + var c = pinned.get(categ); + if ( c == null){ + + var name = findCategoryName(categ); + c = {name:name,products:[]}; + } + c.products.push(p); + //trace( "add " + p.name+" in PINNED"); + pList.remove(p); + pinned.set(categ, c); + + + }else{ + //not in the selected categ nor in pinned groups + continue; + } + } + } + } + + //if some untagged products remain + if (pList.length > 0){ + groups.set(0,{name:"Autres",products:pList}); + } + //trace("----------------"); + //render + var container = App.j(".shop .body"); + //render firts "pinned" groups , then "groups" + for ( source in [pinned, groups]){ + + for (o in source){ + + if (o.products.length == 0) continue; + container.append("
" + o.name + "
"); + for ( p in o.products){ + //trace("GROUP "+o.name+" : "+p.name); + //if the element has already been inserted, we need to clone it + if (untyped p.element.parent().length == 0){ + container.append( untyped p.element ); + }else{ + var clone = untyped p.element.clone(); + container.append( clone ); + } + + + } + } + } + App.j(".product").show(); + + + } + + /** + * is shopping cart empty ? + */ + public function isEmpty(){ + return order.products.length == 0; + + } + + /** + * submit cart + */ + public function submit() { + + var req = new haxe.Http("/shop/submit"); + req.onData = function(d) { + App.instance.setWarningOnUnload(false); + js.Browser.location.href = "/shop/validate/"+place+"/"+date; + + } + req.addParameter("data", haxe.Json.stringify(order)); + req.request(true); + + } + + /** + * filter products by category + */ + public function filter(cat:Int) { + + //icone sur bouton + App.j(".tag").removeClass("active").children().remove("span");//clean + + var bt = App.j("#tag" + cat); + bt.addClass("active").prepend(" "); + + + //affiche/masque produits + for (p in products) { + if (cat==0 || Lambda.has(p.categories, cat)) { + App.j(".shop .product" + p.id).fadeIn(300); + }else { + App.j(".shop .product" + p.id).fadeOut(300); + } + } + + + + + } + + /** + * remove a product from cart + * @param pid + */ + public function remove(pid:Int ) { + + loader.show(); + + //add server side + var r = new haxe.Http('/shop/remove/$pid'); + + r.onData = function(data:String) { + + loader.hide(); + + var d = haxe.Json.parse(data); + if (!d.success) js.Browser.alert("Erreur : "+d); + + //remove locally + for ( p in order.products.copy()) { + if (p.productId == pid) { + order.products.remove(p); + render(); + return; + } + } + render(); + + + } + r.request(); + + + + + } + + /** + * loads products DB and existing cart in ajax + */ + public function init(place:Int,date:String) { + + this.place = place; + this.date = date; + + loader = App.j("#cartContainer #loader"); + + var req = new haxe.Http("/shop/init/"+place+"/"+date); + req.onData = function(data) { + loader.hide(); + + var data : { + products:Array, + categories:Array<{name:String,pinned:Bool,categs:Array}>, + order:OrderInSession } = haxe.Unserializer.run(data); + + //populate local categories lists + for ( cg in data.categories){ + if (cg.pinned){ + pinnedCategories.push(cg); + }else{ + categories.push(cg); + } + } + + //product DB + for (p in data.products) { + //catch dom element for further usage + untyped p.element = App.j(".product"+p.id); + + var id : Int = p.id; + //var id : Int = p.id; + //id = id + 1; + this.products.set(id, p); + this.productsArray.push(p); + //trace(p.name+" : " + p.categories); + } + + //existing order + for ( p in data.order.products) { + subAdd(p.productId,p.quantity ); + } + + render(); + + sortProductsBy(); + + } + req.request(); + + //DISABLED : pb quand le panier est plus haut que l'ecran + //scroll mgmt, only for large screens. Otherwise let the cart on page bottom. + /*if (js.Browser.window.matchMedia("(min-width: 1024px)").matches) { + + jWindow = App.j(js.Browser.window); + cartContainer = App.j("#cartContainer"); + cartTop = cartContainer.position().top; + cartLeft = cartContainer.position().left; + cartWidth = cartContainer.width(); + jWindow.scroll(onScroll); + + }*/ + + + } + + /** + * keep the cart on top when scrolling + * @param e + */ + public function onScroll(e:Dynamic) { + + //cart container top position + + if (jWindow.scrollTop() > cartTop) { + //trace("absolute !"); + cartContainer.addClass("scrolled"); + cartContainer.css('left', Std.string(cartLeft) + "px"); + cartContainer.css('top', Std.string(/*cartTop*/10) + "px"); + cartContainer.css('width', Std.string(cartWidth) + "px"); + + }else { + cartContainer.removeClass("scrolled"); + cartContainer.css('left',""); + cartContainer.css('top', ""); + cartContainer.css('width', ""); + } + + + + } + +} \ No newline at end of file diff --git a/js/Tagger.hx b/js/Tagger.hx new file mode 100755 index 0000000..60a4f37 --- /dev/null +++ b/js/Tagger.hx @@ -0,0 +1,151 @@ +package ; +import Common; +import js.JQuery; +/** + * + * Tag products with categories + * + * @author fbarbut + */ +@:keep +class Tagger +{ + + var contractId : Int; + var data:TaggerInfos; + var pids : Array; //selected product Ids + + public function new(cid:Int) + { + contractId = cid; + pids = []; + } + + public function init() { + var req = new haxe.Http("/product/categorizeInit/"+contractId); + req.onData = function(_data) { + data = haxe.Json.parse(_data); + render(); + } + req.request(); + + } + + function render() { + + var html = new StringBuf(); + + html.add(""); + for (p in data.products) { + html.add(""); + var checked = Lambda.has(pids,p.product.id) ? "checked" : ""; + html.add(''); + html.add(""); + var tags = []; + + //trace('product tags ${p.categories} from tags ${data.categories}'); + + for (c in p.categories) { + //trouve le nom du tag + var name = ""; + var color = ""; + for (gc in data.categories) { + for ( t in gc.tags) { + if (c == t.id) { + name = t.name; + color = gc.color; + break; + } + } + } + //var bt = App.jq("[X]") + tags.push(""+name+""); + } + + html.add(""); + html.add(""); + } + html.add("
" + p.product.name+""+ tags.join(" ") +"
"); + App.jq("#tagger").html(html.toString()); + App.jq("#tagger .tag").click(function(e) { + + var el : js.html.Element = cast e.currentTarget; + + //find tag Id + var tid = Std.parseInt(el.getAttribute('class').split(" ")[1].substr(1)); + + //find product Id + var pid = Std.parseInt(el.parentElement.parentElement.getAttribute('class').substr(1)); + + //remove element + el.remove(); + + //datas + remove(tid,pid); + }); + + } + + public function add() { + var tagId = Std.parseInt(App.jq("#tag").val()); + + if (tagId == 0) js.Browser.alert("Impossible de trouver la catégorie selectionnée"); + + pids = []; + for ( e in App.jq("#tagger input:checked").elements() ) { + pids.push(Std.parseInt(e.attr("name").substr(1))); + } + if (pids.length == 0) js.Browser.alert("Sélectionnez un produit afin de pouvoir lui attribuer une catégorie"); + + for (p in pids) { + addTag(tagId, p); + } + + render(); + } + + public function remove(tagId:Int,productId:Int) { + //data + for ( p in data.products) { + if (p.product.id == productId) { + for ( t in p.categories) { + if (t == tagId) p.categories.remove(t); + } + } + } + } + + function addTag(tagId:Int, productId:Int) { + + //check for doubles + for ( p in data.products) { + if (p.product.id == productId) { + for (t in p.categories) { + if (t == tagId) return; + } + } + } + + //data + for ( p in data.products) { + if (p.product.id == productId) { + p.categories.push(tagId); + break; + } + } + + } + + public function submit() { + + var req = new haxe.Http("/product/categorizeSubmit/" + contractId); + req.addParameter("data", haxe.Json.stringify(data)); + req.onData = function(_data) { + + js.Browser.alert(_data); + } + req.request(true); + + } + +} \ No newline at end of file diff --git a/js/Tuto.hx b/js/Tuto.hx new file mode 100644 index 0000000..589a841 --- /dev/null +++ b/js/Tuto.hx @@ -0,0 +1,131 @@ +package; +import Common; + +/** + * Tutorial javascript widget + * @author fbarbut + */ +class Tuto +{ + var name:String; + var step:Int; + + static var LAST_ELEMENT :String = null; //last hightlit element + + public function new(name:String, step:Int){ + + this.name = name; + this.step = step; + + TutoDatas.get(name, init); + } + + /** + * asyn init + * @param tuto + */ + function init(tuto) + { + + var s = tuto.steps[step]; + + //close previous popovers + var p = App.jq(".popover"); + untyped p.popover('hide'); + + var t = App.instance.t; + if (t == null) trace("Gettext translator is null"); + + if (s == null) { + /** + * tutorial is finished : display a modal window + */ + var m = App.jq('#myModal'); + untyped m.modal('show'); + m.addClass("help"); + m.find(".modal-header").html(" "+tuto.name); + m.find(".modal-body").html(" "+t._("This tutorial is over.")); + var bt = App.jq(" "+t._("Come back to tutorials page")+""); + bt.click(function(?_) { + untyped m.modal('hide'); + js.Browser.location.href = "/contract?stopTuto=1"; + }); + m.find(".modal-footer").append(bt); + m.find(".modal-dialog").removeClass("modal-lg"); //small window pls + + }else if (s.element == null) { + + /** + * no element, make a modal window (usually its the first step of the tutorial) + */ + var m = App.jq('#myModal'); + untyped m.modal('show'); + m.addClass("help"); + m.find(".modal-body").html(s.text); + m.find(".modal-header").html(" "+tuto.name); + + var bt = App.jq(" "+t._("OK")+""); + bt.click(function(?_) { + untyped m.modal('hide'); + new Tuto(name, step + 1); + }); + m.find(".modal-footer").append(bt); + m.find(".modal-dialog").removeClass("modal-lg"); //small window pls + + }else { + + //prepare Bootstrap "popover" + var x = App.jq(s.element).first().attr("title", tuto.name+"
"+(step+1)+"/"+tuto.steps.length+"
"); + var text = "

" + s.text + "

"; + var bt = null; + switch(s.action) { + case TANext : + + bt = App.jq("

"+t._("Next")+"

"); + bt.click(function(?_) { + //untyped m.modal('hide'); + new Tuto(name, step + 1); + if(LAST_ELEMENT!=null) App.jq(s.element).removeClass("highlight"); + }); + + default: + } + + //configure and open popover + var p = switch(s.placement) { + case TPTop: "top"; + case TPBottom : "bottom"; + case TPLeft : "left"; + case TPRight : "right"; + default : null; + } + var options = { container:"body", content:text, html:true , placement:p}; + untyped x.popover(options).popover('show'); + + + //add a footer + var footer = App.jq(""); + + if (bt != null) footer.find(".pull-right").append(bt); + footer.find(".pull-left").append(makeCloseButton(t._('Stop'))); + + App.jq(".popover .popover-content").append(footer); + + //highlight + App.jq(s.element).first().addClass("highlight"); + LAST_ELEMENT = s.element; + } + } + + /** + * Make a "close" bt + */ + function makeCloseButton(?text) { + var bt = App.jq(" "+(text==null?"":text)+""); + bt.click(function(?_) { + js.Browser.location.href = "/contract?stopTuto=1"; + }); + return bt; + } + +} \ No newline at end of file diff --git a/js/libs/libs.js b/js/libs/libs.js new file mode 100644 index 0000000..eb7a3f0 --- /dev/null +++ b/js/libs/libs.js @@ -0,0 +1,28 @@ +// +// npm dependencies library +// +(function(scope) { + 'use-strict'; + scope.__registry__ = Object.assign({}, scope.__registry__, { + // + // list npm modules required in Haxe + // + 'bootstrap': require('bootstrap'), + 'react': require('react'), + 'react-dom': require('react-dom'), + 'react-bootstrap-typeahead': require('react-bootstrap-typeahead'), + 'react-datetime': require('react-datetime'), + 'leaflet': require('leaflet'), + 'react-leaflet': require('react-leaflet'), + 'react-places-autocomplete': require('react-places-autocomplete'), + 'geolib': require('geolib'), + 'react-router':require('react-router'), + 'react-router-dom':require('react-router-dom'), + }); + + if (process.env.NODE_ENV !== 'production') { + // enable React hot-reload + require('haxe-modular'); + } + +})(typeof $hx_scope != "undefined" ? $hx_scope : $hx_scope = {}); diff --git a/js/react/DateTimeField.hx b/js/react/DateTimeField.hx new file mode 100644 index 0000000..f687510 --- /dev/null +++ b/js/react/DateTimeField.hx @@ -0,0 +1,7 @@ +package react; + +/** + * @doc https://www.npmjs.com/package/react-bootstrap-datetimepicker + */ +@:jsRequire('react-bootstrap-datetimepicker') +extern class DateTimeField extends react.ReactComponent {} diff --git a/js/react/Error.hx b/js/react/Error.hx new file mode 100644 index 0000000..2676aa6 --- /dev/null +++ b/js/react/Error.hx @@ -0,0 +1,29 @@ +package react; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +/** + * A Error Div + */ +class Error extends react.ReactComponentOfProps<{error:String}> +{ + + public function new(props:Dynamic) + { + super(props); + } + + + override public function render(){ + + if (props.error == null) return null; + + return jsx('
+ ${props.error} +
+ '); + } + + +} \ No newline at end of file diff --git a/js/react/Message.hx b/js/react/Message.hx new file mode 100644 index 0000000..c9a3478 --- /dev/null +++ b/js/react/Message.hx @@ -0,0 +1,26 @@ +package react; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +/** + * A message Div + */ +class Message extends react.ReactComponentOfProps<{message:String}> +{ + + public function new(props:Dynamic) + { + super(props); + } + + override public function render(){ + + if (props.message == null) return null; + + return jsx('
+ ${props.message} +
+ '); + } +} \ No newline at end of file diff --git a/js/react/ReportHeader.hx b/js/react/ReportHeader.hx new file mode 100644 index 0000000..7236766 --- /dev/null +++ b/js/react/ReportHeader.hx @@ -0,0 +1,94 @@ +package react; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +//datepicker broken if called like this //import react.DateTimeField.*; +//@:jsRequire('react-bootstrap-datetimepicker') +//extern class DateTimeField extends react.ReactComponent {} + + +/** + * @doc https://github.com/YouCanBookMe/react-datetime + */ +@:jsRequire('react-datetime') +extern class DateTime extends react.ReactComponent {} + + + + +/** + * ... + * @author fbarbut + */ +class ReportHeader extends react.ReactComponentOfState +{ + + public function new() + { + super(); + state = {startDate:null, endDate:null, groupBy:null, contracts:[]}; + + //load fr locale of moment.js + var moment = js.Lib.require('moment'); + js.Lib.require('moment/locale/fr'); + + } + + override public function render(){ + + return jsx('
+
+
+ + + + +
+ +
+ +
+ +
+ +
+ +
+
+ Afficher +
+
'); + + } + + function onDateChange(e:js.html.Event){ + trace("onDateChange"); + //var name :String = untyped e.target.name; + //var value :String = untyped e.target.value; + //trace('$name $value'); + trace(e); + //e.preventDefault(); + } + + /** + * @doc https://facebook.github.io/react/docs/forms.html + */ + function onGroupByChange(e:js.html.Event){ + e.preventDefault(); + trace("onGRoupByChange"); + var name :String = untyped e.target.name; + var value :String = untyped e.target.value; + if (value == "ByMember"){ + state.groupBy = ByMember; + }else{ + state.groupBy = ByProduct; + } + trace(state); + setState(state); + } + +} \ No newline at end of file diff --git a/js/react/Typeahead.hx b/js/react/Typeahead.hx new file mode 100644 index 0000000..b09ffbf --- /dev/null +++ b/js/react/Typeahead.hx @@ -0,0 +1,17 @@ +package react; + +/** + * + * Use the 1.4.x version. The 2.x version is still buggy + * + * @doc https://www.npmjs.com/package/react-bootstrap-typeahead + */ +@:jsRequire('react-bootstrap-typeahead', 'Typeahead') +extern class Typeahead extends react.ReactComponent{} + +/** + * @doc https://github.com/ericgio/react-bootstrap-typeahead/blob/803f61c1c8d0c943106233ed3c9306acc19b5b2b/docs/API.md#asynctypeahead + * Async component is needed when options and searches are managed asynchronously + */ +@:jsRequire('react-bootstrap-typeahead', 'AsyncTypeahead') +extern class AsyncTypeahead extends react.ReactComponent{} \ No newline at end of file diff --git a/js/react/VATBox.hx b/js/react/VATBox.hx new file mode 100644 index 0000000..4d30aad --- /dev/null +++ b/js/react/VATBox.hx @@ -0,0 +1,125 @@ +package react; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +// I need to store also the "input" because of https://stackoverflow.com/questions/29140354/how-to-handle-decimal-values-in-reacts-onchange-event-for-input +typedef VATBoxState = {ht:Float, ttc:Float, vat:Float, htInput:String, ttcInput:String,lastEdited:String}; + + +/** + * A box to manage prices with and without VAT + * @author fbarbut + */ +class VATBox extends react.ReactComponentOfPropsAndState<{ttc:Float,currency:String,vatRates:String,vat:Float,formName:String},VATBoxState> +{ + + public function new(props) + { + super(props); + //trace(props); + + this.state = { + ht : round(props.ttc/(1+props.vat/100)), + htInput : Std.string(round(props.ttc/(1+props.vat/100))), + ttc : round(props.ttc), + ttcInput : Std.string(round(props.ttc)), + vat:props.vat, + lastEdited:null + }; + + + } + + override public function render(){ + + var rates :Array= props.vatRates.split("|").map(Std.parseFloat); + + var options = [for (r in rates) jsx('') ]; + var priceInputName = props.formName+"_price"; + var vatInputName = props.formName+"_vat"; + + return jsx('
+ +
+
Hors taxe
+
Taux de TVA
+
TTC
+
+ +
+
+
+ +
${props.currency}
+
+
+ +
+ +
+ +
+
+ +
${props.currency}
+
+
+
+ + + + +
+ '); + } + + + /** + * Recompute prices + */ + function onChange(e:js.html.Event){ + + e.preventDefault(); + var name :String = untyped e.target.name; + var input : String = Std.string(untyped e.target.value); + if (input == null || input == "") input = "0"; + input = StringTools.replace(input, ",", "."); + var value : Float = Std.parseFloat(input); + if (value == null) value = 0; + + var rate = 1 + (state.vat / 100); + //trace('name:$name - raw:' + untyped e.target.value+' - input:$input - value:$value '); + + switch(name){ + case "htInput": + + this.setState(cast {ht:value , htInput:input , ttc: round(value * rate), ttcInput:round(value * rate) , lastEdited:"htInput"}); + + case "ttcInput": + this.setState(cast {ht: round(value / rate), htInput : round(value/rate), ttcInput:input , ttc:value , lastEdited:"ttcInput"}); + + case "vat": + rate = 1 + (value / 100); + if (state.lastEdited == "htInput"){ + //compute ttc from ht + this.setState(cast { vat:value, ht:state.ht, htInput:state.ht, ttc:round(state.ht * rate) , ttcInput:round(state.ht * rate)} ); + }else{ + //compute ht from ttc + this.setState(cast { vat:value, ht: round( state.ttc/rate ), htInput: round( state.ttc/rate ), ttc:state.ttc , ttcInput:state.ttc} ); + } + default: + + } + + + } + + inline function round(f:Float):Float{ + return Math.round(f * 100) / 100; + } + + +} \ No newline at end of file diff --git a/js/react/map/GroupMap.hx b/js/react/map/GroupMap.hx new file mode 100644 index 0000000..70e36af --- /dev/null +++ b/js/react/map/GroupMap.hx @@ -0,0 +1,252 @@ +package react.map; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; +import leaflet.L; + +using Lambda; + + +/** + * Externs for react-leaflet + * @doc https://react-leaflet.js.org/docs/en/intro.html + */ +@:jsRequire('react-leaflet', 'Map') +extern class LeafMap extends ReactComponent {} +@:jsRequire('react-leaflet', 'TileLayer') +extern class TileLayer extends ReactComponent {} +@:jsRequire('react-leaflet', 'Marker') +extern class Marker extends ReactComponent {} +@:jsRequire('react-leaflet', 'CircleMarker') +extern class CircleMarker extends ReactComponent {} +@:jsRequire('react-leaflet', 'Popup') +extern class Popup extends ReactComponent {} +@:jsRequire('react-leaflet', 'FeatureGroup') +extern class FeatureGroup extends ReactComponent {} +@:jsRequire('react-leaflet', 'LayerGroup') +extern class LayerGroup extends ReactComponent {} + + +/* +extern class L2 { + static function icon(a:Dynamic):Dynamic; + static function latLng(lat:Float, lng:Float):Dynamic; +}*/ + +/** + * GroupItem + * @author rcrestey + */ +typedef GroupMapProps = { + var addressCoord:Dynamic; + var groups:Array; + var fetchGroupsInsideBox:Box->Void; + var groupFocusedId:Int; +}; + +typedef GroupMapState = { + var isFitting:Bool; + var focusedMarker:Dynamic; +}; + +typedef Box = { + var minLat:Float; + var maxLat:Float; + var minLng:Float; + var maxLng:Float; +}; + +class GroupMap extends ReactComponentOfPropsAndState { + static inline var DEFAULT_LAT = 46.52863469527167; // center of France + static inline var DEFAULT_LNG = 2.43896484375; // center of France + static inline var INIT_ZOOM = 6; + static inline var DEFAULT_ZOOM = 13; + + var map:Dynamic; + var featureGroup:Dynamic; + var markerMap = new Map(); + + var groupIcon = L.icon({ + iconUrl: '/img/marker.svg', + iconSize: [40, 40], + iconAnchor: [20, 40], + popupAnchor: [0, -30], + className: 'icon' + }); + + var homeIcon = L.icon({ + iconUrl: '/img/home.svg', + iconSize: [40, 40], + iconAnchor: [20, 20], + popupAnchor: [0, -30], + className: 'icon' + }); + + function new() { + super(); + state = { + isFitting: false, + focusedMarker: null + }; + } + + function getMap(element:Dynamic):Void { + map = element.leafletElement; + } + + function getFeatureGroup(element:Dynamic):Void { + featureGroup = element.leafletElement; + setState({ + isFitting: true + }, fitBounds); + } + + function getMarker(element:Dynamic, id:Int):Void { + if (element != null) + markerMap.set(id, element.leafletElement); + } + + + /** + * Call API to get groups in the current bounding box + */ + function getGroups() { + var bounds = map.getBounds(); + var southWest = bounds.getSouthWest(); + var northEast = bounds.getNorthEast(); + + props.fetchGroupsInsideBox({ + minLat: southWest.lat, + maxLat: northEast.lat, + minLng: southWest.lng, + maxLng: northEast.lng + }); + } + + function fitBounds() { + map.fitBounds(featureGroup.getBounds(), { + padding: [30, 30] + }); + } + + function handleMoveEnd() { + if ( + props.addressCoord != null && + !Lambda.empty(props.groups) && + map.distance(map.getCenter(), props.addressCoord) == 0 + ) + setState({ + isFitting: true + }, fitBounds); + else if (state.isFitting) + setState({ + isFitting: false + }); + else + getGroups(); + } + + override public function componentDidMount() { + if (props.addressCoord == null) + getGroups(); + } + + override public function shouldComponentUpdate(nextProps:GroupMapProps, nextState:GroupMapState) { + if (nextState.focusedMarker != state.focusedMarker) + return false; + return true; + } + + override public function componentDidUpdate(prevProps:GroupMapProps, prevState:GroupMapState) { + if (props.groupFocusedId != null) { + if ( + prevProps.groupFocusedId != props.groupFocusedId + || state.focusedMarker == null + ) { + if (state.focusedMarker != null) + state.focusedMarker.closePopup(); + + var focusedMarker = markerMap.get(props.groupFocusedId); + focusedMarker.openPopup(); + + setState({ + focusedMarker: focusedMarker + }); + } + } + else if (prevProps.groupFocusedId != null && state.focusedMarker != null) { + state.focusedMarker.closePopup(); + + setState({ + focusedMarker: null + }); + } + } + + override public function render() { + var center = props.addressCoord == null + ? L.latLng(DEFAULT_LAT, DEFAULT_LNG) + : props.addressCoord; + + var zoom = props.addressCoord == null + ? INIT_ZOOM + : DEFAULT_ZOOM; + + return jsx(' + + + + ${renderGroupMarkers()} + ${renderHomeMarker()} + + + '); + } + + function renderGroupMarkers() { + var markers = props.groups.map(function(group) { + var coord = [group.place.latitude, group.place.longitude]; + + function markerGetter(e:Dynamic) { + getMarker(e, group.place.id); + } + + var image = group.image==null ? null : jsx(''); + + return jsx(' + + + + + + '); + }); + + return jsx('
${markers}
'); + } + + function renderHomeMarker() { + if (props.addressCoord != null) + return jsx(''); + return null; + } +} diff --git a/js/react/map/GroupMapRoot.hx b/js/react/map/GroupMapRoot.hx new file mode 100644 index 0000000..55367c6 --- /dev/null +++ b/js/react/map/GroupMapRoot.hx @@ -0,0 +1,312 @@ +package react.map; +import js.Promise; +import react.ReactComponent; +import react.ReactMacro.jsx; +import utils.HttpUtil; +import leaflet.L; +import Common; +using Lambda; + +@:jsRequire('react-places-autocomplete', 'default') +extern class Autocomplete extends ReactComponent {} + +@:jsRequire('react-places-autocomplete') +extern class GeoUtil { + static function geocodeByAddress(address:Dynamic):Promise; +} + +@:jsRequire('geolib') +extern class Geolib { + static function getDistance(start:Dynamic, end:Dynamic):Float; +} + +/** + * Groups Map + * @author rcrestey + */ + +typedef GroupMapRootState = { + var point:Dynamic; + var address:String; + var groups:Array; + var groupFocusedId:Int; + var isInit:Bool; +}; + +typedef GroupMapRootProps = { + var lat:Float; + var lng:Float; + var address:String; +}; + +class GroupMapRoot extends ReactComponentOfState{ + + static inline var GROUP_MAP_URL = '/api/group/map'; + + var distanceMap = new Map(); + + public function new(props) + { + super(props); + + state = { + point: L.latLng(props.lat, props.lng), + address: props.address, + groups: [], + groupFocusedId: null, + isInit: false + }; + } + + function onChange(address) { + setState({ + address: address + }); + } + + function openPopup(group:Dynamic) { + setState({ + groupFocusedId: group.place.id + }); + } + + function closePopup() { + setState({ + groupFocusedId: null + }); + } + + function geocodeByAddress(address:String):Promise { + return GeoUtil.geocodeByAddress(address) + .then(function(results) { + var lat = results[0].geometry.location.lat(); + var lng = results[0].geometry.location.lng(); + + return {lat: lat, lng: lng}; + }); + } + + /** + * Call API to find groups at $lat and $lng + * @param lat - + * @param lng - + */ + function fetchGroups(lat:Float, lng:Float) { + HttpUtil.fetch(GROUP_MAP_URL, GET, {lat: lat, lng: lng}, JSON) + .then(function(results) { + setState({ + point: L.latLng(lat, lng), + groups: results.groups, + isInit: true + }, fillDistanceMap); + }) + .catchError(function(error) { + trace('Error', error); + }); + } + + /** + * Call API to look for groups in the defined bounding box + */ + var wait : Bool; + function fetchGroupsInsideBox(newBox) { + + switch(wait){ + case null,false : wait = true; + case true : trace("stop"); return; + } + + HttpUtil.fetch(GROUP_MAP_URL, GET, newBox, JSON) + .then(function(results) { + wait = false; + setState({ + groups: results.groups + }, fillDistanceMap); + }); + /*.catchError(function(error) { + trace('Error', error + " stack:"+haxe.CallStack.toString(haxe.CallStack.exceptionStack())) ; + wait = false; + });*/ + } + + function getGroupDistance(group:GroupOnMap):Float { + if (state.point == null) + return null; + + var start = { + latitude: state.point.lat, + longitude: state.point.lng + }; + var end = { + latitude: group.place.latitude, + longitude: group.place.longitude + }; + + return Geolib.getDistance(start, end); + } + + function fillDistanceMap() { + for (group in state.groups) { + distanceMap.set(group.place.id, getGroupDistance(group)); + } + + orderGroupsByDistance(state.groups); + + setState({ + groups: state.groups + }); + } + + function orderGroupsByDistance(groups:Array) { + groups.sort(function(a, b) { + return distanceMap.get(a.place.id) - distanceMap.get(b.place.id); + }); + } + + function convertDistance(distance:Int):String { // to test + if (distance > 10000) + return Math.floor(distance / 1000) + ' km'; + if (distance > 1000) + return Math.floor(distance / 100) / 10 + ' km'; + return distance + ' m'; + } + + function handleSelect(address:String) { + setState({ + address: address + }); + + geocodeByAddress(address) + .then(function(coord) { + fetchGroups(coord.lat, coord.lng); + }) + .catchError(function(error) { + trace('Error', error); + }); + } + + override public function componentDidMount() { + if (state.point != null) + fetchGroups(state.point.lat, state.point.lng); + else if (state.address != '') + handleSelect(state.address); + } + + function renderSuggestion(obj:Dynamic) { + return jsx(' +
+ + ${obj.formattedSuggestion.mainText}  + ${obj.formattedSuggestion.secondaryText} +
+ '); + } + + override public function render() { + var inputProps = { + value: state.address, + onChange: onChange + }; + + var cssClasses = { + root: 'form-group', + input: 'autocomplete-input', + autocompleteContainer: 'autocomplete-results', + }; + + return jsx(' +
+
+ +
+
+ Trouvez un groupe Cagette près de chez vous   + +
+
+
+
+
${renderGroupList()}
+
${renderGroupMap()}
+
+
'); + } + + function renderGroupMap() { + if (!state.isInit) + return null; + + return jsx(' + + '); + } + + function renderGroupList() { + var groups = state.groups.map(function(group) { + return renderGroup(group); + }); + + return jsx(' +
+ ${groups} +
+ '); + } + + /** + * Renders a group in the left list + */ + function renderGroup(group:GroupOnMap) { + var address = [ + group.place.address1, + group.place.address2, + [group.place.zipCode, group.place.city].join(" "), + ]; + + var addressBlock = Lambda.array(address.mapi(function(index, element) { + if (element != null){ + return jsx('
$element
'); + }else{ + return null; + } + })); + + var distance = null; + if (distanceMap.get(group.place.id) != null) + distance = jsx('
${convertDistance(distanceMap.get(group.place.id))}
'); + + var classNames = ['clickable groupBlock']; + if (group.place.id == state.groupFocusedId) + classNames.push('focused'); + + var img = if(group.image==null) { + null; + }else{ + jsx(''); + } + + return jsx(' + $img +

${group.name}

+
${addressBlock}
+ ${distance} +
'); + } +} diff --git a/js/react/order/InsertOrder.hx b/js/react/order/InsertOrder.hx new file mode 100644 index 0000000..976558b --- /dev/null +++ b/js/react/order/InsertOrder.hx @@ -0,0 +1,112 @@ +package react.order; +import react.ReactDOM; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; +import utils.HttpUtil; +import react.product.ProductSelect; +import react.router.Redirect; +import react.router.Link; + + +/** + * A box to add an order to a member + * @author fbarbut + */ +class InsertOrder extends react.ReactComponentOfPropsAndState<{contractId:Int,userId:Int,distributionId:Int,onInsert:UserOrder->Void},{products:Array,error:String,selected:Int}> +{ + + public function new(props) + { + super(props); + state = {products:[],error:null,selected:null}; + } + + override function componentDidMount() + { + //load product list + HttpUtil.fetch("/api/product/get/", GET, {contractId:props.contractId},PLAIN_TEXT) + .then(function(data:String) { + + /*var data : {products:Array} = haxe.Json.parse(data); + for( p in data.products) { + p.unitType = Type.createEnumIndex(UnitType,untyped p.unitType); + }*/ + + var data : {products:Array} = tink.Json.parse(data); + setState({products:data.products, error:null,selected:null}); + + }).catchError(function(data) { + var data = Std.string(data); + trace("Error",data); + if(data.substr(0,1)=="{"){ + //json error from server + var data : ErrorInfos = haxe.Json.parse(data); + setState(cast {error:data.error.message} ); + }else{ + //js error + setState(cast {error:data} ); + } + }); + } + + override public function render(){ + //redirect to orderBox if a product is selected + var redirect = if(state.selected!=null) jsx('<$Redirect to="/" />') else null; + + return jsx(' +
+ $redirect +

Choisissez le produit à ajouter

+ <$Link className="btn btn-default" to="/"> Retour + <$Error error="${state.error}" /> +
+ <$ProductSelect onSelect=$onSelectProduct products=${state.products} /> +
+ '); + } + + function onSelectProduct(p:ProductInfo){ + var uo : UserOrder = cast { + id:null, + product:p, + quantity:1, + productId:p.id, + productPrice:p.price, + paid:false, + invert:false, + user2:null + }; + props.onInsert(uo); + setState(cast {selected:p.id}); + + //do not insert order now, just warn the OrderBox +/* + //insert order + var data = [{id:null,productId:p.id,qt:1,paid:false,invert:false,user2:null} ]; + var req = { + orders:haxe.Json.stringify(data), + distributionId : props.distributionId, + contractId : props.contractId + }; + var r = HttpUtil.fetch("/api/order/update/"+props.userId, POST, req, JSON); + r.then(function(d:Dynamic) { + + if (Reflect.hasField(d, "error")) { + setState(cast {error:d.error.message}); + }else{ + //WOOT + //trace("OK"); + //go to OrderBox with a redirect + setState(cast {selected:p.id}); + } + }).catchError(function(d) { + trace("PROMISE ERROR", d); + setState(cast {error:d.error.message}); + }); +*/ + } + + + +} \ No newline at end of file diff --git a/js/react/order/Order.hx b/js/react/order/Order.hx new file mode 100644 index 0000000..87259e5 --- /dev/null +++ b/js/react/order/Order.hx @@ -0,0 +1,179 @@ +package react.order; +import react.ReactDOM; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; +import react.product.Product; + +/** + * A User order + * @author fbarbut + */ +class Order extends react.ReactComponentOfPropsAndState<{order:UserOrder,onUpdate:UserOrder->Void,parentBox:react.order.OrderBox},{order:UserOrder,inputValue:String}> +{ + var hasPayments :Bool; + var currency : String; + + public function new(props) + { + super(props); + state = {order:props.order,inputValue:null}; + hasPayments = props.parentBox.props.hasPayments; + currency = props.parentBox.props.currency; + + if (state.order.productUnit == null) state.order.productUnit = Piece; + if (state.order.productQt == null) state.order.productQt = 1; + + state.inputValue = if ( isSmartQtInput(state.order) ){ + Std.string(round(state.order.quantity * state.order.productQt)); + }else{ + Std.string(state.order.quantity); + } + } + + override public function render(){ + var o = state.order; + /*var unit = if (o.productHasFloatQt || o.productHasVariablePrice){ + jsx('
${Formatting.unit(o.productUnit)}
'); + }else{ + jsx('
'); + }*/ + + /* + //use smart qt only if hasFloatQt + var productName = if (o.productHasFloatQt || o.productHasVariablePrice){ + jsx('
${o.productName}
'); + }else{ + jsx('
${o.productName} ${o.productQt} ${Formatting.unit(o.productUnit)}
'); + } + */ + /*var productName = if (o.productHasFloatQt || o.productHasVariablePrice){ + jsx('
${o.productName}
');*/ + + var input = if (isSmartQtInput(o)){ + jsx('
+ +
${Formatting.unit(o.productUnit)}
+
'); + }else{ + jsx('
+ +
'); + } + + var alternated = if(props.parentBox.props.contractType==0 && props.parentBox.state.users!=null){ + //constant orders + var options = props.parentBox.state.users.map(function(x) return jsx('') ); + + var checkbox = if(o.invertSharedOrder){ + jsx(''); + }else{ + jsx(''); + } + + jsx('
+ + $checkbox +
'); + }else{ + null; + } + + return jsx('
+
+ <$Product productInfo=${o.product} /> +
+ +
+ ${o.productRef} +
+ +
+ ${round(o.quantity * o.productPrice)} ${currency} +
+ +
+ $input + ${makeInfos()} +
+ + ${paidInput()} + +
$alternated
+ +
'); + } + + function round(f){ + return Formatting.formatNum(f); + } + + function paidInput(){ + if(hasPayments) return null; + if(state.order.paid){ + return jsx('
'); + }else{ + return jsx('
'); + } + } + + function makeInfos(){ + var o = state.order; + return if ( isSmartQtInput(o) ){ + jsx('
+ ${round(o.quantity)} x ${o.productQt} ${Formatting.unit(o.productUnit)} ${o.productName} +
'); + }else{ + null; + } + } + + function isSmartQtInput(o:UserOrder):Bool{ + return o.product.hasFloatQt || o.product.variablePrice || o.product.wholesale; + } + + function onChange(e:js.html.Event){ + e.preventDefault(); + var value :String = untyped (e.target.value == "") ? "0" : e.target.value; + state.inputValue = value; + var v = Formatting.parseFloat(value); + var o = state.order; + if ( isSmartQtInput(o) ){ + //the value is a smart qt, so we need re-compute the quantity + o.quantity = v / o.productQt; + }else{ + o.quantity = v; + } + + this.setState(state); + if (props.onUpdate != null) props.onUpdate(state.order); + } + + function onChangePaid(e:js.html.Event){ + state.order.paid = untyped e.target.checked; + this.setState(state); + if (props.onUpdate != null) props.onUpdate(state.order); + } + + function onChangeInvert(e:js.html.Event){ + state.order.invertSharedOrder = untyped e.target.checked; + this.setState(state); + if (props.onUpdate != null) props.onUpdate(state.order); + } + + function onChangeUser2(e:js.html.Event){ + var v = Std.parseInt(untyped e.target.value); + state.order.userId2 = v==0 ? null : v; + this.setState(state); + if (props.onUpdate != null) props.onUpdate(state.order); + } + + function onKeyPress(event:js.html.KeyboardEvent){ + /*if(event.key == 'Enter'){ + trace('enter !'); + }*/ + } +} \ No newline at end of file diff --git a/js/react/order/OrderBox.hx b/js/react/order/OrderBox.hx new file mode 100644 index 0000000..3f10ee5 --- /dev/null +++ b/js/react/order/OrderBox.hx @@ -0,0 +1,224 @@ +package react.order; +import react.ReactDOM; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; +import utils.HttpUtil; +import react.router.HashRouter; +import react.router.Route; +import react.router.Switch; +import react.router.Link; + +typedef OrderBoxState = { + orders:Array, + error:String, + users:Null>, +}; +typedef OrderBoxProps = { + userId:Int, + distributionId:Int, + contractId:Int, + contractType:Int, + date:String, + place:String, + userName:String, + onValidate:Void->Void, + currency:String, + hasPayments:Bool +}; + +/** + * A box to edit/add orders of a member + * @author fbarbut + */ +class OrderBox extends react.ReactComponentOfPropsAndState +{ + + public function new(props) + { + super(props); + state = { orders : [], error : null, users:null }; + } + + override function componentDidMount() + { + + //request api avec user + distrib + HttpUtil.fetch("/api/order/get/"+props.userId, GET, {distributionId:props.distributionId,contractId:props.contractId}, PLAIN_TEXT) + .then(function(data:String) { + + var data : {orders:Array} = tink.Json.parse(data); + /*for( o in orders){ + //convert ints to enums, enums have been lost in json serialization + o.productUnit = Type.createEnumIndex(Unit, cast o.productUnit ); + }*/ + setState({orders:data.orders, error:null}); + + if(props.contractType==0) loadUsers(); + + }).catchError(function(data) { + var data = Std.string(data); + trace("Error",data); + if(data.substr(0,1)=="{"){ + //json error from server + var data : ErrorInfos = haxe.Json.parse(data); + setState( cast {error:data.error.message} ); + }else{ + //js error + setState( cast {error:data} ); + } + }); + } + + /** + * load user list when contract is constant orders + */ + function loadUsers(){ + HttpUtil.fetch("/api/user/getFromGroup/", GET, {}, PLAIN_TEXT) + .then(function(data:String) { + + var data : {users:Array} = tink.Json.parse(data); + setState({users:data.users, error:null}); + + }).catchError(function(data) { + + var data = Std.string(data); + if(data.substr(0,1)=="{"){ + //json error from server + var data : ErrorInfos = haxe.Json.parse(data); + setState( cast {error:data.error.message} ); + }else{ + //js error + setState( cast {error:data} ); + } + }); + } + + override public function render(){ + + //edit orders + + + var renderOrders = this.state.orders.map(function(o){ + var k :String = if(o.id!=null) { + Std.string(o.id); + } else { + o.productId+"-"+Std.random(99999); + }; + return jsx('<$Order key="$k" order="$o" onUpdate=$onUpdate parentBox=${this} />') ; + } ); + + + var delivery = if(props.date==null){ + null; + }else{ + jsx('

Pour la livraison du ${props.date} à ${props.place}

'); + } + + var renderOrderBox = function() return jsx(' +
+

Commandes de ${props.userName}

+ $delivery + <$Error error="${state.error}" /> +
+
+
Produit
+
Ref.
+
Prix
+
Qté
+
Payé
+
Alterné avec
+
+ ${renderOrders} +
+ + Valider + +   + <$Link className="btn btn-default" to="/insert"> Ajouter un produit +
+
+ '); + + + var onProductSelected = function(uo:UserOrder){ + + var existingOrder = Lambda.find(state.orders,function(x) return x.productId==uo.productId ); + if(existingOrder!=null){ + existingOrder.quantity += uo.quantity; + this.setState(this.state); + }else{ + this.state.orders.push(uo); + this.setState(this.state); + } + + }; + + + //insert product box + var renderInsertBox = function(){ + return jsx('<$InsertOrder contractId="${props.contractId}" userId="${props.userId}" distributionId="${props.distributionId}" onInsert=$onProductSelected/>'); + } + + return jsx('<$HashRouter> + <$Switch> + <$Route path="/" exact=$true render=$renderOrderBox /> + <$Route path="/insert" exact=$true render=$renderInsertBox /> + + '); + + } + + /** + * called when an order is updated + */ + function onUpdate(data:UserOrder){ + /*trace("ON UPDATE : " + data); + for ( o in state.orders){ + if (o.id == data.id) { + o.quantity = data.quantity; + o.paid = data.paid; + break; + } + } + setState(this.state);*/ + } + + /** + * submit updated orders to the API + */ + function onClick(?_){ + + var data = new Array<{id:Int,productId:Int,qt:Float,paid:Bool,invertSharedOrder:Bool,userId2:Int}>(); + for ( o in state.orders) data.push({id:o.id, productId : o.productId, qt: o.quantity, paid : o.paid, invertSharedOrder:o.invertSharedOrder, userId2:o.userId2}); + + var req = { orders:data }; + + var p = HttpUtil.fetch("/api/order/update/"+props.userId+"?distributionId="+props.distributionId+"&contractId="+props.contractId, POST, req, JSON); + p.then(function(data:Dynamic) { + + //WOOT + if (props.onValidate != null) props.onValidate(); + + }).catchError(function(data) { + var data = Std.string(data); + trace("Error",data); + if(data.substr(0,1)=="{"){ + //json error from server + var data : ErrorInfos = haxe.Json.parse(data); + setState( cast {error:data.error.message} ); + }else{ + //js error + setState( cast {error:data} ); + } + }); + + } + + function onKeyPress(e:js.html.KeyboardEvent){ + if(e.key=="Enter") onClick(); + } + + + +} \ No newline at end of file diff --git a/js/react/product/ComposerApp.hx b/js/react/product/ComposerApp.hx new file mode 100644 index 0000000..376b4db --- /dev/null +++ b/js/react/product/ComposerApp.hx @@ -0,0 +1,104 @@ +package react; + +import api.react.ReactMacro.jsx; +import js.html.InputElement; +import Common; + +typedef ComposerAppState = { + products:Array<{id:Int,name:String,qt:Float,unit:UnitType}> +} + +typedef ComposerAppRefs = { + pi:ProductInput, + productContainer:js.html.DivElement, + qt:InputElement, + unit:js.html.SelectElement, +} + +/** + * Composite product composer + * + */ +class ComposerApp extends ReactComponentOfStateAndRefs +{ + /*var products:Array<{id:Int,name:String,?qt:Float,?unit:UnitType}>; + + + public function new(props:Dynamic) + { + + super(props); + products = [{id:1,name:"pipo"},{id:2,name:"Loclac"}]; + } + + override public function render(){ + return jsx(' +
+ +
+ + + + + + + + + Ajouter + + + +
+ +
+ ${createChildren()} +
+ +
+ '); + + } + + function onChange(){ + + } + + function getUnits(){ + var out = []; + for ( c in Unit.createAll()){ + + out.push(jsx( '')); + + } + + return out; + } + + function createChildren() + { + + + return [for (p in products) jsx('')]; + } + + function addItem(){ + var text :String = refs.pi.refs.input.value; + if (text.length > 0) + { + trace("add " + text); + trace("qt " + this.refs.qt.value); + trace("unit " + this.refs.unit.selectedIndex); + + var qt = Std.parseFloat(this.refs.qt.value); + var unit = UnitType.createByIndex(this.refs.unit.selectedIndex); + var id = Std.random(999); + products.push( {id:id, name:text,qt:qt,unit:unit}); + + setState({products:[{id:id,name:text, qt:qt, unit:unit}]}); + //this.forceUpdate(); + } + }*/ + +} \ No newline at end of file diff --git a/js/react/product/Product.hx b/js/react/product/Product.hx new file mode 100644 index 0000000..8f62937 --- /dev/null +++ b/js/react/product/Product.hx @@ -0,0 +1,39 @@ +package react.product; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +/** + * A Product + * @author fbarbut + */ +class Product extends react.ReactComponentOfProps<{productInfo:ProductInfo}> +{ + + public function new(props) + { + super(props); + } + + override public function render(){ + var p :ProductInfo = props.productInfo; + + //convert int to enum + //p.unitType = Type.createEnumIndex(Common.Unit,cast p.unit); + + //var unit = ; + var imgStyle = {width:'64px',height:'64px','backgroundImage':'url("${p.image}")'}; + var divStyle = p.active ? {} : {opacity: 0.4}; + + return jsx('
+
+
+
+
+ ${p.name} ${p.qt} ${Formatting.unit(p.unitType)}
+ ${p.price} € +
+
'); + } + +} \ No newline at end of file diff --git a/js/react/product/ProductInput.hx b/js/react/product/ProductInput.hx new file mode 100644 index 0000000..ca8919e --- /dev/null +++ b/js/react/product/ProductInput.hx @@ -0,0 +1,168 @@ +package react.product; +import react.ReactDOM; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; +import react.Typeahead; + +typedef ProductInputProps = { + formName:String, + txpProductId:Int, + productName:String, +} +typedef ProductInputState = { + txpProductId:Int, + productName:String, + categoryId:Int, + breadcrumb:String, +} + + +/** + * Product Text Input with autocompletion + * + * @author fbarbut + */ +class ProductInput extends react.ReactComponentOfPropsAndState +{ + + public static var DICO : TxpDictionnary = null; + var options : Array<{id:Int,label:String}>; + + public function new(props:ProductInputProps) + { + super(props); + options = []; + this.state = { + txpProductId : props.txpProductId, + productName : props.productName, + categoryId : 0, + breadcrumb : "" + }; + + } + + override public function render(){ + var inputName :String = props.formName+"_name"; + var txpProductInputName :String = props.formName+"_txpProductId"; + + return jsx(' +
+ +
+ +
${state.breadcrumb}
+ + + +
+ +
+ +
+ +
+ '); + } + + /** + * Called when typing is stopped + * @param o + */ + function onSearch(o){ + //trace("on search : "+o); + } + + /** + * Each time a single letter change in the input + * @param input + */ + function onInputChange(input:String){ + trace('on input change $input'); + this.setState({productName:input}); + } + + /** + * Called when an item is selected in suggestions + */ + function onChange(selection:Array<{label:String,id:Int}>){ + + if (selection == null || selection.length == 0) return; + + trace("on change "+selection[0]); + + var product = Lambda.find(DICO.products, function(x) return x.id == selection[0].id); + setTaxo(product); + this.setState({productName:selection[0].label}); + } + + /** + * init typeahead auto-completion features when component is mounted + */ + override function componentDidMount(){ + + //get dictionnary + if (DICO == null){ + + var r = new haxe.Http("/product/getTaxo"); + r.onData = function(data){ + //load dico + DICO = haxe.Unserializer.run(data); + + for ( p in DICO.products){ + options.push({label:p.name,id:p.id}); + } + + //default values of input + if (props.txpProductId != null){ + var txp = Lambda.find(DICO.products, function(x) return x.id == props.txpProductId); + setTaxo(txp); + } + }; + r.request(); + } + } + + function setTaxo(txp:{id:Int, name:String, category:Int, subCategory:Int}){ + + if (txp == null) return; + + //trace(txp); + + this.setState({ + categoryId:txp.category, + txpProductId:txp.id, + breadcrumb:getBreadcrumb(txp)/*, + productName:product.name //do not override product name ! */ + }); + + this.refs.image.src="/img/taxo/cat"+txp.category+".png"; + } + + /** + * generate string like "fruits & vegetables / vegetables / carrots" + * @param name + */ + function getBreadcrumb(product){ + //cat + var str = DICO.categories.get(product.category).name; + if (product.subCategory != null){ + str += " / " + DICO.subCategories.get(product.subCategory).name; + } + str += " / " + product.name; + return str; + } + + + +} \ No newline at end of file diff --git a/js/react/product/ProductSelect.hx b/js/react/product/ProductSelect.hx new file mode 100644 index 0000000..98b3dc6 --- /dev/null +++ b/js/react/product/ProductSelect.hx @@ -0,0 +1,41 @@ +package react.product; +import react.ReactDOM; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; +import utils.HttpUtil; + +/** + * A Product selector + * @author fbarbut + */ +class ProductSelect extends react.ReactComponentOfPropsAndState<{onSelect:ProductInfo->Void,products:Array},{selected:Int}> +{ + + public function new(props) + { + super(props); + state = { selected : null }; + } + + override public function render(){ + + var products = props.products.map(function(info){ + //var selector = info.id==state.selected ? jsx(''):jsx('
<$Product productInfo=$info />
'); + return jsx('
+
<$Product productInfo=$info />
+
'); + }); + + return jsx('
${products}
'); + } + + function onClick(i:Int){ + this.setState(cast {selected:i}); + if(props.onSelect!=null){ + var p = Lambda.find(props.products,function(x) return x.id==i); + props.onSelect(p); + } + } + +} \ No newline at end of file diff --git a/js/react/store/Cart.hx b/js/react/store/Cart.hx new file mode 100644 index 0000000..eef8958 --- /dev/null +++ b/js/react/store/Cart.hx @@ -0,0 +1,103 @@ +package react.store; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +typedef CartProps = { + var order:OrderSimple; + var addToCart:ProductInfo -> Int -> Void; + var removeFromCart:ProductInfo -> ?Int -> Void; + var submitOrder:OrderSimple -> Void; +}; + +class Cart extends react.ReactComponentOfProps +{ + + function addToCart(product:ProductInfo, quantity:Int):Void { + props.addToCart(product, quantity); + } + + function removeFromCart(product:ProductInfo, quantity:Int):Void { + props.removeFromCart(product, quantity); + } + + function removeAllFromCart(product:ProductInfo):Void { + props.removeFromCart(product); + } + + function submitOrder():Void { + props.submitOrder(props.order); + } + + override public function render(){ + return jsx(' +
+

Ma Commande

+ ${renderProducts()} + ${renderFooter()} +
+ '); + } + + function renderProducts() { + var productsToOrder = props.order.products.map(function(product:ProductWithQuantity) { + var quantity = product.quantity; + var product = product.product; + + return jsx(' +
+
${product.name}
+
$quantity
+
+
+ + +
+
+ - +
+
+ x +
+
+
+ '); + }); + + return jsx(' +
+ ${productsToOrder} +
+ '); + } + + function renderFooter() { + var buttonClasses = ["order-button"]; + var submit = submitOrder; + + if (props.order.products.length == 0) { + buttonClasses.push("order-button--disabled"); + submit = null; + } + + return jsx(' +
+
+ Total +
${props.order.total} €
+
+
Commander
+
+ '); + } +} + + + + + diff --git a/js/react/store/Filters.hx b/js/react/store/Filters.hx new file mode 100644 index 0000000..63e7e43 --- /dev/null +++ b/js/react/store/Filters.hx @@ -0,0 +1,45 @@ +package react.store; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +using Lambda; + +typedef FiltersProps = { + var categories:Array; + var filters:Array; + var toggleFilter:String -> Void; +}; + +class Filters extends react.ReactComponentOfProps +{ + override public function render(){ + return jsx(' +
+

Filtres

+ ${renderFilters()} +
+ '); + } + + function renderFilters() { + return props.categories.map(function(category) { + var classNames = ["filter"]; + if (props.filters.has(category.name)) + classNames.push("active"); + + return jsx(' +
+ ${category.name} +
+ '); + }); + } +} + diff --git a/js/react/store/Product.hx b/js/react/store/Product.hx new file mode 100644 index 0000000..8cc4158 --- /dev/null +++ b/js/react/store/Product.hx @@ -0,0 +1,65 @@ +package react.store; + +import js.Browser.window; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +typedef ProductProps = { + var product:ProductInfo; + var addToCart:ProductInfo -> Int -> Void; +}; + +typedef ProductState = { + var quantity:Int; +}; + +class Product extends react.ReactComponentOfPropsAndState +{ + static inline var OVERLAY_URL = '/shop/productInfo'; + static inline var IMAGE_WIDTH = 120; + static inline var IMAGE_HEIGHT = 120; + + public function new() { + super(); + state = { + quantity: 1 + }; + } + + function openOverlay() { + untyped window._.overlay('$OVERLAY_URL/${props.product.id}', props.product.name); + } + + function updateQuantity(event:Dynamic) { + var quantity = Std.parseInt(event.target.value); + + if (Std.is(quantity, Int) && quantity > 0) + setState({ + quantity: Std.int(quantity) + }); + } + + function addToCart() { + props.addToCart(props.product, state.quantity); + } + + override public function render(){ + var product = props.product; + + return jsx(' +
+ {product.name} +
+ + ${product.name} + +
${product.price} €
+ +
Ajouter
+
+
+ '); + } +} + diff --git a/js/react/store/ProductList.hx b/js/react/store/ProductList.hx new file mode 100644 index 0000000..098bf93 --- /dev/null +++ b/js/react/store/ProductList.hx @@ -0,0 +1,73 @@ +package react.store; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +using Lambda; + +typedef ProductListProps = { + var categories:Array; + var productsBySubcategoryIdMap:Map>; + var filters:Array; + var addToCart:ProductInfo -> Int -> Void; +}; + +class ProductList extends react.ReactComponentOfProps +{ + override public function render(){ + return jsx(' +
+ ${renderCategories()} +
+ '); + } + + function renderCategories() { + return props.categories.map(function(category) { + if (!props.filters.has(category.name)) + return null; + + return jsx(' +
+

${category.name}

+
+ ${renderSubCategories(category)} +
+
+ '); + }); + } + + function renderSubCategories(category) { + var subCategories = category.subcategories.map(function(category) { + if (!props.productsBySubcategoryIdMap.exists(category.id)) + return jsx('
Loading...
'); + + var products = props.productsBySubcategoryIdMap.get(category.id); + + return jsx(' +
+

${category.name}

+
+ ${renderProducts(products)} +
+
+ '); + }); + + return jsx(' +
+ $subCategories +
+ '); + } + + function renderProducts(products) { + return products.map(function(product) { + return jsx(' + + '); + }); + } +} + diff --git a/js/react/store/Store.hx b/js/react/store/Store.hx new file mode 100644 index 0000000..e2a420c --- /dev/null +++ b/js/react/store/Store.hx @@ -0,0 +1,229 @@ +package react.store; +import react.ReactComponent; +import react.ReactMacro.jsx; +import haxe.Json; + +using Lambda; + +import Common; +import utils.CartUtils; +import utils.HttpUtil; + +typedef StoreProps = { + var place:Int; + var date:String; +}; + +typedef StoreState = { + var place:PlaceInfos; + var orderByEndDates:Array; + var categories:Array; + var productsBySubcategoryIdMap:Map>; + var order:OrderSimple; + var filters:Array; +}; + +class Store extends react.ReactComponentOfPropsAndState +{ + static inline var CATEGORY_URL = '/api/shop/categories'; + static inline var PRODUCT_URL = '/api/shop/products'; + static inline var INIT_URL = '/api/shop/init'; + static inline var VIEW_URL = '/place/view'; + + public function new() { + super(); + state = { + place: null, + orderByEndDates: [], + categories: [], + filters: [], + productsBySubcategoryIdMap: new Map(), + order: { + products: [], + total: 0 + } + }; + } + + override function componentDidMount() { + var categoriesRequest = HttpUtil.fetch(CATEGORY_URL, GET, {date: props.date, place: props.place}, JSON); + var initRequest = HttpUtil.fetch(INIT_URL, GET, {date: props.date, place: props.place}, JSON); + + initRequest.then(function(infos:Dynamic) { + setState({ + place: infos.place, + orderByEndDates: infos.orderEndDates + }); + }) + .catchError(function(error) { + trace("ERROR", error); + }); + + categoriesRequest.then(function(categories:Dynamic) { + var categories:Array = categories.categories; + var subCategories = []; + + for (category in categories) { + subCategories = subCategories.concat(category.subcategories); + } + + setState({ + categories: categories, + filters: categories.map(function(category) { + return category.name; + }) + }); + + subCategories.map(function(category:CategoryInfo) { + return HttpUtil.fetch(PRODUCT_URL, GET, {date: props.date, place: props.place, subcategory: category.id}, JSON) + .then(function(result) { + var productsBySubcategoryIdMapCopy = [ + for (key in state.productsBySubcategoryIdMap.keys()) + key => state.productsBySubcategoryIdMap.get(key) + ]; + productsBySubcategoryIdMapCopy.set(category.id, result.products); + + setState({ + productsBySubcategoryIdMap: productsBySubcategoryIdMapCopy + }); + }); + }); + }) + .catchError(function(error) { + trace("ERROR", error); + }); + } + + function toggleFilter(category:String) { + var filters = state.filters.copy(); + + if (state.filters.find(function(categoryInFilter) { + return category == categoryInFilter; + }) != null) + filters.remove(category); + else + filters.push(category); + + if (filters.length == 0) + filters = state.categories.map(function(category) { + return category.name; + }); + + setState({ + filters: filters + }); + } + + function addToCart(productToAdd:ProductInfo, quantity:Int):Void { + setState({ + order: CartUtils.addToCart(state.order, productToAdd, quantity) + }); + } + + function removeFromCart(productToRemove:ProductInfo, ?quantity:Int):Void { + setState({ + order: CartUtils.removeFromCart(state.order, productToRemove, quantity) + }); + } + + function submitOrder(order:OrderSimple) { + var orderInSession = { + total: order.total, + products: order.products.map(function(p:ProductWithQuantity){ + return { + productId: p.product.id, + quantity: p.quantity + }; + }) + } + trace('Order', orderInSession); + } + + override public function render(){ + return jsx(' +
+ ${renderHeader()} + + + +
+ '); + } + + function renderHeader() { + if (state.orderByEndDates == null || state.orderByEndDates.length == 0) + return null; + + var endDates; + + if (state.orderByEndDates.length == 1) { + var orderEndDate = state.orderByEndDates[0].date; + endDates = [jsx('
La commande fermera le $orderEndDate
')]; + } + else { + endDates = state.orderByEndDates.map(function(order) { + if (order.contracts.length == 1) + return jsx(' +
+ La commande ${order.contracts[0]} fermera le: ${order.date} +
+ '); + + return jsx(' +
+ Les autres commandes fermeront: ${order.date} +
+ '); + }); + } + + var viewUrl = '$VIEW_URL/${props.place}'; + var addressBlock = Lambda.array([ + state.place.address1, + state.place.address2, + [state.place.zipCode, state.place.city].join(" "), + ].mapi(function(index, element) { + if (element == null) + return null; + return jsx('
$element
'); + })); + + return jsx(' +
+
+
+ Distribution le ${props.date} +
+ +
+ $endDates +
+
+
+ + + ${state.place.name} + +
+ $addressBlock +
+
+
+ '); + } +} + diff --git a/js/react/user/LoginBox.hx b/js/react/user/LoginBox.hx new file mode 100644 index 0000000..0db3a9f --- /dev/null +++ b/js/react/user/LoginBox.hx @@ -0,0 +1,139 @@ +package react.user; +import react.ReactComponent; +import react.ReactMacro.jsx; +import Common; + +typedef LoginBoxProps = { + redirectUrl:String, + message:String, + ?phoneRequired:Bool +} + +typedef LoginBoxState = { + email:String, + password:String, + error:String +} + +/** + * Login Box + * @author fbarbut + */ +class LoginBox extends react.ReactComponentOfPropsAndState +{ + + public function new(props:LoginBoxProps) + { + if (props.redirectUrl == null) props.redirectUrl = "/"; + if (props.message == "") props.message = null; + super(props); + this.state = {email:"", password:"", error:null}; + } + + function setError(err:String){ + this.setState(cast {error:err}); + } + + override public function render(){ + + return jsx('
+ <$Error error="${state.error}" /> + <$Message message="${props.message}" /> +
+
+ +
+ +
+
+
+ +
+ +
+
+

+ S\'identifier +
+
+ Mot de passe oublié ? +

+
+ +
'); + } + + /** + * @doc https://facebook.github.io/react/docs/forms.html + */ + function onChange(e:js.html.Event){ + e.preventDefault(); + + var name :String = untyped e.target.name; + var value :String = untyped /*(e.target.value == "") ? null :*/ e.target.value; + Reflect.setField(state, name, value); + this.setState(this.state); + } + + /** + * displays a registerBox + */ + public function registerBox(){ + + var body = js.Browser.document.querySelector('#myModal .modal-body'); + ReactDOM.unmountComponentAtNode( body ); + + js.Browser.document.querySelector("#myModal .modal-title").innerHTML = "Inscription"; + ReactDOM.render(jsx('<$RegisterBox redirectUrl="${props.redirectUrl}" phoneRequired="${props.phoneRequired}"/>'), body ); + } + + public function submit(?e:js.html.Event){ + + if (state.email == ""){ + setError("Veuillez saisir votre email"); + return; + } + if (state.password == ""){ + setError("Veuillez saisir votre mot de passe"); + return; + } + + //lock button + var el: js.html.Element = null; + if(e!=null){ + el = cast e.target; + el.classList.add("disabled"); + } + + + var req = new haxe.Http("/api/user/login"); + req.addParameter("email", state.email); + req.addParameter("password", state.password); + req.addParameter("redirecturl", props.redirectUrl); + + req.onData = req.onError = function(d){ + + var d = req.responseData; + + if(e!=null) el.classList.remove("disabled"); + + var d = haxe.Json.parse(d); + if (Reflect.hasField(d, "error")) setError(d.error.message); + if (Reflect.hasField(d, "success")) js.Browser.window.location.href = props.redirectUrl; + } + req.request(true); + } + + function onKeyPress(e:js.html.KeyboardEvent){ + if(e.key=="Enter") submit(); + } + + + +} \ No newline at end of file diff --git a/js/react/user/RegisterBox.hx b/js/react/user/RegisterBox.hx new file mode 100644 index 0000000..1be1a4e --- /dev/null +++ b/js/react/user/RegisterBox.hx @@ -0,0 +1,165 @@ +package react.user; +import react.ReactDOM; +import react.ReactComponent; +import react.ReactMacro.jsx; + +typedef RegisterBoxState = {firstName:String, lastName:String, email:String, password:String, error:String, phone:String}; +typedef RegisterBoxProps = {redirectUrl:String,message:String,phoneRequired:Bool}; + + +/** + * Registration box ( sign up ) + * @author fbarbut + */ +class RegisterBox extends react.ReactComponentOfPropsAndState +{ + + + public function new(props:RegisterBoxProps) + { + if (props.redirectUrl == null) props.redirectUrl = "/"; + super(props); + this.state = {firstName:"",lastName:"",email:"",password:"",error:null,phone:""}; + } + + + override public function render(){ + + //tips for conditionnal rendering : https://github.com/massiveinteractive/haxe-react#gotchas + var phone = null; + if (props.phoneRequired){ + phone = jsx('
+ +
+ +
+
'); + } + + return jsx(' +
+ <$Error error="${state.error}" /> + <$Message message="${props.message}" /> +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + ${phone} + +
+ +
+ +
+
+

+ Inscription +

+
+
+

+ Déjà inscrit ? + Connectez-vous ici +

+
+ + '); + } + + /** + * @doc https://facebook.github.io/react/docs/forms.html + */ + function onChange(e:js.html.Event){ + + e.preventDefault(); + var name :String = untyped e.target.name; + var value :String = untyped e.target.value; + //trace('onChange : $name = $value'); + Reflect.setField(state, name, value); + this.setState(this.state); + } + + /** + * displays a login box + */ + public function loginBox(){ + + var body = js.Browser.document.querySelector('#myModal .modal-body'); + ReactDOM.unmountComponentAtNode( body ); + + js.Browser.document.querySelector("#myModal .modal-title").innerHTML = "Connexion"; + ReactDOM.render(jsx('<$LoginBox redirectUrl="${props.redirectUrl}" />'), body ); + } + + + public function submit(e:js.html.Event ){ + + if (state.email == ""){ + setError("Veuillez saisir votre email"); + return; + } + + if (state.password == ""){ + setError("Veuillez saisir un mot de passe"); + return; + } + + if (state.firstName == ""){ + setError("Veuillez saisir votre prénom"); + return; + } + + if (state.lastName == ""){ + setError("Veuillez saisir votre nom de famille"); + return; + } + + if (state.phone == "" && props.phoneRequired){ + setError("Veuillez saisir votre numéro de téléphone"); + return; + } + + //lock button + var el : js.html.Element = cast e.target; + el.classList.add("disabled"); + + var req = new haxe.Http("/api/user/register"); + req.addParameter("firstName", state.firstName); + req.addParameter("lastName", state.lastName); + req.addParameter("email", state.email); + req.addParameter("password", state.password); + req.addParameter("redirecturl", props.redirectUrl); + if(props.phoneRequired) req.addParameter("phone", state.phone); + + req.onData = req.onError = function(d){ + var d = req.responseData; + el.classList.remove("disabled"); + var d = haxe.Json.parse(d); + if (Reflect.hasField(d, "error")) setError(d.error.message); + if (Reflect.hasField(d, "success")) js.Browser.window.location.href = props.redirectUrl; + } + + req.request(true); + } + + function setError(err:String){ + this.setState(cast {error:err}); + } + +} \ No newline at end of file diff --git a/js/test/TestAll.hx b/js/test/TestAll.hx new file mode 100644 index 0000000..c7cab2a --- /dev/null +++ b/js/test/TestAll.hx @@ -0,0 +1,20 @@ +package test; + +import js.Browser; +import utest.Runner; +import utest.ui.Report; +import test.utils.TestCartUtils; + +class TestAll +{ + public static function main() + { + var runner = new Runner(); + + // Utils + runner.addCase(new TestCartUtils()); + + Report.create(runner); + runner.run(); + } +} diff --git a/js/test/mocks.json b/js/test/mocks.json new file mode 100644 index 0000000..5aac6b5 --- /dev/null +++ b/js/test/mocks.json @@ -0,0 +1,44 @@ +[ + { + "contractTaxName": null, + "desc": "Des grattons fait avec du cochon gascon, élevé en plein air et selon les préceptes de l'agriculture biologique. Meilleur que des rillettes!", + "vatValue": 0.234597156398104, + "price": 4.5, + "name": "Gratton de porc noir", + "type": 0, + "contractTax": null, + "contractId": 4672, + "orderable": true, + "hasFloatQt": false, + "id": 56610, + "qt": 190, + "unitType": 2, + "ref": "GRA-1", + "vat": 5.5, + "categories": null, + "stock": null, + "organic": false, + "image": "/img/taxo/cat10.png" + }, + { + "contractTaxName": null, + "desc": "Un pâté savoureux qui se mange sans modération! Date de péremption : 1 Avril 2019 Lot : L16AV01P Poids : 200gr Ingrédients : Viande de porc issue de l’agriculture biologique, Sel, Poivre", + "vatValue": 0.297156398104265, + "price": 5.7, + "name": "Pâté de porc noir", + "type": 0, + "contractTax": null, + "contractId": 4672, + "orderable": true, + "hasFloatQt": false, + "id": 56611, + "qt": 1, + "unitType": 0, + "ref": "PAPN-1", + "vat": 5.5, + "categories": null, + "stock": null, + "organic": false, + "image": "/file/2521_6a77c954191e5a2d8cd5a5d8cade4f6b.PNG" + } +] diff --git a/js/test/utils/TestCartUtils.hx b/js/test/utils/TestCartUtils.hx new file mode 100644 index 0000000..08f62e3 --- /dev/null +++ b/js/test/utils/TestCartUtils.hx @@ -0,0 +1,58 @@ +package test.utils; + +import sys.io.File.getContent; +import haxe.Json.parse; +import utest.Assert; +import utils.CartUtils; +import Common; + +class TestCartUtils +{ + static var products:Array; + public function new() { + var http = new haxe.Http("localhost/js/test/mocks.json?format=json"); + + products = parse(getContent("js/test/mocks.json")); + } + + public function testAddToCart() + { + var order = { + products: [{ + product: products[0], + quantity: 1 + }], + total: products[0].price + }; + + var newProduct = products[1]; + + order = CartUtils.addToCart(order, newProduct, 1); + Assert.equals(order.products.length, 2); + Assert.equals(order.total, products[0].price + products[1].price); + order = CartUtils.addToCart(order, newProduct, 3); + Assert.equals(order.products[1].quantity, 4); + Assert.equals(order.total, products[0].price + 4 * products[1].price); + } + + public function testRemoveFromCart() + { + var order = { + products: [{ + product: products[0], + quantity: 5 + }, { + product: products[1], + quantity: 3 + }], + total: 5 * products[0].price + 3 * products[1].price + }; + + order = CartUtils.removeFromCart(order, products[0], 2); + Assert.equals(order.products[0].quantity, 3); + Assert.equals(order.total, 3 * products[0].price + 3 * products[1].price); + order = CartUtils.removeFromCart(order, products[1]); + Assert.equals(order.products.length, 1); + Assert.equals(order.total, 3 * products[0].price); + } +} diff --git a/js/utils/CartUtils.hx b/js/utils/CartUtils.hx new file mode 100644 index 0000000..71f744d --- /dev/null +++ b/js/utils/CartUtils.hx @@ -0,0 +1,64 @@ +package utils; + +import Math; +using Lambda; + +import Common; + +class CartUtils { + public static function addToCart(order:OrderSimple, productToAdd:ProductInfo, quantity:Int):OrderSimple { + var products = order.products.copy(); + var total = order.total; + + var existingProduct = products.find(function(p) { + return p.product.id == productToAdd.id; + }); + + if (existingProduct == null) + products.push({ + product: productToAdd, + quantity: quantity + }); + else + existingProduct.quantity += quantity; + + total += quantity * productToAdd.price; + total = Math.round(total * 100) / 100; // to avoid calculation errors + + return { + products: products, + total: total + }; + } + + public static function removeFromCart(order:OrderSimple, productToRemove:ProductInfo, ?quantity:Int):OrderSimple { + var products = order.products.copy(); + var total = order.total; + + var existingProduct = products.find(function(p) { + return p.product.id == productToRemove.id; + }); + + if (quantity == null) + quantity = existingProduct.quantity; + + if (existingProduct == null) + throw "Can't remove a non existing product"; + else if (quantity >= existingProduct.quantity) + products.remove(existingProduct) + else + existingProduct.quantity -= quantity; + + if (products.length == 0) + total = 0; + else { + total -= Math.min(quantity, existingProduct.quantity) * productToRemove.price; + total = Math.round(total * 100) / 100; // to avoid calculation errors + } + + return { + products: products, + total: total + }; + } +} diff --git a/js/utils/HttpUtil.hx b/js/utils/HttpUtil.hx new file mode 100644 index 0000000..3851fdb --- /dev/null +++ b/js/utils/HttpUtil.hx @@ -0,0 +1,115 @@ +package utils; + +import haxe.Json; +import js.Promise; +import js.html.XMLHttpRequest; + +@:enum abstract HttpMethod(String) to String { + var POST = 'POST'; + var GET = 'GET'; + var HEAD = 'HEAD'; + var PUT = 'PUT'; + var DELETE = 'DELETE'; + var TRACE = 'TRACE'; + var OPTIONS = 'OPTIONS'; + var CONNECT = 'CONNECT'; + var PATCH = 'GET'; +} + +@:enum abstract FetchFormat(String) from String to String { + var PLAIN_TEXT = "text/plain"; + var JSON = "application/json"; +} + +//json version of a tink.core.Error +typedef ErrorInfos = {error:{code:Int,message:String,stack:String}} + +/** + * Manage HTTP request to a REST API. + * + * POST requests can only have a single JSON object (payload) + */ +class HttpUtil +{ + static public function fetch( + url: String, + ?method: HttpMethod = GET, + ?params: Dynamic = null, + ?accept: FetchFormat = PLAIN_TEXT, + ?contentType: String = JSON + ): Promise { + + return new Promise(function(resolve: Dynamic->Void, reject) { + var data: String = null; + if (params != null) + { + if (params.body != null){ + data = Json.stringify(params.body); + } else if(method==POST) { + data = Json.stringify(params); + } else { + url += (url.indexOf('?') > -1) ? '&' : '?'; + url += objToString(params); + } + } + + var http = new XMLHttpRequest(); + http.open(method, url, true); + + if (contentType != null && contentType.length > 0) + http.setRequestHeader("Content-type", contentType); + + if (accept != null) + http.setRequestHeader("Accept", accept); + + http.onreadystatechange = function() { + //trace("readystate",http.readyState, http.status); + if (http.readyState == 4){ + switch (http.status){ + case 200: + switch (accept){ + case JSON: + try { + var json = Json.parse(http.responseText); + resolve(json); + } catch (err: Dynamic){ + reject(err); + } + default: + resolve(http.responseText); + } + + case 204: + resolve(true); + + default: + reject(http.responseText); + } + } + }; + + http.send(data); + }); + } + + static public function objToString(obj: Dynamic): String + { + var str = ""; + var cpt = 0; + for (key in Reflect.fields(obj)) + { + var value: Dynamic = Reflect.field(obj, key); + if (value == null) continue; + + if (cpt++ > 0) + str += "&"; + + if (Std.is(value, Array) && value.length > 0) + str += '$key=${value.join(";")}'; + else if (Std.string(value) != "") // String / Int / Float + str += '$key=${StringTools.trim(Std.string(value))}'; + } + return str; + } + +} diff --git a/lang/de/tmp/README.md b/lang/de/tmp/README.md new file mode 100644 index 0000000..246660c --- /dev/null +++ b/lang/de/tmp/README.md @@ -0,0 +1 @@ +This is where the compiled templates are. \ No newline at end of file diff --git a/lang/en/tmp/README.md b/lang/en/tmp/README.md new file mode 100644 index 0000000..246660c --- /dev/null +++ b/lang/en/tmp/README.md @@ -0,0 +1 @@ +This is where the compiled templates are. \ No newline at end of file diff --git a/lang/master/tmp/README.md b/lang/master/tmp/README.md new file mode 100644 index 0000000..246660c --- /dev/null +++ b/lang/master/tmp/README.md @@ -0,0 +1 @@ +This is where the compiled templates are. \ No newline at end of file diff --git a/lang/master/tpl/account/payments.mtt b/lang/master/tpl/account/payments.mtt new file mode 100644 index 0000000..41674f9 --- /dev/null +++ b/lang/master/tpl/account/payments.mtt @@ -0,0 +1,105 @@ +::use 'design.mtt':: + +
+

Coordonnées

+
+ +
::member.firstName:: ::member.lastName:: + ::if member.firstName2!=null && member.lastName2!=null:: + & ::member.firstName2:: ::member.lastName2:: + + ::end::
+ + $$nullSafe(::member.address1::)
+ $$nullSafe(::member.address2::)
+ $$nullSafe(::member.zipCode::) + $$nullSafe(::member.city::)
+
+ + ::if member.phone!=null:: + Tel : ::member.phone::
+ ::end:: + + ::if member.email!=null:: + E-mail : ::member.email::
+ ::end:: + + ::if member.email2!=null:: + E-mail : ::member.email2::
+ ::end:: + + +
+ +
+ +
+

::_("Payments")::

+
+ + + + + + + ::if balance<0:: + ::set color = "C00":: + ::else:: + ::set color = "0C0":: + ::end:: + + + + + + ::foreach t rb.current():: + + $$operation(::t::) + + ::end:: + + + + + +
::_("Balance"):: : + ::if balance<0:: + - + ::end:: + + ::Math.abs(balance):: ::currency():: + +
+
    + ::if rb.paginationStartPage != 1:: +
  • + + + +
  • + ::end:: + ::foreach i loopList(rb.paginationStartPage,rb.paginationEndPage):: + ::if rb.page==i:: +
  • ::i::
  • + ::else:: +
  • ::i::
  • + ::end:: + ::end:: + ::if rb.paginationEndPage < rb.pages + 1:: +
  • + + + +
  • + ::end:: +
+
+ + +
+
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/admin/cssDemo.mtt b/lang/master/tpl/admin/cssDemo.mtt new file mode 100644 index 0000000..0496fac --- /dev/null +++ b/lang/master/tpl/admin/cssDemo.mtt @@ -0,0 +1,53 @@ +::use 'admin/design.mtt':: + +
+
+
+

H1 Title

+

H2 Title

+

H3 Title

+

H4 Title

+
+
+ +
+
+

+ Titre H2 +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Donec ullamcorper venenatis augue, vitae tempor quam viverra at. + Sed faucibus ipsum sit amet turpis consectetur finibus. Nulla facilisi. + Vivamus consequat vestibulum blandit. Nunc laoreet tellus ac lorem commodo hendrerit. +

+

+ Nulla gravida, libero et scelerisque convallis, nibh nisi ultrices nulla, sed faucibus sapien augue et turpis. + Nunc quis tempor dolor. Duis elementum nisl eu dolor malesuada convallis. + Sed id tellus euismod, eleifend neque ac, iaculis odio. Cras euismod odio quis elit elementum rhoncus. + Mauris in elementum ex, ut scelerisque leo. Suspendisse viverra mi non neque vulputate, in fermentum sapien sodales. + Sed in laoreet ante. Suspendisse non velit sit amet lectus consequat ornare sit amet quis ipsum. +

+ + +
+ Nulla gravida, libero et scelerisque convallis, nibh nisi ultrices nulla, sed faucibus sapien augue et turpis. + Nunc quis tempor dolor. Duis elementum nisl eu dolor malesuada convallis. +
+ Big Button + Big Button +
+ +
+
+
+ +
+
+
Panel heading without title
+
+ Panel content +
+
+
+::end:: \ No newline at end of file diff --git a/lang/master/tpl/admin/default.mtt b/lang/master/tpl/admin/default.mtt new file mode 100644 index 0000000..4be3e8d --- /dev/null +++ b/lang/master/tpl/admin/default.mtt @@ -0,0 +1,10 @@ +::use 'admin/design.mtt':: + + +

Console d'administration de Cagette.net

+

+ ::hDate(now):: +

+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/admin/design.mtt b/lang/master/tpl/admin/design.mtt new file mode 100644 index 0000000..39d894d --- /dev/null +++ b/lang/master/tpl/admin/design.mtt @@ -0,0 +1,36 @@ +::use 'design.mtt':: +
+ +
+ + + + +
+ + +
+ +
+
+ ::raw __content__:: +
+
+ + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/admin/emails.mtt b/lang/master/tpl/admin/emails.mtt new file mode 100644 index 0000000..1b30cc3 --- /dev/null +++ b/lang/master/tpl/admin/emails.mtt @@ -0,0 +1,50 @@ +::use 'admin/design.mtt':: +
+
+

Emails

+ +

::_("Emails are temporarily stored in a buffer, waiting to be sent by the cron")::

+

+

::num:: unsent emails

+

+ + ::set url = "/admin/emails":: + + $$browse(::browser::,::url::) + + + + + + + + + + + + + + ::if(e.tries==0):: + + ::else:: + + ::end:: + + + + + +
iddatetriestitlehtmlstatus
::e.id::::e.cdate::::e.tries::::e.tries::::e.title:: + ::if e.htmlBody != null:: + [show] +
+ ::raw e.htmlBody:: +
+ ::end:: +
::e.rawStatus::
+ + $$browse(::browser::,::url::) +
+
+::end:: + diff --git a/lang/master/tpl/admin/errors.mtt b/lang/master/tpl/admin/errors.mtt new file mode 100644 index 0000000..764b5b3 --- /dev/null +++ b/lang/master/tpl/admin/errors.mtt @@ -0,0 +1,100 @@ +::use 'admin/design.mtt':: +
+
+

Errors

+ +
+
+ User : +
+ + Error : +
+ + +
+
+ +
+ Date serveur : ::now::
+ + Vider la table + +
+ + + + + [No canvas support] + + + ::set url = "/admin/errors":: + + $$browse(::browser::,::url::) + + + + + + + + + + + + + + ::set e = r.error.split('Called'):: + + + + + +
iddateErrorinfosuser idURL
::r.id::::r.date:: + ::e.shift():: + ::if e.length > 0:: + [stack] +
+
Called::e.join("Called")::
+
+ ::end:: +
+ [infos] +
+ IP: ::r.ip::
+ User-Agent: ::r.userAgent:: +
+
+ + ::r._user.getName():: + + ::r.url::
+ $$browse(::browser::,::url::) +
+
+::end:: + diff --git a/lang/master/tpl/admin/graph.mtt b/lang/master/tpl/admin/graph.mtt new file mode 100644 index 0000000..3108f32 --- /dev/null +++ b/lang/master/tpl/admin/graph.mtt @@ -0,0 +1,56 @@ +::use 'admin/design.mtt':: + + +

::key::

+

+ Du ::hDate(from):: au ::hDate(to)::
+ Total : ::total::
+ Moyenne : ::averageValue:: + +

+

+ + + [No canvas support] + + +

+

+ + + ::foreach d data:: + + + + + ::end:: +
::hDate(d.date)::::d.value::
+ +

+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/admin/plugins/default.mtt b/lang/master/tpl/admin/plugins/default.mtt new file mode 100644 index 0000000..3f81e2d --- /dev/null +++ b/lang/master/tpl/admin/plugins/default.mtt @@ -0,0 +1,33 @@ +::use 'admin/design.mtt':: + +

Plugins

+

+ L'installation des plugins se fait sur la machine de développement. +

+ + + +::foreach p plugins:: + + + + + + + + + +::end:: + +
NomInstallé
::p.name:: + ::set i = p.isInstalled():: + $$bool(::i::) + + ::if i==false:: + Installer + ::end:: + + +
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/admin/taxo.mtt b/lang/master/tpl/admin/taxo.mtt new file mode 100644 index 0000000..e8146cf --- /dev/null +++ b/lang/master/tpl/admin/taxo.mtt @@ -0,0 +1,31 @@ +::use 'admin/design.mtt':: + +

Dictionnaire de produits

+ +

+ Cagette.net inclut maintenant un dictionnaire de produits qui permet d'identifier + facilement les produits que vous saisissez. +

+ +
+ + ::foreach c categ:: + + + +

#::c.id:: ::c.name::

+ ::foreach sc c.getSubCategories():: +

+ ::sc.name:: +
+ ::foreach p sc.getProducts():: + ::p.name::, + ::end:: + +

+ ::end:: + ::end:: + + +::end:: + diff --git a/lang/master/tpl/amap/default.mtt b/lang/master/tpl/amap/default.mtt new file mode 100644 index 0000000..bf83a83 --- /dev/null +++ b/lang/master/tpl/amap/default.mtt @@ -0,0 +1,78 @@ +::use 'design.mtt':: + + +
+ +
+ +

+ ::user._amap.name:: +

+ + ::if user._amap._image!=null:: + ::if user._amap.extUrl!=null && user._amap.extUrl!="":: +
+ ::else:: +
+ ::end:: + ::end:: + + ::raw nl2br(user.getAmap().txtIntro):: +
+ ::if user.isAmapManager():: + $$edit(::_("Edit")::,amap/edit) + ::end:: + +
+ +
+ ::_("Main contact"):: :
+ $$contact(::user.getAmap()._contact::) +
+ +
+ +
+
+ + ::foreach c contracts:: +
+
+
+
+
+

+ ::c._vendor.name::
+ ::c._vendor.city:: (::c._vendor.zipCode::) +

+
+ +
+

::c.name::

+ +
+ ::foreach p c.getProductsPreview(5):: +
+ ::end:: +
+ +
+ +
+ ::if c._contact!=null:: + ::_("Coordinator"):: : +
+ $$contact(::c._contact::) + ::end:: +
+ +
+
+
+ ::end:: + + +
+
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/amapadmin/addimage.mtt b/lang/master/tpl/amapadmin/addimage.mtt new file mode 100644 index 0000000..84c1800 --- /dev/null +++ b/lang/master/tpl/amapadmin/addimage.mtt @@ -0,0 +1,32 @@ +::use 'amapadmin/design.mtt':: + + +

::_("Choose a logo")::

+ + +
+ +
+
+ + +
+ ::_("Format : JPG, GIF or PNG")::
+
+ + +
+
+ +
+ +
+ + +
+ +
+ + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/amapadmin/default.mtt b/lang/master/tpl/amapadmin/default.mtt new file mode 100644 index 0000000..276cf06 --- /dev/null +++ b/lang/master/tpl/amapadmin/default.mtt @@ -0,0 +1,50 @@ +::use 'amapadmin/design.mtt':: + +

::user._amap.name::

+ +
+ +
+

+ ::membersNum:: ::_("members")::.
+ ::contractsNum:: ::_("contracts in progress")::.
+ +


+ + $$edit(::_("Edit properties")::,/amap/edit) + +  ::_("Choose a logo"):: +

+ +
+ +

+ ::raw _("Your group has a public page, You can use it as a micro-website.
Copy-paste the address of this page and share it !"):: +
+

+ + +

+ + http://::HOST::/group/::user._amap.id:: + +

+ +

+   ::_("Display"):: +

+ + +
+ +
+ + ::if user._amap._image!=null:: + + ::end:: +
+ +
+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/amapadmin/design.mtt b/lang/master/tpl/amapadmin/design.mtt new file mode 100644 index 0000000..db4da2b --- /dev/null +++ b/lang/master/tpl/amapadmin/design.mtt @@ -0,0 +1,39 @@ +::use 'design.mtt':: +
+ +
+ + + + +
+ +
+ + +
+
+ ::raw __content__:: +
+ + + + +
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/amapadmin/rights.mtt b/lang/master/tpl/amapadmin/rights.mtt new file mode 100644 index 0000000..f116f99 --- /dev/null +++ b/lang/master/tpl/amapadmin/rights.mtt @@ -0,0 +1,40 @@ +::use 'amapadmin/design.mtt':: + +

::_("Management of rights and access")::

+ + $$insert(::_("Add a right")::,/amapadmin/editRight) + + + + + + + + + ::foreach u users:: + + + + + + + + + + ::end:: +
::_("Name")::::_("Rights")::::_("Actions")::
+ ::u._user.getName():: + +
    + ::foreach r u._rights:: +
  • ::u.getRightName(r)::
  • + ::end:: +
+
+ $$insert(::_("Modify")::,/amapadmin/editRight/::u.userId::) +
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/base.mtt b/lang/master/tpl/base.mtt new file mode 100644 index 0000000..b0af0c9 --- /dev/null +++ b/lang/master/tpl/base.mtt @@ -0,0 +1,129 @@ + + + + + + + ::if pageTitle!=null:: + ::pageTitle:: + ::else:: + ::NAME:: + ::end:: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ::if container==null:: ::set container="container":: ::end:: + + + ::if DEBUG:: +
DEBUG ::session.lang::
+ ::end:: + + ::if HOST.substr(0,3)=="pp.":: +
PREPROD
+ ::end:: + + + + + ::raw __content__:: + + + + + diff --git a/lang/master/tpl/block/modal.mtt b/lang/master/tpl/block/modal.mtt new file mode 100644 index 0000000..de3cd10 --- /dev/null +++ b/lang/master/tpl/block/modal.mtt @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/lang/master/tpl/blocks/user.mtt_removeme b/lang/master/tpl/blocks/user.mtt_removeme new file mode 100644 index 0000000..ba3c5fe --- /dev/null +++ b/lang/master/tpl/blocks/user.mtt_removeme @@ -0,0 +1,12 @@ +
+ ::if user!=null:: + ::_("Hello"):: ::user.getName()::
+ + ::_("Log out"):: + ::else:: + + ::raw _("You already have an account?
+ Click here to log in")::
+ ::_("Log in"):: + ::end:: +
\ No newline at end of file diff --git a/lang/master/tpl/categories/default.mtt b/lang/master/tpl/categories/default.mtt new file mode 100644 index 0000000..d732963 --- /dev/null +++ b/lang/master/tpl/categories/default.mtt @@ -0,0 +1,48 @@ +::use 'amapadmin/design.mtt':: + +

::_("Categories")::

+ +

+ ::raw _("Categories allow to sort your products per category, for example \"vegetables, meet, backery, etc.\"
It is possible to create many groups of categories in order to sort products as we wish."):: +

+

+ ::_("You currently have no category. Would you like to create the list of categories by default ?"):: + ::_("Create the list of default categories"):: +

+ +::foreach g groups:: + + + + + + + ::foreach c g.getCategories():: + + + + + + ::end:: + + + + + +
+ Groupe "::g.name::" + + ::_("Modify group"):: + ::_("Remove group"):: +
+ ::c.name:: + + ::_("Modify"):: + ::_("Remove"):: +
+ ::_("Add a category in this group"):: +
+::end:: + +::_("Add a group"):: +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contract/default.mtt b/lang/master/tpl/contract/default.mtt new file mode 100644 index 0000000..0e192fc --- /dev/null +++ b/lang/master/tpl/contract/default.mtt @@ -0,0 +1,260 @@ +::use 'design.mtt':: +
+

::_("Last orders")::

+ + ::if constOrders.length>0:: +
+ + + +

::_("CSA Contracts")::

+ + ::foreach c constOrders:: + +

::c.contract.name::

+ + + + + + + + + + + ::set total = 0:: + ::foreach m c.orders:: + + + + + + + + + + + ::end:: + + + + +
::_("Qty||short version for quantity")::::_("Product")::::_("U.P||short version of price per unit")::::_("Sub-total")::::_("Fees")::::_("Total")::::_("Paid")::
+ ::raw m.smartQt:: + + + + ::m.productName:: + + ::if m.userName2 != null:: +
::_("Order alternated with"):: + ::if user.id == m.userId:: + ::m.userName2:: + ::else:: + ::m.userName:: + ::end:: + ::end:: + +
+ ::formatNum(m.productPrice):: ::currency():: + + ::formatNum(m.subTotal):: ::currency():: + + + ::if m.percentageValue!=null:: + + ::formatNum(m.fees):: ::currency():: + + ::end:: + + + ::formatNum(m.total):: ::currency():: + ::set total = total + m.total:: + + ::if m.paid==true:: + ::_("Paid"):: + ::else:: + ::_("Unpaid"):: + ::end:: +
+ Total : + ::formatNum(total):: ::currency():: + + ::set d = c.contract.getDistribs(false,null).length:: + ( soit ::formatNum(d*total):: ::currency():: pour ::d:: livraisons ) +
+ ::end:: + +
+ ::end:: + + + ::if count(varOrders)>0:: +
+ + ::foreach d varOrders:: +

::_("Order delivered on"):: ::hDate(d.date)::

+ + + + + + + + + + + ::set total = 0:: + ::foreach o d.orders:: + + + + + + + + + + + ::end:: + + + + + +
::_("Qty||short version for quantity")::::_("Product")::::_("U.P||short version of price per unit")::::_("Sub-total")::::_("Fees")::::_("Total")::::_("Paid")::
+ ::raw o.smartQt:: + + + + ::o.productName:: + + + ::formatNum(o.productPrice):: ::currency():: + + ::formatNum(o.subTotal):: ::currency():: + + $$nullSafe(::formatNum(o.fees)::) + + ::formatNum(o.total):: ::currency():: + ::set total = total + o.total:: + + ::if o.paid==true:: + ::_("Paid"):: + ::else:: + ::_("Unpaid"):: + ::end:: +
+ ::if !user._amap.hasPayments():: + $$edit(Modifier cette commande,/contract/editOrderByDate/::d.date::) + ::end:: + + TOTAL::formatNum(total):: ::currency()::
+ ::end:: +
+ ::end:: + + +
+
+

::_("Profile")::

+
+ +

+ $$contact(::user::) +

+ + + + + $$edit(::_("Edit")::,/account/edit) + +
+ + ::if user.getAmap().hasPayments():: +

::_("Payments")::

+
+ + ::_("Balance"):: : + ::if userAmap.balance<0:: + ::set color = "C00":: + ::else:: + ::set color = "0C0":: + ::end:: + + ::userAmap.balance:: ::currency():: + + + + + + + ::_("Payments details"):: + + +
+ ::end:: + + + ::if user.isAmapManager():: +

::_("Tutorials")::

+
+ + + ::foreach t tutos:: + + + + + ::end:: + +
::t.name:: + ::if t.completion==null:: + + ::_("Start"):: + + ::else:: + + + ::_("Stop"):: + + ::end:: +
+ + ::if stopTuto:: +
+ + ::end:: +
+ ::end:: + +

::_("Language")::

+
+ ::_("Display the interface in another language"):: (beta) + : +
    + + ::raw langLinks:: + +
+

+ ::_("Current language is"):: + + ::langText:: + +

+
+ +
+ + +::end:: diff --git a/lang/master/tpl/contract/insertChoose.mtt b/lang/master/tpl/contract/insertChoose.mtt new file mode 100644 index 0000000..27451e8 --- /dev/null +++ b/lang/master/tpl/contract/insertChoose.mtt @@ -0,0 +1,37 @@ +::use 'design.mtt':: + +

::_("Choose a type of contract:")::

+ +
+
+ + + +

+ ::_("It is a contract with a duration of one year or for one season. Products are always the same at each delivery"):: +

+

+ ::raw _("Example: there is a contract for vegetables that is 1 year long. Jean takes a basket that costs 10€ per delivery, and Brigitte takes a basket that costs 15€ per delivery."):: +

+
+
+ +
+
+ + + +

+ ::_("It is a contract in which members can change their orders at each delivery. There is a different order for each delivery."):: +

+

+ ::raw _("Example: Jean orders a basket that costs 10€ for the first delivery, then nothing for the second delivery, then a basket for 20€ etc."):: +

+
+
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contract/order.mtt b/lang/master/tpl/contract/order.mtt new file mode 100644 index 0000000..e904efc --- /dev/null +++ b/lang/master/tpl/contract/order.mtt @@ -0,0 +1,139 @@ +::use 'design.mtt':: +
+
+

::_("Order"):: ::c.name::

+ + ::if distribution!=null:: + ::_("For the delivery of the"):: ::hDate(distribution.date):: + ::end:: + +

+ ::raw _("As long as orders are open, you can come back on this page and modify your order.
You can check any time your current orders on the following page my account."):: +

+ +
+ + + ::if distribution!=null:: + + ::end:: + + + + + + ::foreach d userOrders:: + ::if d.distrib==null:: + + ::else:: + + ::end:: + ::end:: + + + + + ::if userOrders.length>0:: + + ::foreach uo userOrders[0].datas:: + + + + + + + ::set i = repeat.uo.index:: + + ::foreach d userOrders:: + + ::end:: + + + ::end:: + + ::else:: + + + + ::end:: + + + + + + ::foreach d userOrders:: + + + + ::end:: + +
::_("Product")::::_("Price")::::_("Qty")::::dDate(d.distrib.date)::
+ + + +   + + + ::uo.product.getName():: + + + ::set price = uo.product.price + uo.product._contract.computeFees(uo.product.price):: + ::formatNum(price):: ::currency():: + + + ::set o = d.datas[i]:: + + ::if o.order==null:: + ::set q = "":: + ::else:: + ::set q = o.order.quantity:: + ::end:: + + + ::if d.distrib==null:: + + + ::else:: + + ::end:: + + +
::_("There is currently no open order")::
Total + + ::set total=0:: + ::foreach o d.datas:: + + ::if o.order!=null:: + ::set q = o.order.quantity:: + ::set price = o.product.price + o.product._contract.computeFees(o.product.price):: + + ::set total = total+(q*price):: + + ::end:: + + ::end:: + + ::formatNum(total):: ::currency():: +
+ + ::_("Tip: to remove an order please key-in the quantity 0")::

+ + + +
+ +
+
+
+
+ ::_("Supplier:"):: ::c._vendor.name:: +
+
+ ::_("Contact in charge:"):: $$contact(::c._contact::) +
+
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contract/orderAndPay.mtt b/lang/master/tpl/contract/orderAndPay.mtt new file mode 100644 index 0000000..b1ae969 --- /dev/null +++ b/lang/master/tpl/contract/orderAndPay.mtt @@ -0,0 +1,137 @@ +::use 'design.mtt':: +
+
+

::_("Order"):: ::c.name::

+ + +

+ ::raw _("Key-in here your orders in advance for this supplier.
You can check your current orders any time on the following page my account.
Tip: to remove an order please key-in the quantity 0"):: +

+ +
+ + + + + + + + + + ::foreach d userOrders:: + ::if d.distrib==null:: + + ::else:: + + ::end:: + ::end:: + + + + + ::if userOrders.length>0:: + + ::foreach uo userOrders[0].datas:: + + + + + + + ::set i = repeat.uo.index:: + + ::foreach d userOrders:: + + ::end:: + + + ::end:: + + ::else:: + + + + ::end:: + + + + + + ::foreach d userOrders:: + + + + ::end:: + +
::_("Product")::::_("Price")::::_("Qty")::::dDate(d.distrib.date)::
+ + + +   + + + ::uo.product.getName():: + + + ::set price = uo.product.price + uo.product._contract.computeFees(uo.product.price):: + ::formatNum(price):: ::currency():: + + + ::set o = d.datas[i]:: + + ::if o.order==null:: + ::if d.distrib==null:: + + + ::else:: + + ::end:: + ::else:: + ::o.order.quantity:: + ::end:: + +
::_("There is currently no open order")::
Total + + ::set total=0:: + ::foreach o d.datas:: + + ::if o.order!=null:: + ::set q = o.order.quantity:: + ::set price = o.product.price + o.product._contract.computeFees(o.product.price):: + + ::set total = total+(q*price):: + + ::end:: + + ::end:: + + ::formatNum(total):: ::currency():: +
+ + + +
+ +
+
+
+
+

+ ::_("Supplier:"):: ::c._vendor.name:: +

+

+ ::_("Contact in charge:"):: $$contact(::c._contact::) +

+ +
+
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contract/orderByDate.mtt b/lang/master/tpl/contract/orderByDate.mtt new file mode 100644 index 0000000..e7d5a01 --- /dev/null +++ b/lang/master/tpl/contract/orderByDate.mtt @@ -0,0 +1,75 @@ +::use 'design.mtt':: +
+
+

+ ::__("Modify the order delivered on ::date::",{date:hDate(date)}):: +

+ +

+ ::raw _("You can modify here an existing order. If you would like to remove a product, just key-in the quantity 0.
You may not be able to modify all elements as the order could be closed for some suppliers"):: +

+ +
+ + + + + + + + + + + + + ::set total = 0:: + ::foreach o orders:: + + + + + + + + + ::set total = total + o.total:: + + ::end:: + + + + + + + + +
::_("Product")::::_("U.P.")::::_("Sub-total")::::_("Fees")::::_("Total")::::_("Quantity")::
+ + + ::o.quantity:: x ::o.productName:: + + + ::formatNum(o.productPrice):: ::currency():: + + ::formatNum(o.subTotal):: ::currency():: + + $$nullSafe(::formatNum(o.fees)::) + + ::formatNum(o.total):: ::currency():: + + ::if o.canModify:: + + ::else:: + ::_("Order closed or already paid"):: + ::end:: +
::_("TOTAL")::::roundTo(total,2)::::currency()::
+ + + +
+ +
+
+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contract/view.mtt b/lang/master/tpl/contract/view.mtt new file mode 100644 index 0000000..0ad9a7b --- /dev/null +++ b/lang/master/tpl/contract/view.mtt @@ -0,0 +1,137 @@ +::use 'design.mtt':: +
+
+ +

::c.name::

+ +
+ +
+ ::if c.description!=null:: + ::raw nl2br(c.description):: + ::end:: +
+ +
+ +
+ +
+ ::foreach p c.getProducts(true):: +
+ + + + + +
+ + +
+ +
+

+ ::p.getName():: +

+ ::formatNum(p.getPrice()):: ::currency():: + + +
+
+ ::end:: +
+ +
+ +
+ ::set vendor = c._vendor:: +
+ + + +
+
+ +

::vendor.name::

+ + ::vendor.city:: (::vendor.zipCode::) + +

+ ::raw nl2br(vendor.desc):: +

+ + ::if vendor.linkUrl!=null:: +

+ ::if vendor.linkText!=null:: + ::vendor.linkText:: + ::else:: + ::_("Read more"):: + ::end:: +

+ ::end:: + +
+
+

::_("Coordinator")::

+ ::if c._contact!=null:: + ::c._contact.firstName:: ::c._contact.lastName::
+ ::c._contact.email::
+ ::c._contact.phone::
+ ::end:: +
+ +
+ +
+
+
+ + +
+

::_("Next distributions")::

+ + ::set distribs = c.getDistribs(true,null):: +
    + ::foreach d distribs:: +
  • + + ::dDate(d.date):: +
    ::d._place.name:: + +

    + ::if user._amap.hasShopMode():: + ::_("Order"):: + ::end:: +

    +
  • + ::end:: +
+ + ::if distribs.length==0:: +

+ ::_("No planned distributions."):: +

+ ::else:: +

+ ::_("Order"):: +

+ ::end:: + + +
+

+ ::_("You can check your previous orders in 'my account' section"):: +

+ + +
+ + + + +
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/calendar.mtt b/lang/master/tpl/contractadmin/calendar.mtt new file mode 100644 index 0000000..d75f155 --- /dev/null +++ b/lang/master/tpl/contractadmin/calendar.mtt @@ -0,0 +1,56 @@ +::use 'design.mtt':: + + + +
+
+ +

Calendrier

+ +
+ + ::foreach c calendar:: + + ::if c.d.getTime() < now:: + ::set col="0.3":: + ::else:: + ::set col="1":: + ::end:: + +
+ + ::hDate(c.d)::
+ ::foreach e c.events:: +
::e.name::
+ ::end:: +
+ + ::end:: +
+ + +
+
+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/contract.mtt b/lang/master/tpl/contractadmin/contract.mtt new file mode 100644 index 0000000..c0b5194 --- /dev/null +++ b/lang/master/tpl/contractadmin/contract.mtt @@ -0,0 +1,57 @@ +::use 'design.mtt':: +::set c = contract:: + + + + +
+
+ + ::c.name:: - ::c._vendor.name:: + +
+
+ Produits + + ::foreach p c.getProducts():: + + + + + ::end:: +
::p.name::$$edit(modifier,/product/edit/::p.id::)
+ $$insert(Nouveau produit,/product/insert) + +
+ +
+ ::_("Deliveries"):: + + ::foreach d c.getDistribs():: + + + + + ::end:: +
::d.date.toString()::$$edit(modifier,/distribution/edit/::d.id::)
+ $$insert(Nouvelle livraison,/distribution/insert) +
+
+
+ +
+
+ + + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/default.mtt b/lang/master/tpl/contractadmin/default.mtt new file mode 100644 index 0000000..fead074 --- /dev/null +++ b/lang/master/tpl/contractadmin/default.mtt @@ -0,0 +1,149 @@ +::use 'design.mtt':: + +
+ + +
0:: style="max-height:400px;overflow-y: auto;"> +

::_("Distributions to validate")::

+ +

::_("You need to validate these distributions which took place recently")::

+ + ::foreach m multidistribs:: +

+ ::if(!m.isConfirmed()):: + + ::dDate(m.getDate())::, ::m.getPlace().name:: + + ::if (isAdmin):: + Admin : Autovalidate + ::end:: + ::elseif(isAdmin):: + + ::dDate(m.getDate())::, ::m.getPlace().name:: + + Admin : Unvalidate + ::end:: +

+ ::end:: + +
+ +
+

+ ::if getParam("old"):: + ::_("Old contracts"):: + ::else:: + ::_("Contracts"):: + ::end:: +

+ + ::foreach c contracts:: + ::if c.endDate!=null && c.endDate.getTime() < Date.now().getTime():: + ::set class = "disabled":: + ::elseif c.startDate!=null && c.startDate.getTime() > Date.now().getTime():: + ::set class = "disabled":: + ::else:: + ::set class = "":: + ::end:: + + + + + + + + ::end:: +
+ ::if c._vendor._image!=null:: +
+ ::end:: +
::c._vendor.name:: + ::c.name:: + + du ::hDate(c.startDate)::
+ au ::hDate(c.endDate):: +
+ +
+ $$edit(::_("Manage")::,/contractAdmin/view/::c.id::) + + ::if user.canManageAllContracts():: + $$delete(::_("Del.||short version of delete")::,/contract/delete/::c.id::?token=::token::) + ::end:: +
+ +
+ + + ::if user.canManageAllContracts():: + $$insert(::_("New contract")::,/contract/insert/) + ::end:: + ::_("Old contracts"):: + ::_("Calendar"):: + + +
+ +
+ + + +
+
+

::_("Farmers")::

+ + ::foreach v vendors:: + + + + + + ::end:: +
+ ::if v._image!=null:: +
+ ::end:: +
::v.name:: +
+ $$edit(::_("Edit")::,/vendor/edit/::v.id::) + ::_("Picture"):: + ::if user.isAmapManager():: $$delete(,/vendor/delete/::v.id::?token=::token::) ::end:: +
+
+ $$insert(::_("New farmer")::,/vendor/insert/) + +
+
+ +
+
+ +

::_("Delivery places")::

+ + ::foreach p places:: + + + + + ::end:: +
::p.name:: + $$edit(::_("Edit")::,/place/edit/::p.id::) + ::if user.isAmapManager():: $$delete(::_("Del.||short version for delete")::,/place/delete/::p.id::?token=::token::) ::end:: +
+ $$insert(::_("New delivery place")::,/place/insert/) +
+
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/deliveries.mtt b/lang/master/tpl/contractadmin/deliveries.mtt new file mode 100644 index 0000000..9dc5e89 --- /dev/null +++ b/lang/master/tpl/contractadmin/deliveries.mtt @@ -0,0 +1,82 @@ +::use 'contractadmin/design.mtt':: + +

::_("Distributions")::

+ + + ::foreach d deliveries:: + + ::if d.date!=null && d.date.getTime() < Date.now().getTime():: + ::set style = "opacity:0.5;":: + ::else:: + ::set style = "":: + ::end:: + + + + + + + + ::end:: +
+ ::if d.distributionCycleId!=null:: + + ::end:: + + ::set da = getDate(d.date):: + + ::if d.end!=null:: + + ::set e = getDate(d.end):: + ::end:: + + ::da.dow:: ::da.d:: ::da.m:: de ::da.h+":"+da.i:: + + ::if d.end!=null:: + à ::e.h+":"+e.i:: + ::end:: + + ::d._place.name:: + +
+ $$edit(::_("Edit")::,/distribution/edit/::d.id::) + $$delete(::_("Delete")::,/distribution/delete/::d.id::) +  ::_("Distribution list"):: +
+
+ + + 0:: class="table"> + + + + + ::foreach c cycles:: + + + + + ::end:: +
::_("Distribution cycle")::
+ ::switch c.cycleType:: + ::case:: ::_("Weekly"):: + ::case:: ::_("Monthly"):: + ::case:: ::_("Every 2 weeks"):: + ::case:: ::_("Every 3 weeks"):: + ::end:: + + : Du ::hDate(c.startDate):: au ::hDate(c.endDate):: + + $$delete(::_("Del.")::,::"/distribution/deleteCycle/"+c.id::) +
+ + $$insert(::_("One time distribution")::,/distribution/insert/::c.id::) + $$insert(::_("Distribution cycle")::,/distribution/insertCycle/::c.id::) + + + ::_("Distributions engagement"):: + + + ::_("Old distributions"):: + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/design.mtt b/lang/master/tpl/contractadmin/design.mtt new file mode 100644 index 0000000..a48c196 --- /dev/null +++ b/lang/master/tpl/contractadmin/design.mtt @@ -0,0 +1,44 @@ +::use 'design.mtt':: + +
+

::c.name:: - ::c._vendor.name::

+
+ +
+
+ +
+
+ + +
+
+ ::raw __content__:: +
+
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/distributionp.mtt b/lang/master/tpl/contractadmin/distributionp.mtt new file mode 100644 index 0000000..e9a59d4 --- /dev/null +++ b/lang/master/tpl/contractadmin/distributionp.mtt @@ -0,0 +1,60 @@ +::use 'contractadmin/design.mtt':: +

::_("Participate to deliveries")::

+ +
+ ::_("This page allows to see if members have participated to deliveries enough."):: +
+ +
+ ::raw __("::distribs:: deliveries requiring ::distributorNum:: person(s)",{distribs:distribs,distributorNum:distributorNum})::,
+ ::users:: ::_("person(s) using this contract")::
+ = ::num:: ::_("participations to deliveries to do per family.")::
+
+ + + + + + + + + ::foreach p participations:: + + + + + + + ::end:: +
::_("Suscriber")::::raw _("Number of attendances to deliveries")::
+ ::p.user.getCoupleName():: + + ::p.count:: + + ::if p.count>=numRounded:: + OK + ::else:: + ::_("Insufficient"):: + ::end:: +
+ + ::if(extParticipations.length>0):: +
+ + + + + + + + ::foreach p extParticipations:: + + + + + + ::end:: +
::_("Member without order in this contract")::::raw _("Number of attendances to deliveries")::
::p.user.getCoupleName()::::p.count::
+ ::end:: + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/edit.mtt b/lang/master/tpl/contractadmin/edit.mtt new file mode 100644 index 0000000..46fe73b --- /dev/null +++ b/lang/master/tpl/contractadmin/edit.mtt @@ -0,0 +1,148 @@ +::use 'contractadmin/design.mtt':: + + + ::if u==null:: +

::_("Key-in an order")::

+ ::else:: +

::_("Key-in an order for"):: ::u.getCoupleName()::

+ ::end:: + + ::if distribution!=null:: +

::_("For the delivery of the"):: ::hDate(distribution.date)::

+ ::end:: + +
+ + ::if distribution!=null:: + + ::end:: + + ::if u==null :: +
+ +
+ +
+
+ ::end:: + +
+ + + + + + + + + ::if distribution==null::::end:: + + ::set total = 0:: + ::foreach uo userOrders:: + + + + + + + + + + + + + ::end:: + + ::set extra = 0:: + + ::if c.hasPercentageOnOrders():: + + + + + + + + + + + ::set extra = total*(c.percentageValue/100):: + + + + + + ::end:: + + + + + + + + + +
::_("Product")::::_("Price")::::_("Quantity")::::_("Status")::::_("Alternately with :")::
+ ::if uo.product.active==false:: + + ::else:: + + ::end:: + + ::if uo.product.active==false:: + ::uo.product.getName():: ::_("(inactive)"):: + ::else:: + ::uo.product.getName():: + ::end:: + + + ::formatNum(uo.product.price):: ::currency():: + + ::if uo.order==null:: + ::set q = 0:: + ::else:: + ::set q = uo.order.quantity:: + ::end:: + + ::set total = total+(q*uo.product.price):: + + + + + + +
::_("Total order")::::total::::currency()::
::c.percentageName:: (::c.percentageValue::%)::extra::::currency()::
::_("Total")::::total+extra::::currency():: + ::_("Check all"):: + +
+ + + +
+::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/orders.mtt b/lang/master/tpl/contractadmin/orders.mtt new file mode 100644 index 0000000..91ca1c3 --- /dev/null +++ b/lang/master/tpl/contractadmin/orders.mtt @@ -0,0 +1,214 @@ +::use 'contractadmin/design.mtt':: + +

::_("Orders")::

+ + ::if distribution!=null:: + ::_("Delivery of the"):: ::hDate(distribution.date):: + ::end:: + +
+ +
+ + ::if distribution!=null:: + ::_("New order"):: + ::else:: + ::_("New order"):: + ::end:: + + ::if distribution!=null:: + $$export(/contractAdmin/orders/::c.id::?csv=1&d=::distribution.id::) + + ::_("Summary by products"):: + + + + + ::else:: + $$export(/contractAdmin/orders/::c.id::?csv=1) + + ::_("Summary by products"):: + ::end:: +
+ + ::if disabledProducts > 0:: + $$alert( ::raw __("Be careful, there are ::disabledProducts:: order(s) containing disabled products.",{disabledProducts:disabledProducts}):: ) + ::end:: + + + + + ::set total = 0:: + ::set totalAdh = 0:: + + ::set name = "":: + ::set name2 = "":: + + ::foreach m orders:: + + ::if m.userName != name || m.userName2 != name2:: + + + + + + + + + + + + + + + + + ::end:: + + + ::if m.disabled==true:: + ::set class="danger":: + ::else:: + ::set class="":: + ::end:: + + + + + + + + + + + + + ::end:: + + + + + + + + + + + + +
+ + ::if distribution!=null:: + ::set basket = getBasket(m.userId,distribution._place.id,distribution.date):: +
+ N°::basket.num:: +
+ ::end:: + + ::m.userName:: + ::if m.userName2!=null:: + ::_("alternated with"):: ::m.userName2:: + ::end:: + + ::set name = m.userName :: + ::set name2 = m.userName2 :: + ::set totalAdh = totalAdh+1:: +
+ + + + ::if distribution!=null:: + + + ::_("Edit"):: + + ::else:: + + + ::_("Edit"):: + + ::end:: + + +
::_("Prod. ID")::::_("Qty")::::_("Product")::::_("U.P||price per unit")::::_("Sub-total")::::_("Fees")::::_("Total")::::_("Paid")::
+ $$nullSafe(::m.productRef::) + + ::if(m.quantity==0 && m.canceled):: + ::_("Canceled"):: + ::else:: + ::raw m.smartQt:: + ::end:: + + ::m.productName:: + + ::formatNum(m.productPrice):: ::currency():: + + ::formatNum(m.subTotal):: ::currency():: + + + ::if m.percentageValue!=null:: + + ::formatNum(m.fees):: ::currency():: + + ::end:: + + + ::formatNum(m.total):: ::currency():: + ::set total = total + m.total:: + + ::if !m.canceled && m.quantity!=0:: + ::if m.paid==true:: + ::_("Paid"):: + ::else:: + ::_("Not paid"):: + ::end:: + ::end:: + + ::if(m.quantity==0):: +      + ::if distribution==null:: + + + ::_("Delete"):: + + ::else:: + + + ::_("Delete"):: + + ::end:: + + ::end:: + +
::_("Total")::::formatNum(total):: ::currency()::
+ + +
+ ::raw __("::totalAdh:: member(s) in this distribution.",{totalAdh:totalAdh}):: +
+ +::end:: diff --git a/lang/master/tpl/contractadmin/ordersByDate.mtt b/lang/master/tpl/contractadmin/ordersByDate.mtt new file mode 100644 index 0000000..5d4b812 --- /dev/null +++ b/lang/master/tpl/contractadmin/ordersByDate.mtt @@ -0,0 +1,172 @@ +::use 'design.mtt':: + + + + +
+
+ +
+

+ Distribution du ::hDate(date):: +

+

::place::

+
+ +
+ + + + +
+ + + + ::set total = 0:: + ::set subtotal = 0:: + ::set contractTotal = 0:: + + ::foreach m orders:: + + ::set i = repeat.m.index:: + + + ::if orders[i-1]==null || m.userName != orders[i-1].userName || m.userName2 != orders[i-1].userName2:: + + + + + + + + + + + + + + + + + ::end:: + + + + + + + + + + + + + + + + + ::if ctotal && (orders[i+1]==null || m.contractName != orders[i+1].contractName || m.userName != orders[i+1].userName ):: + + + + + + ::set contractTotal = 0:: + + ::end:: + + + ::if (orders[i+1]==null || m.userName != orders[i+1].userName) :: + + ::if subtotal!=0:: + + + + + ::set subtotal = 0:: + + ::end:: + ::end:: + + + ::end:: + + + + + + + + + +
+ + ::set basket = getBasket(m.userId,place.id,date):: + +
+ ::_("Num"):: ::basket.num:: +
+ + ::m.userName:: + ::if m.userName2!=null:: + ::_("alternately with"):: ::m.userName2:: + ::end:: + ::set contractTotal = 0:: +
::_("Contract")::::_("Qty")::::_("Ref")::::_("Product")::::_("U.P.")::::_("Sub-total")::::_("Fees")::::_("Total")::::_("Paid")::
+ + ::short(m.contractName,40):: + + + ::if(m.quantity==0 && m.canceled):: + ::_("Canceled"):: + ::else:: + ::raw m.smartQt:: + ::end:: + + $$nullSafe(::m.productRef::) + + ::short(m.productName,40):: + + ::formatNum(m.productPrice):: ::currency():: + + ::formatNum(m.subTotal):: ::currency():: + + + ::if m.percentageValue!=null:: + + ::formatNum(m.fees):: ::currency():: + + ::end:: + + + ::formatNum(m.total):: ::currency():: + ::set total = total + m.total:: + ::set subtotal = subtotal + m.total:: + ::set contractTotal = contractTotal + m.total:: + + ::if m.paid==true:: + ::_("Paid"):: + ::else:: + ::_("Not paid"):: + ::end:: +
Total ::m.contractName::::formatNum(contractTotal):: ::currency()::
::_("Total member")::::formatNum(subtotal):: ::currency()::
::_("Total of all orders:"):: ::formatNum(total):: ::currency()::
+
+ +
+ +::end:: diff --git a/lang/master/tpl/contractadmin/ordersByProduct.mtt b/lang/master/tpl/contractadmin/ordersByProduct.mtt new file mode 100644 index 0000000..7d8682c --- /dev/null +++ b/lang/master/tpl/contractadmin/ordersByProduct.mtt @@ -0,0 +1,70 @@ +::use 'contractadmin/design.mtt':: + +

::_("Summary table per product")::

+ + ::if distribution!=null:: +

+ ::set date = hDate(distribution.date):: + ::raw __("Delivery of the ::date::",{date:date}):: +

+ ::end:: + +

+ ::_("Be careful, if the price of the product was modified during the sale, every order of a member is taken into account with the price as it was at the time of the creation of the order"):: +

+ ::set total = 0:: + + + + + + + + + + + ::foreach m orders:: + + + + + + + + + + + ::end:: + + + + + + +
::_("Weight/Vol.")::::_("Quantities")::::_("Product")::::_("Reference")::::_("Unit Price incl. VAT")::::_("Total")::
+ ::raw m.weightOrVolume:: + + ::formatNum(m.quantity):: + + ::m.pname:: + + $$nullSafe(::m.ref::) + + ::formatNum(m.priceTTC):: ::currency():: + + ::formatNum(m.totalTTC):: ::currency():: + ::set total = total + m.totalTTC:: +
Total::formatNum(total):: ::currency()::
+ + ::if distribution!=null:: + $$export(/contractAdmin/ordersByProduct/::c.id::?csv=1&d=::distribution.id::) + + ::_("Order form"):: + ::else:: + $$export(/contractAdmin/ordersByProduct/::c.id::?csv=1) + + ::_("Order form"):: + ::end:: + + +::end:: diff --git a/lang/master/tpl/contractadmin/ordersByProductList.mtt b/lang/master/tpl/contractadmin/ordersByProductList.mtt new file mode 100644 index 0000000..7bc91ad --- /dev/null +++ b/lang/master/tpl/contractadmin/ordersByProductList.mtt @@ -0,0 +1,89 @@ + + + + + Bon de commande + + + + + + + + + +
+ ::if group._image!=null:: + + ::end:: + +

::group.name::

+ ::if distribution!=null:: +

Livraison du ::hDate(distribution.date)::

+ ::end:: +

::c.name:: - ::c._vendor.name::

+
+ ::set cv = c._vendor:: + ::if cv._image!=null:: + + ::end:: +
+ +

+ ::set cc = c._contact:: + ::set cv = c._vendor:: + Responsable contrat : ::cc.firstName:: ::cc.lastName::, ::cc.email::, + ::if cc.phone!=null :: ::cc.phone:: ::end:: +

+

+ Producteur : ::cv.name:: , + ::if cv.email!=null :: ::cv.email:: ::end::, + ::if cv.phone!=null :: ::cv.phone:: ::end:: +

+ +

+

::_("Summary table per product")::

+ ::_("Be careful, if the price of the product was modified during the sale, every order of a member is taken into account with the price as it was at the time of the creation of the order"):: +

+ ::set total = 0:: + + + + + + + + + + ::foreach m orders:: + + + + + + + + + ::end:: + + + + + +
::_("Weight/Vol.")::::_("Quantities")::::_("Product")::::_("Reference")::::_("Unit price incl. VAT")::::_("Total")::
+ ::raw m.weightOrVolume:: + + ::formatNum(m.quantity):: + + ::m.pname:: + + $$nullSafe(::m.ref::) + + ::formatNum(m.priceTTC):: ::currency():: + + ::formatNum(m.totalTTC):: ::currency():: + ::set total = total + m.totalTTC:: +
::_("Total")::::formatNum(total):: ::currency()::
+ + + diff --git a/lang/master/tpl/contractadmin/ordersByTimeFrame.mtt b/lang/master/tpl/contractadmin/ordersByTimeFrame.mtt new file mode 100644 index 0000000..c11cf68 --- /dev/null +++ b/lang/master/tpl/contractadmin/ordersByTimeFrame.mtt @@ -0,0 +1,146 @@ +::use 'design.mtt':: + +
+
+ +
+

::_("Orders from the"):: ::hDate(from):: ::_("to"):: ::hDate(to)::

+
+ + ::set uri = from.toString().substr(0,10)+"/"+to.toString().substr(0,10):: + + +
+ + + + ::set total = 0:: + ::set subtotal = 0:: + ::set contractTotal = 0:: + + ::foreach m orders:: + + ::set i = repeat.m.index:: + + + ::if orders[i-1]==null || m.userName != orders[i-1].userName || m.userName2 != orders[i-1].userName2:: + + + + + + + + + + + + + + + + + ::end:: + + + + + + + + + + + + + + + + + ::if ctotal && (orders[i+1]==null || m.contractName != orders[i+1].contractName || m.userName != orders[i+1].userName ):: + + + + + + ::set contractTotal = 0:: + + ::end:: + + + ::if (orders[i+1]==null || m.userName != orders[i+1].userName) :: + + ::if subtotal!=0:: + + + + + ::set subtotal = 0:: + + ::end:: + ::end:: + + + ::end:: + + + + + + + + + +
+ ::m.userName:: + ::if m.userName2!=null:: + en alternance avec ::m.userName2:: + ::end:: + ::set contractTotal = 0:: +
::_("Contract")::::_("Qty")::::_("Ref")::::_("Product")::::_("U.P.")::::_("Sub-total")::::_("Fees")::::_("Total")::::_("Paid")::
+ + ::short(m.contractName,40):: + + + ::if(m.quantity==0 && m.canceled):: + ::_("Canceled"):: + ::else:: + ::raw m.smartQt:: + ::end:: + + $$nullSafe(::m.productRef::) + + ::short(m.productName,40):: + + ::formatNum(m.productPrice):: ::currency():: + + ::formatNum(m.subTotal):: ::currency():: + + + ::if m.percentageValue!=null:: + + ::formatNum(m.fees):: ::currency():: + + ::end:: + + + ::formatNum(m.total):: ::currency():: + ::set total = total + m.total:: + ::set subtotal = subtotal + m.total:: + ::set contractTotal = contractTotal + m.total:: + + ::if m.paid==true:: + ::_("Paid"):: + ::else:: + ::_("Not paid"):: + ::end:: +
Total ::m.contractName::::formatNum(contractTotal):: ::currency()::
::_("Total member")::::formatNum(subtotal):: ::currency()::
::_("Total of all orders:"):: ::formatNum(total):: ::currency()::
+
+ +
+ +::end:: diff --git a/lang/master/tpl/contractadmin/products.mtt b/lang/master/tpl/contractadmin/products.mtt new file mode 100644 index 0000000..99d2296 --- /dev/null +++ b/lang/master/tpl/contractadmin/products.mtt @@ -0,0 +1,156 @@ +::use 'contractadmin/design.mtt':: + +

::_("Products")::

+ +
+

+ $$insert(::_("New product")::,/product/insert/::c.id::) + ::_("Import from Excel/CSV"):: + + ::if !c._amap.hasTaxonomy():: + ::_("Categories"):: + ::end:: + +

+
+ + + +
+ + + + + + + + + ::if c.hasStockManagement():: + + ::end:: + + + + ::foreach p c.getProducts(false):: + + ::if p.active==false:: + ::set opacity=0.4:: + ::else:: + ::set opacity=1:: + ::end:: + + + + + + + + + + + ::end:: +
::_("Product ID||id or reference of a product")::::_("Name")::::_("Price")::::_("Stock")::
+ + + + $$nullSafe(::p.ref::) + ::p.name:: + + ::if p._unitType!=null && p.qt!=null:: + ::p.qt:: ::unit(p._unitType,false):: + ::end:: + + ::if !c._amap.hasTaxonomy():: +
+ ::foreach c p.getCategories():: + ::c.name:: + ::end:: +
+ ::else:: +
+ ::p.getFullCategorization().join(" / "):: +
+ ::end:: +
+ ::if p.organic ::::end:: + ::if p.variablePrice ::::end:: +
+
::formatNum(p.price):: ::currency():: + ::if c.hasStockManagement() :: + ::if p.stock==null:: + - + ::else:: + ::if p.stock <= 10:: + + ::p.stock:: + + ::else:: + ::p.stock:: + ::end:: + ::end:: + ::end:: + +
+ $$edit(::_("Edit")::,/product/edit/::p.id::) + ::_("Picture"):: + $$delete(::_("Del.")::,/product/delete/::p.id::?token=::token::) +
+
+ +
+ + +

+

+ + + +

+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/selectDistrib.mtt b/lang/master/tpl/contractadmin/selectDistrib.mtt new file mode 100644 index 0000000..29aff6e --- /dev/null +++ b/lang/master/tpl/contractadmin/selectDistrib.mtt @@ -0,0 +1,53 @@ +::use 'contractadmin/design.mtt':: + + ::if distributions.length>0:: + +

::_("Orders")::

+ + + + + + + + ::foreach d distributions:: + + ::if d.date!=null && d.date.getTime() < Date.now().getTime():: + ::set style = "opacity:0.5;":: + ::else:: + ::set style = "":: + ::end:: + + + + + + + ::end:: +
::_("Date")::::_("Location")::
::hDate(d.date)::::d._place.name:: + + + ::_("Orders"):: + + +
+ + ::else:: +
+ + Aucune distribution prochainement.
+ ::_("Click here to manage deliveries"):: +
+ ::end:: + + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/stats.mtt b/lang/master/tpl/contractadmin/stats.mtt new file mode 100644 index 0000000..fca38d7 --- /dev/null +++ b/lang/master/tpl/contractadmin/stats.mtt @@ -0,0 +1,63 @@ +::use 'contractadmin/design.mtt':: + + + + ::if stat==0:: +

+ ::__("Seniority of subscribers of ::contract::",{contract:c.name}):: + +

+ + + + + + ::foreach a anciennete:: + + + + + ::end:: +
::_("Number")::::_("Year")::
::a.cnt::::a.uyear::
+ ::end:: + + ::if stat==1:: +

::_("Sharing between orders of"):: "::c.name::"

+ + + + + + + + + ::foreach r repartition:: + + + + + + + + + ::end:: + + + + + + +
::_("Quantity")::::_("Product")::::_("Total price")::::_("% of orders")::
::r.quantity::::r.name::::r.quantity*r.price:: ::currency()::::r.percent:: %
Total: ::totalQuantity:: ::_("products")::::totalPrice:: ::currency()::
+ + + $$export(/contractAdmin/stats/::c.id::?csv=1&stat=1) + ::end:: +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/stocks.mtt b/lang/master/tpl/contractadmin/stocks.mtt new file mode 100644 index 0000000..0a134a8 --- /dev/null +++ b/lang/master/tpl/contractadmin/stocks.mtt @@ -0,0 +1,27 @@ +::use 'contractadmin/design.mtt':: + + +

::_("Products")::

+ + + ::foreach p c.getProducts():: + + + + + + + + + + ::end:: +
+ + + + $$nullSafe(::p.ref::)::p.name::::p.stock:: + $$edit(modifier,/product/edit/::p.id::) +
+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/vendorsByDate.mtt b/lang/master/tpl/contractadmin/vendorsByDate.mtt new file mode 100644 index 0000000..fa603be --- /dev/null +++ b/lang/master/tpl/contractadmin/vendorsByDate.mtt @@ -0,0 +1,61 @@ +::use 'design.mtt':: + +
+
+ +

::_("Global view of orders - delivery of"):: ::hDate(date)::

+
+ + ::foreach o orders:: + + ::set total = 0:: + + + + + + + + + + + + + ::foreach m o.orders:: + + + + + + + + ::end:: + + + + + +
+ +

::o.contract.name:: - ::o.contract._vendor.name::

+ +
::_("Quantities")::::_("Product")::::_("Reference")::::_("Unit price incl. VAT")::::_("Total")::
+ ::m.quantity:: + + ::m.pname:: + + $$nullSafe(::m.ref::) + + ::formatNum(m.priceTTC):: ::currency():: + + ::formatNum(m.totalTTC):: ::currency():: + ::set total = total + m.totalTTC:: +
Total::formatNum(total):: ::currency()::
+ + ::end:: + +
+ +
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/vendorsByTimeFrame.mtt b/lang/master/tpl/contractadmin/vendorsByTimeFrame.mtt new file mode 100644 index 0000000..a75be4c --- /dev/null +++ b/lang/master/tpl/contractadmin/vendorsByTimeFrame.mtt @@ -0,0 +1,68 @@ +::use 'design.mtt':: + +
+
+ +

::_("Global view of orders - from"):: ::hDate(from):: ::_("to"):: ::hDate(to)::

+ +
+ $$export(/contractAdmin/vendorsByTimeFrame/::from.toString().substr(0,10)::/::to.toString().substr(0,10)::?csv=1) +
+ +
+ ::foreach o orders:: + + ::set total = 0:: + + + + + + + + + + + + + + ::foreach m o.orders:: + + + + + + + + ::set total = total + m.totalTTC:: + + ::end:: + + + + + +
+ +

::o.contract.name:: - ::o.contract._vendor.name::

+
::_("Quantities")::::_("Product")::::_("Reference")::::_("Unit price incl. VAT")::::_("Total HT")::::_("Total TTC")::
+ ::formatNum(m.quantity):: + + ::m.pname:: + + $$nullSafe(::m.ref::) + + ::formatNum(m.priceTTC):: ::currency():: + + ::formatNum(m.totalHT):: ::currency():: + + ::formatNum(m.totalTTC):: ::currency():: +
::_("Total")::::formatNum(total):: ::currency()::
+ + ::end:: + +
+ +
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/contractadmin/view.mtt b/lang/master/tpl/contractadmin/view.mtt new file mode 100644 index 0000000..61750cf --- /dev/null +++ b/lang/master/tpl/contractadmin/view.mtt @@ -0,0 +1,70 @@ +::use 'contractadmin/design.mtt':: + +

::_("Summary")::

+ + + + + + + + + + + + + + +
+ ::_("Contract type"):: + + ::if contract.type==0:: + ::_("CSA Contract"):: + ::else:: + ::_("Variable order contract"):: + ::end:: +
::_("Start date")::::hDate(contract.startDate)::
::_("End date")::::hDate(contract.endDate)::
+ +
+ $$edit(::_("Edit")::,/contract/edit/::c.id::) + + ::if user.isAmapManager():: + $$edit(::_("Duplicate")::,/contractAdmin/duplicate/::c.id::) + ::end:: +
+ +
+ +

::_("Farmer")::

+ + + + + + + + + + + + + +
::_("Name")::::contract._vendor.name::
::_("Email")::$$nullSafe(::contract._vendor.email::)
::_("Phone")::$$nullSafe(::contract._vendor.phone::)
+ + +

::_("Products")::

+
+ + ::foreach p contract.getProducts(true):: + + + + + + ::end:: +
::p.getName()::::formatNum(p.price):: ::currency()::
+
+ + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/cssDemo.mtt b/lang/master/tpl/cssDemo.mtt new file mode 100644 index 0000000..42ffda0 --- /dev/null +++ b/lang/master/tpl/cssDemo.mtt @@ -0,0 +1,194 @@ +::use 'design.mtt':: + +
+ +
+
+
+ + Hou là là ! +
+ +
+
+ +
+
+ +
+ + Todo bem +
+ +
+
+ +
+
+ +
+

Boutons large

+ + + + + + + + +
+ +
+

Boutons default

+ + + + + + + + + + + + + + + + + + + +
+ + +
+

Boutons small

+ + + + + + + +
+ +
+

Boutons XS

+ + + + + + + +
+ + + +
+

H1 Title

+

H2 Title

+

H3 Title

+

H4 Title

+ +
+ +
+ +
+
+

Panel title

+
+
+

+ Type : Gratuit jusqu'à 20 foyers ( actuellement 1 foyers dans votre groupe) +

+

+ +

+ +
+
+ +
+ +
+ + + + + + +
+ + + +
+
+ + + + +
+ + + Le Lorem Ipsum est simplement du faux texte employé dans la composition + et la mise en page avant impression. Le Lorem Ipsum est le faux texte standard de l'imprimerie depuis les années 1500, + quand un peintre anonyme assembla ensemble des morceaux de texte pour réaliser un livre spécimen de polices de texte. + Il n'a pas fait que survivre cinq siècles, mais s'est aussi adapté à la bureautique informatique, sans que son contenu n'en soit modifié. + + + +
+ +
+

Permanences

+ +

N'oubliez pas de vous inscrire pour aider lors des livraisons

+ +
    +
  • Liste 1
  • +
  • Liste 2
  • +
+
+ +
+

+ Attention, vous n'avez défini aucun mot de passe pour votre compte ! +

+ Définir un mot de passe + +
+ + $$today(Mercredi,15,Décembre,19,90,::user._amap._mainPlace::) + + $$date(Mercredi,15,Décembre,19,::user._amap._mainPlace::) + + +
+::end:: \ No newline at end of file diff --git a/lang/master/tpl/design.mtt b/lang/master/tpl/design.mtt new file mode 100644 index 0000000..6b4814d --- /dev/null +++ b/lang/master/tpl/design.mtt @@ -0,0 +1,269 @@ +::use 'base.mtt':: + + ::if getParam('__redirect')!=null:: + + + ::end:: + +
+
+ ::set groupName = getCurrentGroup():: + ::if groupName!=null && noGroup!=true:: +

::groupName::

+ ::else:: +
+ + ::NAME:: + +
+ ::end:: +
+ + +
+ + + ::if user != null && noGroup!=true:: + + + + ::end:: + + +
+
+ ::foreach notification getMessages():: +
+ ::if notification.error:: + + ::else:: + + ::end:: + + ::raw notification.text:: +
+ ::end:: +
+
+ + + ::if extraNotifBlock!=null:: + ::raw extraNotifBlock:: + ::end:: + +
+ ::raw __content__:: +
+ + + + + +
+ + version ::VERSION::-::getPlatform():: +
+ +
+
+ + + ::foreach r sqlLog:: + + + + + + + + ::end:: +
::r.t::ms::r.length::ExpStack::r.sql::
+
+
+ + ::if (false && user!=null && user.isAmapManager()):: + + ::end:: +::end:: diff --git a/lang/master/tpl/distribution/list.mtt b/lang/master/tpl/distribution/list.mtt new file mode 100644 index 0000000..0335dca --- /dev/null +++ b/lang/master/tpl/distribution/list.mtt @@ -0,0 +1,156 @@ + + + + + ::_("Attendance sheet"):: + + + +

+ ::contract._amap.name:: - + ::__("Distribution of the ::date::, from ::from:: to ::to::",{date:dDate(distrib.date),from:hHour(distrib.date),to:hHour(distrib.end)}):: +

+

+ ::distrib._contract.name:: - ::distrib._contract._vendor.name:: +

+ +

+ ::_("Place"):: : + ::distrib._place.toString()::, ::distrib._place.getAddress():: +

+

+ ::set c = contract._contact:: + ::set v = contract._vendor:: + ::_("Contact in charge of the contract") : ::c.firstName:: ::c.lastName::, ::c.email::, ::c.phone:: +

+

+ ::_("Farmer"):: : + ::v.name:: , + ::if v.email!=null :: ::v.email:: ::end::, + ::if v.phone!=null :: ::v.phone:: ::end:: +

+

+ ::_("Members on duty"):: : + ::if distrib._distributor1!=null:: ::distrib._distributor1.getName()::, ::end:: + ::if distrib._distributor2!=null:: ::distrib._distributor2.getName()::, ::end:: + ::if distrib._distributor3!=null:: ::distrib._distributor3.getName()::, ::end:: + ::if distrib._distributor4!=null:: ::distrib._distributor4.getName():: ::end:: +

+ +

::raw nl2br(distrib._contract._amap.txtDistrib)::

+ + + ::set total = 0:: + ::set subtotal = 0:: + + ::foreach m orders:: + + ::set i = repeat.m.index:: + + + ::if orders[i-1]==null || m.userName != orders[i-1].userName || m.userName2 != orders[i-1].userName2:: + + + + + + + + + + + + + + + ::end:: + + + + + + + + + + + + + + + ::if (orders[i+1]==null || m.userName != orders[i+1].userName || m.userName2 != orders[i+1].userName2) :: + ::if subtotal!=0:: + + + + + + ::set subtotal = 0:: + + ::end:: + ::end:: + ::end:: + + + + + + + +
+ + ::set basket = getBasket(m.userId,distrib._place.id,distrib.date):: + N°::basket.num:: - + + ::m.userName:: + + + ::set u = getUser(m.userId):: + + ::if u !=null && u.phone!=null:: + - Tél. $$nullSafe(::u.phone::) + ::end:: + + ::if m.userName2 != null:: + ::_("alternately with"):: + + ::m.userName2:: + + + ::set u = getUser(m.userId2): + ::if u !=null:: + - Tél. $$nullSafe(::u.phone::) + ::end:: + ::end:: +
::_("Qty")::::_("Ref")::::_("Product")::::_("U.P.")::::_("Sub-total")::::_("Fees")::::_("Total")::::_("Signature")::
+ ::if(m.quantity==0 && m.canceled):: + ::_("Canceled"):: + ::else:: + ::raw m.smartQt:: + ::end:: + + $$nullSafe(::m.productRef::) + + ::m.productName:: + + ::formatNum(m.productPrice):: ::currency():: + + ::formatNum(m.subTotal):: ::currency():: + + + ::if m.percentageValue!=null:: + ::formatNum(m.fees):: ::currency():: + ::end:: + + + ::formatNum(m.total):: ::currency():: + ::set total = total + m.total:: + ::set subtotal = subtotal + m.total:: + +
::_("TOTAL")::::formatNum(subtotal):: ::currency()::
::_("Total order:"):: ::formatNum(total):: ::currency()::
+ + + + + + diff --git a/lang/master/tpl/distribution/listByDate.mtt b/lang/master/tpl/distribution/listByDate.mtt new file mode 100644 index 0000000..153e327 --- /dev/null +++ b/lang/master/tpl/distribution/listByDate.mtt @@ -0,0 +1,252 @@ + + + + + ::_("Attendance sheet"):: + + + + +

::user._amap.name::

+

Livraison du ::hDate(date)::

+ + + ::set name = "":: + ::set name2 = "":: + ::set total = 0:: + ::set productNum = 0:: + + + + + + + + + + + + ::foreach m orders:: + + + ::if m.userName != name || m.userName2 != name2:: + ::set class="name":: + ::else:: + ::set class="":: + ::end:: + + + + ::set name = m.userName :: + ::set name2 = m.userName2 :: + + + + + + + + + + + ::set next = orders[repeat.m.index+1]:: + ::if next==null || next.userName!=name || next.userName2!=name2:: + + + + + + + + + + + + + + + + ::if (user._amap.hasShopMode()):: + + + + + + + ::else:: + + + ::set ua = u.getUserAmap(user._amap):: + ::set balance = ua.balance:: + + + ::end:: + + + + ::end:: + ::end:: + +
::_("Name")::::_("Contact details")::::_("Qty")::::_("Product")::::_("U.P.")::::_("Fees")::::_("Total")::::_("Signature")::
+ ::if m.userName != name || m.userName2 != name2:: + + ::set basket = getBasket(m.userId,place.id,date):: + ::_("Num"):: ::basket.num:: - + + ::m.userName:: + ::if m.userName2 != null:: +
::_("alternately with"):: ::m.userName2:: + ::end:: + ::set total = 0:: + ::set productNum = 0:: + ::end:: +
+ ::if m.userName != name || m.userName2 != name2:: + ::set u = getUser(m.userId): + ::if u !=null:: + $$nullSafe(::u.phone::) + ::end:: + + ::if m.userName2 != null:: + ::set u = getUser(m.userId2): + ::if u !=null:: +
$$nullSafe(::u.phone::) + ::end:: + ::end:: + ::end:: + +
+ ::if(m.quantity==0 && m.canceled):: + ::_("Canceled"):: + ::else:: + ::raw m.smartQt:: + ::end:: + + ::set productNum = productNum+m.quantity:: + + ::m.productName:: + + ::formatNum(m.productPrice):: ::currency():: + + ::if m.fees!=null:: + ::formatNum(m.fees):: ::currency():: + ::end:: + + ::formatNum(m.total):: ::currency():: + ::set total = total + m.total:: +
::formatNum(productNum)::::_("TOTAL")::::formatNum(total):: ::currency()::
::_("Payment:"):: + + ::if(basket!=null):: + ::set tpaid=0:: + ::set op = basket.getOrderOperation(false):: + ::if op!=null:: + ::foreach p op.getRelatedPayments():: + + + + + + ::end:: + ::end:: + + + ::set total = numClean(total):: + ::set tpaid = numClean(tpaid):: + + ::if(total!=tpaid):: + + + + + + + ::end:: + ::end:: +
+ ::if(p.pending==true):: + + ::else:: + + ::end:: + + ::formatNum(p.amount):: ::currency():: + ::set tpaid = p.amount+tpaid:: + + ::p.name:: +
::formatNum(total-tpaid):: ::currency():: + ::if(total>tpaid):: + ::_("To be paid"):: ::formatNum(total-tpaid):: ::currency():: + ::else:: + ::_("Pay back"):: ::formatNum(tpaid-total):: ::currency():: + ::end:: +
+
::_("Balance of the member:"):: ::ua.balance:: ::currency():: + ::set pending = 0:: + + + + ::foreach p ua.getLastOperations(5):: + ::if p.type==2 && p.pending==true:: + + + + + ::end:: + ::end:: + + + + ::if(balance<0 && balance+pending!=0):: + + + + + ::end:: + +
+ ::if(p.pending==true):: + + ::else:: + + ::end:: + + ::formatNum(p.amount):: ::currency():: + - ::p.name:: + + ::set pending = pending + p.amount:: + +
+ ::if(balance+pending<0):: + ::_("To be paid"):: ::formatNum(abs(balance+pending)):: ::currency():: pour équilibrer le solde + ::else:: + ::_("Pay back"):: ::formatNum(balance+pending):: ::currency():: pour équilibrer le solde + ::end:: +
+ + + +
+ +

::raw nl2br(user.getAmap().txtDistrib)::

+ + + + + + + ::foreach r sqlLog:: + + + + + + + + ::end:: +
::r.t::ms::r.length::ExpStack::r.sql::
+ + + + diff --git a/lang/master/tpl/distribution/listByDateOnePage.mtt b/lang/master/tpl/distribution/listByDateOnePage.mtt new file mode 100644 index 0000000..e22140f --- /dev/null +++ b/lang/master/tpl/distribution/listByDateOnePage.mtt @@ -0,0 +1,163 @@ + + + + + ::_("Attendance sheet"):: + + + + + ::fill head:: + +

::user._amap.name::

+

::_("Delivery of the"):: ::hDate(date)::

+ + ::end:: + + + ::fill tableHeader:: + + ::_("Qty"):: + ::_("Product"):: + ::_("U.P."):: + ::_("Fees"):: + ::_("Total"):: + + ::_("Signature"):: + + ::end:: + + ::set name = "":: + ::set name2 = "":: + ::set total = 0:: + ::set productNum = 0:: + + ::foreach m orders:: + + ::if m.userName != name || m.userName2 != name2:: + + ::if name != "":: + ::raw "
":: + ::end:: + + ::raw "" :: + + + + + + + + + + + ::raw "
+ + + ::set basket = getBasket(m.userId,place.id,date):: + ::_("Num"):: ::basket.num:: - + ::m.userName:: + + ::set total = 0:: + ::set productNum = 0:: + + ::set u = getUser(m.userId): + ::if u!=null && u.phone!=null:: + Tél: $$nullSafe(::u.phone::) + ::end:: + + ::user._amap.name:: +
+ ::if m.userName2 != null:: + ::_("alternately with")::
+ ::m.userName2:: + ::set total = 0:: + ::set productNum = 0:: + ::end:: +
+ ::if m.userName2 != null:: + ::set u = getUser(m.userId2): + ::if u !=null:: + $$nullSafe(::u.phone::) + ::end:: + ::end:: + + ::_("Delivery of the"):: ::hDate(date):: +
" :: + + ::raw "":: + ::raw tableHeader:: + + + ::end:: + + ::if m.userName != name || m.userName2 != name2:: + ::set class="name":: + ::else:: + ::set class="":: + ::end:: + + + + + + + + + + + ::set name = m.userName :: + ::set name2 = m.userName2 :: + + + ::set next = orders[repeat.m.index+1]:: + ::if next==null || next.userName!=name || next.userName2!=name2:: + + + + + + + + ::end:: + ::end:: + +

::raw nl2br(user.getAmap().txtDistrib)::

+ + + + + +
+ ::if(m.quantity==0 && m.canceled):: + ::_("Canceled"):: + ::else:: + ::raw m.smartQt:: + ::end:: + ::set productNum = productNum+m.quantity:: + + ::m.productName:: + + ::formatNum(m.productPrice):: ::currency():: + + ::if m.fees!=null:: + ::formatNum(m.fees):: ::currency():: + ::end:: + + ::formatNum(m.total):: ::currency():: + ::set total = total + m.total:: +
::formatNum(productNum)::::_("TOTAL")::::formatNum(total):: ::currency()::
+ ::foreach r sqlLog:: + + + + + + + + ::end:: +
::r.t::ms::r.length::ExpStack::r.sql::
+ + + + diff --git a/lang/master/tpl/distribution/listByDateOnePageContract.mtt b/lang/master/tpl/distribution/listByDateOnePageContract.mtt new file mode 100644 index 0000000..0e50ef8 --- /dev/null +++ b/lang/master/tpl/distribution/listByDateOnePageContract.mtt @@ -0,0 +1,178 @@ + + + + + ::_("Attendance sheet"):: + + + + + ::fill head:: + +

::user._amap.name::

+

::_("Delivery of the"):: ::hDate(date)::

+ + ::end:: + + + ::fill tableHeader:: + + ::_("Contract"):: + ::_("Qty"):: + ::_("Product"):: + ::_("U.P."):: + ::_("Fees"):: + ::_("Total"):: + + ::_("Signature"):: + + ::end:: + + ::set name = "":: + ::set name2 = "":: + ::set total = 0:: + ::set productNum = 0:: + ::set currentContract = "":: + + ::foreach m orders:: + + ::if m.userName != name || m.userName2 != name2:: + + ::if name != "":: + ::raw "
":: + ::end:: + + ::raw "" :: + + + + + + + + + + + ::raw "
+ + ::set basket = getBasket(m.userId,place.id,date):: + N°::basket.num:: - + ::m.userName:: + + ::set total = 0:: + ::set productNum = 0:: + + ::set u = getUser(m.userId): + ::if u!=null && u.phone!=null:: + Tél: $$nullSafe(::u.phone::) + ::end:: + + ::user._amap.name:: +
+ ::if m.userName2 != null:: + ::_("alternately with")::
+ ::m.userName2:: + ::set total = 0:: + ::set productNum = 0:: + ::end:: +
+ ::if m.userName2 != null:: + ::set u = getUser(m.userId2): + ::if u !=null:: + $$nullSafe(::u.phone::) + ::end:: + ::end:: + + ::_("Delivery of the"):: ::hDate(date):: +
" :: + + ::raw "":: + ::raw tableHeader:: + ::end:: + + ::if m.userName != name || m.userName2 != name2:: + ::set class="name":: + ::set currentContract = "":: + + ::else:: + ::set class="":: + ::end:: + + ::if currentContract != short(m.contractName,40) :: + ::if currentContract == "" :: + ::set td_style="font-size:" + fontRatio + "%" :: + ::else:: + ::set td_style="font-size:" + fontRatio + "%;border-top: 2px solid #AAA;" :: + ::end:: + + ::set currentContract = short(m.contractName,40):: + ::else:: + + ::end:: + + + + + + + + + + ::set name = m.userName :: + ::set name2 = m.userName2 :: + + + ::set next = orders[repeat.m.index+1]:: + ::if next==null || next.userName!=name || next.userName2!=name2:: + + + + + + + + + ::end:: + ::end:: + +

::raw nl2br(user.getAmap().txtDistrib)::

+ + + + + +
+ ::short(m.contractName,40):: + + ::if(m.quantity==0 && m.canceled):: + ::_("Canceled"):: + ::else:: + ::raw m.smartQt:: + ::end:: + ::set productNum = productNum+m.quantity:: + + ::m.productName:: + + ::formatNum(m.productPrice):: ::currency():: + + ::if m.fees!=null:: + ::formatNum(m.fees):: ::currency():: + ::end:: + + ::formatNum(m.total):: ::currency():: + ::set total = total + m.total:: +
::formatNum(productNum)::::_("TOTAL")::::formatNum(total):: ::currency()::
+ ::foreach r sqlLog:: + + + + + + + + ::end:: +
::r.t::ms::r.length::ExpStack::r.sql::
+ + + + diff --git a/lang/master/tpl/distribution/listByDateShort.mtt b/lang/master/tpl/distribution/listByDateShort.mtt new file mode 100644 index 0000000..a2362a2 --- /dev/null +++ b/lang/master/tpl/distribution/listByDateShort.mtt @@ -0,0 +1,115 @@ + + + + + ::_("Attendance sheet"):: + + + + +

::user._amap.name::

+

Livraison du ::hDate(date)::

+ + + + + ::set name = "":: + ::set name2 = "":: + ::set total = 0:: + ::set productNum = 0:: + + + + + + + + + + ::foreach m orders:: + + + ::if m.userName != name || m.userName2 != name2:: + ::set class="name":: + ::else:: + ::set class="":: + ::end:: + + + + + ::set name = m.userName :: + ::set name2 = m.userName2 :: + + + + + + + ::set next = orders[repeat.m.index+1]:: + ::end:: + +
::_("Name")::::_("Contact details")::::_("Qty")::::_("Product")::::_("Signature")::
+ ::if m.userName != name || m.userName2 != name2:: + + ::set basket = getBasket(m.userId,place.id,date):: + N°::basket.num:: - + + ::m.userName:: + + ::if m.userName2 != null:: +
::_("alternately with"):: ::m.userName2:: + ::end:: + ::set total = 0:: + ::set productNum = 0:: + ::end:: + +
+ ::if m.userName != name || m.userName2 != name2:: + ::set u = getUser(m.userId): + ::if u !=null:: + $$nullSafe(::u.phone::) + ::end:: + + ::if m.userName2 != null:: + ::set u = getUser(m.userId2): + ::if u !=null:: +
$$nullSafe(::u.phone::) + ::end:: + ::end:: + ::end:: + +
+ ::if(m.quantity==0 && m.canceled):: + ::_("Canceled"):: + ::else:: + ::raw m.smartQt:: + ::end:: + ::set productNum = productNum+m.quantity:: + + ::m.productName:: +
+ +

::raw nl2br(user.getAmap().txtDistrib)::

+ + + + + + + ::foreach r sqlLog:: + + + + + + + + ::end:: +
::r.t::ms::r.length::ExpStack::r.sql::
+ + + + diff --git a/lang/master/tpl/distribution/listByProductUser.mtt b/lang/master/tpl/distribution/listByProductUser.mtt new file mode 100644 index 0000000..574bfe1 --- /dev/null +++ b/lang/master/tpl/distribution/listByProductUser.mtt @@ -0,0 +1,91 @@ + + + + + ::_("Attendance sheet"):: + + + +

+ ::contract._amap.name:: - + ::__("Distribution of the ::date::, from ::from:: to ::to::",{date:dDate(distrib.date),from:hHour(distrib.date),to:hHour(distrib.end)}):: +

+

+ ::distrib._contract.name:: - ::distrib._contract._vendor.name:: +

+ +

+ ::_("Place"):: : + ::distrib._place.toString()::, ::distrib._place.getAddress():: +

+

+ ::set c = contract._contact:: + ::set v = contract._vendor:: + ::_("Contact in charge of the contract") : ::c.firstName:: ::c.lastName::, ::c.email::, ::c.phone:: +

+

+ ::_("Farmer"):: : + ::v.name:: , + ::if v.email!=null :: ::v.email:: ::end::, + ::if v.phone!=null :: ::v.phone:: ::end:: +

+

+ ::_("Members on duty"):: : + ::if distrib._distributor1!=null:: ::distrib._distributor1.getName()::, ::end:: + ::if distrib._distributor2!=null:: ::distrib._distributor2.getName()::, ::end:: + ::if distrib._distributor3!=null:: ::distrib._distributor3.getName()::, ::end:: + ::if distrib._distributor4!=null:: ::distrib._distributor4.getName():: ::end:: +

+ + + + + ::foreach p products:: + + ::end:: + + + ::foreach user users:: + + + ::foreach p products:: + + ::end:: + + + ::end:: + + + ::foreach p products:: + + ::end:: + +
+ ::p.name::
+ ::p.ref:: +
TOTAL
+ ::set basket = getBasket(user.id,distrib._place.id,distrib.date):: + N°::basket.num:: - + + ::user.getCoupleName():: + + + ::set o = orders.get(user.id).get(p.id):: + + ::if(o!=null):: + ::raw o.smartQt:: + ::end:: + ::formatNum(totalByUser(user.id)):: ::currency()::
TOTAL + ::set q = totalByProduct(p.id):: + ::if(p.qt!=null && (p.hasFloatQt || p.variablePrice) ):: + ::q * p.qt:: ::unit(p._unitType,false):: + ::else:: + ::q:: + ::end:: +
+ +

::raw nl2br(distrib._contract._amap.txtDistrib)::

+ + + + diff --git a/lang/master/tpl/distribution/planning.mtt b/lang/master/tpl/distribution/planning.mtt new file mode 100644 index 0000000..cc39240 --- /dev/null +++ b/lang/master/tpl/distribution/planning.mtt @@ -0,0 +1,110 @@ +::use 'design.mtt':: +
+
+

::_("Planning of deliveries:"):: ::contract.name::

+ +

+ ::raw _("Subscribe now by selecting checkboxes corresponding to dates which are convenient for you. The goal of attendances consists in helping during the delivery of products."):: +

+ +
+ + + + ::foreach d distribs:: + ::set date = getDate(d.date):: + + ::end:: + + + + + + + + ::set me = doodle.get(user.id):: + ::foreach d distribs:: + + ::end:: + + + + + ::foreach doo doodle:: + + + + ::foreach d distribs:: + + ::end:: + + ::end:: + + +
::date.dow::
+ ::date.d::
+ ::date.m::
+ ::date.h+":"+date.i::
+ ::user.getCoupleName():: + + + ::if me !=null:: + ::set a = me.planning.get(d.id):: + ::if a!=null:: + + ::else:: + ::if !d.hasEnoughDistributors():: + + ::else:: + ::_("full"):: + ::end:: + ::end:: + ::else:: + ::if !d.hasEnoughDistributors():: + + ::else:: + ::_("full"):: + ::end:: + + ::end:: + +
::doo.user.getCoupleName():: + ::set a = doo.planning.get(d.id):: + + ::if a!=null:: + + ::end:: +
+
+ + + +
+
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/distribution/validate.mtt b/lang/master/tpl/distribution/validate.mtt new file mode 100644 index 0000000..5046099 --- /dev/null +++ b/lang/master/tpl/distribution/validate.mtt @@ -0,0 +1,92 @@ +::use 'design.mtt':: + + + + + +
+
+ +
+

+ ::_("Validate the delivery of the"):: ::hDate(date):: +

+

::place:: +

+ +

+ ::if(confirmed):: +

+ + ::_("Validated"):: +
+ ::else:: +
+ + ::_("Not validated"):: +
+ ::end:: +

+
+ + +

+ + ::_("Back to the summary sheet of orders"):: +

+ + + + + ::foreach u users:: + + ::set basket = getBasket(u.id,place.id,date):: + + ::if(basket.isValidated()==true):: + ::set class="success":: + ::else:: + ::set class="danger":: + ::end:: + + + + + + + + ::end:: + + + + +
+
+ ::_("Num"):: ::basket.num:: +
+
+ ::u.getCoupleName():: + + + ::if(class=="danger"):: + + + ::_("To be validated"):: + + ::else:: + + + ::_("Validated"):: + + ::end:: +
+
+ +
+ +::end:: diff --git a/lang/master/tpl/empty.mtt b/lang/master/tpl/empty.mtt new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/lang/master/tpl/empty.mtt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lang/master/tpl/error.mtt b/lang/master/tpl/error.mtt new file mode 100644 index 0000000..5bf07ae --- /dev/null +++ b/lang/master/tpl/error.mtt @@ -0,0 +1,31 @@ + + + + + + +
+
+
+

::_("Ouch !")::

+

+ ::_("An error occurred."):: +

+

+ ::_("If the website is unavailable, please try again in few minutes."):: +
+ ::_("If this happen repeatedly, please let us know so we can solve the issue."):: +

+ +

::_("Technical description of the error")::

+ +
::message::
+ + ::if stack:: +

Stack

+
::stack::
+ ::end:: +
+
+ + \ No newline at end of file diff --git a/lang/master/tpl/form.mtt b/lang/master/tpl/form.mtt new file mode 100644 index 0000000..8f265a8 --- /dev/null +++ b/lang/master/tpl/form.mtt @@ -0,0 +1,14 @@ +::use 'design.mtt':: +
+
+

::title::

+

+ ::raw text:: +

+ + ::raw form:: + +
+
+ +::end:: diff --git a/lang/master/tpl/group/map.mtt b/lang/master/tpl/group/map.mtt new file mode 100644 index 0000000..9455ec9 --- /dev/null +++ b/lang/master/tpl/group/map.mtt @@ -0,0 +1,13 @@ +::use 'base.mtt':: + + + +
+ + + +::end:: diff --git a/lang/master/tpl/group/place.mtt b/lang/master/tpl/group/place.mtt new file mode 100644 index 0000000..b3580cb --- /dev/null +++ b/lang/master/tpl/group/place.mtt @@ -0,0 +1,17 @@ +
+
+ + +
+
+
+
+ + ::place.name::
+ $$nullSafe(::place.address1::)
+ $$nullSafe(::place.address2::)
+ $$nullSafe(::place.zipCode::) $$nullSafe(::place.city::) + +
+ +
\ No newline at end of file diff --git a/lang/master/tpl/group/view.mtt b/lang/master/tpl/group/view.mtt new file mode 100644 index 0000000..1edf17f --- /dev/null +++ b/lang/master/tpl/group/view.mtt @@ -0,0 +1,195 @@ +::use 'base.mtt':: + + +
+ + +
+
+ ::foreach notification getMessages():: +
+ ::if notification.error:: + + ::else:: + + ::end:: + + ::raw notification.text:: +
+ ::end:: +
+
+ +
+ +
+
+

+ + ::if group._image!=null:: + + ::if group.extUrl!=null && group.extUrl!="":: +
+ ::else:: +
+ ::end:: + + ::end:: +

+ +

+ + ::if isMember:: + + + + ::_("Go to my account"):: + + + ::else:: + +

Inscriptions :

+ ::switch group._regOption:: + + ::case:: +

+ ::_("Contact the person in charge in order to subscribe to this group and get to know membership conditions."):: +

+

+ + + ::_("Membership request"):: + +

+ ::case:: +

+ ::_("This group accepts subscriptions on waiting list. A coordinator will contact you."):: +

+

+ + + ::_("Subscription on waiting list"):: + +

+ + ::case:: +

+ + + ::_("Free subscription"):: + +

+ ::case:: +

+ ::_("This group is full and do not accept any more subscription."):: +

+ ::end:: + + ::end:: + + +

+ + ::if user==null:: +
+

::_("Already member? Log in:")::

+ + ::end:: + +
+
+ +

+ ::group.name:: +

+ +

+ ::if group.txtIntro!=null && group.txtIntro!="":: + ::raw nl2br(group.txtIntro):: + ::end:: +

+ + +

+

::_("Next deliveries")::

+ + ::foreach d group.getDeliveries(4):: + +
+ ::set s = getDate( d.date ):: + ::if isToday(d.date):: + $$today(::s.dow::,::s.d::,::s.m::,::s.y::,::s.h::,::d._place::) + ::else:: + $$date(::s.dow::,::s.d::,::s.m::,::s.y::,::d._place::) + ::end:: +
+ + ::end:: +

+ +
+ +

+

::_("Contact")::

+ + + ::group._contact.firstName:: ::group._contact.lastName::
+ + + ::group._contact.email::
+
+ + ::group._contact.phone::
+
+

+ +
+ +

+

::_("Suppliers and products")::

+

+ ::_("Non-complete extract of available products:"):: +

+ + ::foreach c contracts:: +

+ ::c.name::
+ ::c._vendor.name::, $$nullSafe(::c._vendor.zipCode::) $$nullSafe(::c._vendor.city::) +

+ ::foreach p c.getProductsPreview(6):: +
+
+
+ ::end:: +
+

+ ::end:: + +

+ +
+
+ + + + + +
+ +
+ + +
+ +
+ + + +::end:: diff --git a/lang/master/tpl/home.mtt b/lang/master/tpl/home.mtt new file mode 100644 index 0000000..48df434 --- /dev/null +++ b/lang/master/tpl/home.mtt @@ -0,0 +1,353 @@ +::use 'design.mtt':: + +::if user==null:: +
+::end:: + +
+ + + ::if newGroup:: +
+

::_("Welcome to Cagette.net !")::

+

+ ::raw _("Congratulations, you just created a new group !
As an example, we created a fake farmer and fake products.
It will show you how things work together in Cagette.net.
Of course, you'll be able to delete them."):: +

+
+

+ ::raw _("As a starter, we recommand you to follow the guided tour to discover step by step the main sections of the software."):: +

+

+ ::_("Start the guided tour"):: +

+ +
+ ::end:: + + + ::if !amap.hasShopMode():: +
0 )::> + +

::_("Open orders")::

+
    + ::foreach c openContracts:: +
  • ::c.name::
  • + ::end:: +
+
+ ::end:: + + + ::if distribs==null || count(distribs)==0:: +
+ + ::_("You delivery planning is currently empty||on homepage when there is no planned deliveries"):: +
+
+ ::else:: + + + ::foreach d distribs:: + + ::if d.userHasOrders(user):: + + +
+ + ::set place = d.getPlace():: + ::set active = d.isActive():: + ::set start = d.getDate():: + ::set end = d.getEndDate():: + ::set ordersStartDate = d.getOrdersStartDate():: + ::set ordersEndDate = d.getOrdersEndDate():: +
+ +
+ ::set s = getDate(start):: + ::if isToday(start):: + $$today(::s.dow::,::s.d::,::s.m::,::s.y::,::s.h::,null) + ::else:: + $$date(::s.dow::,::s.d::,::s.m::,::s.y::,null) + ::end:: +
+ +
+ ::if active:: + + ::_("Modify my order"):: + + + ::_("Add products"):: + + ::elseif(ordersEndDate!=null):: + + ::if Date.now().getTime() > ordersEndDate.getTime():: + ::_("Order closed since")::
+ ::hDate(ordersEndDate):: + ::else:: + ::_("Order will open on")::
+ ::hDate(ordersStartDate):: + ::end:: + ::end:: +
+ +
+ $$place(::place::) +
+
+ +
::_("My ordered products"):: :
+ +
+ ::foreach dist d.distributions:: + ::set orders = prepare(dist.getUserOrders(user)):: + ::if orders.length>0:: +

+ ::set s = getDate(dist.date) :: + ::set e = getDate(dist.end):: + + ::s.h+":"+s.i:: - ::e.h+":"+e.i:: +  ::_("Distribution"):: ::dist._contract.name:: +

+ +
+ ::foreach c orders:: + +
0:: > + + + + + +
+
+
+ 1:: >::raw c.smartQt:: + + ::_("Canceled"):: + + ::c.productName:: + + ::if c.userId2!=null:: + ::if user.id==c.userId:: + ::set you = c.userName:: + ::set mate = c.userName2:: + ::else:: + ::set mate = c.userName:: + ::set you = c.userName2:: + ::end:: + +
( + + ::raw __("alternated with ::mate::",{mate:mate})::, + + ::if getWhosTurn(c.id,dist)==false:: + + ::if c.userName == you:: + ::_("It's your turn"):: + ::else:: + ::_("It's his/her turn"):: + ::end:: + ::else:: + ::if c.userName2 == you:: + ::_("It's your turn"):: + ::else:: + ::_("It's his/her turn"):: + ::end:: + ::end:: + ) + ::end:: +
+
+ + ::end:: +
+ + + ::end:: + + ::end:: + + +
+ + ::raw d.extraHtml:: +
+ + +
+ + +
+ + + + ::elseif(amap.hasShopMode()):: + + + + + +
+ ::set place = d.getPlace():: + ::set active = d.isActive():: + ::set start = d.getDate():: + ::set end = d.getEndDate():: + ::set ordersStartDate = d.getOrdersStartDate():: + ::set ordersEndDate = d.getOrdersEndDate():: + + ::set class ="":: + ::if !active:: + ::set class="disabled":: + ::end:: + +
+ +
+ ::if active==true:: + + + ::_("Order||Order button on homepage"):: + +
+ + ::elseif(ordersEndDate!=null):: + + ::if Date.now().getTime() > ordersEndDate.getTime():: + ::_("Order closed since")::
+ ::hDate(ordersEndDate):: + ::else:: + ::_("Order will open on")::
+ ::hDate(ordersStartDate):: + ::end:: + ::end:: + +
+ +
+ ::set s = getDate(start):: + ::if isToday(start):: + $$today(::s.dow::,::s.d::,::s.m::,::s.y::,::s.h::,null) + ::else:: + $$date(::s.dow::,::s.d::,::s.m::,::s.y::,null) + ::end:: +
+ +
+ ::set e = getDate(end):: + + ::set start = s.h+":"+s.i:: + ::set end = e.h+":"+e.i:: + + ::__("Delivery from ::start:: to ::end::||Delivery from 18:00 to 19:30",{start:start,end:end}):: +
+ $$place(::place::) +
+
+ + +
+ + ::foreach p d.getProductsExcerpt():: +
+ ::end:: + + +
+ ::raw d.extraHtml:: +
+
+ + +
+ + ::end:: + + ::end:: + + ::end:: + + +
+ + + +
+ +
+ + ::if amap._image!=null:: + ::if amap.extUrl!=null && amap.extUrl!="":: +
+ ::else:: +
+ ::end:: + ::end:: + + ::if amap.txtHome!=null && amap.txtHome!="":: + ::raw nl2br(amap.txtHome):: + ::end:: + +
+ +
0 ::> +

::_("Permanences")::

+

::_("Don't forget to register for permanences")::

+
    + ::foreach c contractsWithDistributors:: +
  • ::c.name::
  • + ::end:: +
+
+ +
+

+ ::_("Warning, you don't have defined a password yet !"):: +

+ ::_("Set my password"):: + +
+ + + ::if blocks!=null:: + ::foreach b blocks:: +
+

::b.title::

+

::raw b.html::

+
+ ::end:: + ::end:: +
+::end:: diff --git a/lang/master/tpl/install/default.mtt b/lang/master/tpl/install/default.mtt new file mode 100644 index 0000000..576ec2d --- /dev/null +++ b/lang/master/tpl/install/default.mtt @@ -0,0 +1,35 @@ +::use 'design.mtt':: +
+
+ +

::_("Update")::

+ + + + ::foreach s status:: + + + + + + + + ::end:: + + +
+ ::s.parameter:: + + $$bool(::s.valid::) + + ::s.message:: + + ::if !s.valid:: + ::_("Update"):: + ::end:: +
+ + +
+
+::end:: \ No newline at end of file diff --git a/lang/master/tpl/macros.mtt b/lang/master/tpl/macros.mtt new file mode 100644 index 0000000..18438e9 --- /dev/null +++ b/lang/master/tpl/macros.mtt @@ -0,0 +1,312 @@ + + + confirm(::if text==null::'Confirmer cette action ?'::else::'::escapeJS(text)::'::end::) + + + + + ::if id == null && def != null:: + ::def:: + ::else:: + /file/::makeFileSign(id)::.jpg + ::end:: + + + + + $$browseCustom(::b::,::url::,Page ::b.page:: ::if b.pages::/::b.pages:: ::end::) + + + < + ::if b.prev::::_("Previous")::::else:: ::_("Previous"):: ::end:: + | ::custom:: | + ::if b.next::::_("Next")::::else:: ::_("Next"):: ::end:: > + + + ::if text==null::::else::::text::::end:: + + + + + +  ::text:: + + + + + + + +  ::text:: + + + + + + + ::if text!=null && text!="":: +  ::text:: + ::end:: + + + + + ::_("Excel/CSV Export"):: + + + + ::if v==true:: + + ::else:: + + ::end:: + + + + ::if(v==true || v==1):: + + ::else:: + + ::end:: + + + + +
+
+
::dow::
+
::d::
+
::m::
+
+ ::if place!=null:: +
+ + ::place:: +
+ ::end:: +
+
+ + + + ::place:: +
+ $$nullSafe(::place.address1::) + $$nullSafe(::place.address2::) + $$nullSafe(::place.zipCode::) + $$nullSafe(::place.city::) +
+
+ + +
+ +
+ ::if Std.parseInt(h)<=12:: + ::_("THIS MORNING"):: + ::elseif Std.parseInt(h)<18:: + CET
::_("AFTERNOON")::
+ ::else:: + ::_("THIS EVENING"):: + ::end:: + +
+ ::if place!=null:: +
+ + ::place:: +
+ ::end:: +
+
+ + + + + + ::if u!=null:: + ::u.firstName:: ::u.lastName::
+ + ::u.email::
+
+ + ::u.phone::
+
+ ::else:: + ::_("No contact"):: + ::end:: + +
+ + + +
+ + + ::raw msg:: +
+
+ + + + ::set x = t.getTypeIndex():: + + + ::if x==0:: +  ::_("Order"):: + ::elseif x==1:: +  ::raw _("CSA contract"):: + ::elseif x==2:: + + ::_("Payment"):: ::t.getPaymentTypeName():: + ::else:: + ::_("Subscription"):: + ::end:: + + + + ::if t.pending==true:: + + ::else:: + + ::end:: + + + + ::t.name::
+ + ::if x==2 && t.pending==true:: + ::if(t.getPaymentType()=="check"):: + ::_("Check to be given at the collection of products"):: + ::elseif(t.getPaymentType()=="cash"):: + ::_("Cash to be given at the collection of products"):: + ::elseif(t.getPaymentType()=="transfer"):: + ::_("This transfer must be validated by a coordinator"):: + ::else:: + ::t.name::
+ ::hDate(t.date):: + ::end:: + + ::else:: + ::hDate(t.date):: + ::end:: + + + ::if(t.amount<0):: + - + ::end:: + + ::formatNum(Math.abs(t.amount)):: ::currency():: + ::set class="":: +
+ + + ::_("Back"):: + + + + + ::set now = Date.now():: +
+
+ + ::set open = 0:: + ::set notyetopened = 0:: + ::set closed = 0:: + + ::foreach d k:: + ::if d.orderStartDate!=null:: + ::if now.getTime() > d.orderStartDate.getTime() && now.getTime() < d.orderEndDate.getTime():: + + ::set open = open+1:: + ::elseif now.getTime() > d.orderEndDate.getTime():: + + ::set closed = closed+1:: + ::else:: + + ::set notyetopened = notyetopened+1:: + ::end:: + ::else:: + + ::set closed = closed+1:: + ::end:: + ::end:: + +
+ ::set x = getDate(k[0].date):: + $$date(::x.dow::,::x.d::,::x.m::,::x.y::,::k[0]._place::) +
+ +
::x.h+":"+x.i::
+ +
+
+ + ::if (open==k.length):: + + + + + ::elseif(closed==k.length):: + + + + + ::elseif(notyetopened==k.length):: + + + + + ::else:: + + + + + ::end:: + + ::k[0]._contract._amap.name::
+ + + ::foreach d k:: + + + + + ::end:: +
+ ::if d.orderStartDate!=null:: + ::if now.getTime() > d.orderStartDate.getTime() && now.getTime() < d.orderEndDate.getTime():: + + + ::elseif now.getTime() > d.orderEndDate.getTime():: + + + ::else:: + + + ::end:: + ::else:: + + + ::end:: + + ::set c = getCatalog(d):: + ::c.name::   + + ::if showBtns:: + + ::end:: +
+ +
+
+
+ +
\ No newline at end of file diff --git a/lang/master/tpl/mail/design.mtt b/lang/master/tpl/mail/design.mtt new file mode 100644 index 0000000..89c41f9 --- /dev/null +++ b/lang/master/tpl/mail/design.mtt @@ -0,0 +1,40 @@ + + + +
+ ::raw __content__:: +
+ +
+ +
+ ::_("This e-mail was sent from"):: ::_("Cagette.net")::. + + ::if group!=null:: +
+ ::_("You receive this message because you are part of"):: ::group.name:: + ::end:: + + ::if list!=null:: + ( ::list:: ) + ::end:: + + + ::if group!=null:: + + ::end:: + +
\ No newline at end of file diff --git a/lang/master/tpl/mail/forgottenPassword.mtt b/lang/master/tpl/mail/forgottenPassword.mtt new file mode 100644 index 0000000..08dacd9 --- /dev/null +++ b/lang/master/tpl/mail/forgottenPassword.mtt @@ -0,0 +1,16 @@ +:: use 'mail/design.mtt':: + +

+ ::_("Hello"):: ::user.firstName::, +

+

+ ::_("You asked to modify your password,"):: + ::_("Click here to perform this operation")::. +

+

+ ::_("Be careful, this link is valid only 24 hours for security reasons."):: +

+ +::end:: + + diff --git a/lang/master/tpl/mail/invitation.mtt b/lang/master/tpl/mail/invitation.mtt new file mode 100644 index 0000000..d6b4f32 --- /dev/null +++ b/lang/master/tpl/mail/invitation.mtt @@ -0,0 +1,45 @@ +:: use 'mail/design.mtt':: + +
+ + + +

+ ::_("Hello"):: $$nullSafe(::name::), +

+ +

+ ::if groupName!=null:: + + ::raw __("As a member of ::groupName::",{groupName:groupName}):: +
+ ::_("you have been pre-registered on Cagette.net"):: + + ::else:: + + ::_("you have been pre-registered on Cagette.net"):: + + ::end:: +

+ +

+ ::_("This software makes the organisation of the group easy by informing you about the next delivery dates, orders and current contracts"):: +

+

+ ::_("In order to finalize the subscription, please choose your password"):: +

+

+ ::_("You will then be able to connect anytime by using your e-mail"):: + ( ::email:: ::if email2!=null:: ou ::email2:: ::end:: ) + ::_("and your password on the website"):: ::HOST:: + + + » ::_("Finalize your subscription"):: + + +

+ +
+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/mail/message.mtt b/lang/master/tpl/mail/message.mtt new file mode 100644 index 0000000..3a80a5c --- /dev/null +++ b/lang/master/tpl/mail/message.mtt @@ -0,0 +1,8 @@ +:: use 'mail/design.mtt':: + + +::raw text:: + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/mail/newPasswordConfirmed.mtt b/lang/master/tpl/mail/newPasswordConfirmed.mtt new file mode 100644 index 0000000..a2f0520 --- /dev/null +++ b/lang/master/tpl/mail/newPasswordConfirmed.mtt @@ -0,0 +1,17 @@ +:: use 'mail/design.mtt':: + +

+ ::_("Hello"):: ::user.firstName::, +

+

+ ::__("A new password has been defined for ::site::",{site:NAME}):: +
+ ::raw __("The new password is ::pass::, don't forget it !",{pass:password}):: +

+

+ ::raw __("You can use the following emails to log into your account : ::emails::",{emails:emails}):: +

+ +::end:: + + diff --git a/lang/master/tpl/mail/orderSummaryForMember.mtt b/lang/master/tpl/mail/orderSummaryForMember.mtt new file mode 100644 index 0000000..e871a00 --- /dev/null +++ b/lang/master/tpl/mail/orderSummaryForMember.mtt @@ -0,0 +1,74 @@ +::use 'mail/design.mtt':: + +

Récapitulatif de votre commande "::contract.name::"

+ + + + + + + + + + +
+ Date de livraison + + ::dDate(distribution.date):: de ::hHour(distribution.date):: à ::hHour(distribution.end):: +
Adresse + ::distribution._place.getFullAddress():: +
+ +
+
+ + + + + + + + + + + + ::set total = 0:: + ::foreach m orders:: + + + + + + + + + + ::end:: + + + + + + +
QtéProduitP.USous-totalFraisTotal
+ ::raw m.smartQt: + + ::m.productName:: + + ::formatNum(m.productPrice):: ::currency():: + + ::formatNum(m.subTotal):: ::currency():: + + + ::if m.percentageValue!=null:: + + ::formatNum(m.fees):: ::currency():: + + ::end:: + + + ::formatNum(m.total):: ::currency():: + ::set total = total + m.total:: +
Total :::formatNum(total):: ::currency()::
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/mail/ordersByProduct.mtt b/lang/master/tpl/mail/ordersByProduct.mtt new file mode 100644 index 0000000..03904be --- /dev/null +++ b/lang/master/tpl/mail/ordersByProduct.mtt @@ -0,0 +1,69 @@ +::use 'mail/design.mtt':: + +

Récapitulatif de commande par produits

+ +

+ Les commandes du contrat ::contract.name:: dont vous êtes coordinateur ont fermé le ::hDate(distribution.orderEndDate)::. +

+ + + + + + + + + + +
+ Date de livraison + + ::dDate(distribution.date):: de ::hHour(distribution.date):: à ::hHour(distribution.end):: +
Adresse + ::distribution._place.getFullAddress():: +
+ +
+
+ + + + + + + + + + + + ::set total = 0:: + ::foreach m orders:: + + + + + + + + + ::end:: + + + + + + +
Poids/Vol.QuantitésProduitRéférencePrix unitaire TTCTotal
::raw m.weightOrVolume:: + ::raw m.quantity:: + + ::m.pname:: + + $$nullSafe(::m.ref::) + + ::formatNum(m.priceTTC):: ::currency():: + + ::formatNum(m.totalTTC):: ::currency():: + ::set total = total + m.totalTTC:: +
Total :::formatNum(total):: ::currency()::
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/maintain.mtt b/lang/master/tpl/maintain.mtt new file mode 100644 index 0000000..af51940 --- /dev/null +++ b/lang/master/tpl/maintain.mtt @@ -0,0 +1,17 @@ +::use 'design.mtt':: +
+

+ + ::_("Maintenance")::

+

+ Le site est actuellement en maintenance.
+ Revenez dans quelques instants. +

+

+ The website is currently under maintenance.
+ Please come back later. +

+ +
+ +::end:: diff --git a/lang/master/tpl/member/balance.mtt b/lang/master/tpl/member/balance.mtt new file mode 100644 index 0000000..8a82b04 --- /dev/null +++ b/lang/master/tpl/member/balance.mtt @@ -0,0 +1,63 @@ +::use 'design.mtt':: +
+

::_("Balance of members")::

+
+ +
+
+

::_("Balanced")::

+ + ::foreach ua balanced:: + + + + + ::end:: +
+ ::ua._user.getName():: + + ::ua.balance:: ::currency():: +
+ +
+
+
+
+

::_("In debt")::

+ + ::foreach ua debt:: + + + + + ::end:: +
+ ::ua._user.getName():: + + ::ua.balance:: ::currency():: +
+ +
+
+
+
+

::_("In credit")::

+ + ::foreach ua credit:: + + + + + ::end:: +
+ ::ua._user.getName():: + + ::ua.balance:: ::currency():: +
+ +
+
+ + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/member/default.mtt b/lang/master/tpl/member/default.mtt new file mode 100644 index 0000000..8ed97b1 --- /dev/null +++ b/lang/master/tpl/member/default.mtt @@ -0,0 +1,164 @@ +::use 'design.mtt':: +
+
+ ::if select!=null:: +

::_("Selection :||show a sublist of members which has been selected"):: + ::if select=="nocontract":: + ::_("Members without any contracts"):: + ::elseif select=="nomembership":: + ::_("Members whose membership should be renewed"):: + ::elseif select=="newusers":: + ::_("Members who has never logged in"):: + ::elseif select=="contract":: + ::_("Members with orders or contracts"):: + ::end:: +

+ +

::members.current().length:: ::_("members")::.

+ + ::_("Back to main page"):: + ::elseif search!=null:: +

::_("Search"):: : ::search::

+ ::_("Back to main page"):: + + ::else:: +

::_("Members")::

+ ::end:: + + + ::if select=="newusers":: +
+

+ ::_("You can invite by email all the members who never logged in, in one single clic."):: +

+

+ ::_("Send invitations"):: +

+ ::end:: + + + ::foreach m members.current():: + + + + + + ::end:: + + + + + +
+ + ::m.lastName:: ::m.firstName:: + ::if m.lastName2!=null:: + & ::m.lastName2:: ::m.firstName2:: + ::end:: + + + ::if m.address1!=null:: + ::m.address1:: + ::end:: + ::if m.address2!=null:: + ::m.address2:: + ::end:: + ::if m.zipCode!=null:: + ::m.zipCode:: + ::end:: + ::if m.city!=null:: + ::m.city:: + ::end:: +
+ +
    + ::if members.paginationStartPage != 1:: +
  • + + + +
  • + ::end:: + ::foreach i loopList(members.paginationStartPage,members.paginationEndPage):: + ::if members.page==i:: +
  • ::i::
  • + ::else:: +
  • ::i::
  • + ::end:: + ::end:: + ::if members.paginationEndPage < members.pages + 1:: +
  • + + + +
  • + ::end:: +
+
+ +
+ + ::_("New member"):: + $$export(::Web.getURI()+"?"+Web.getParamsString()::&csv=1) + ::_("Import members"):: + +
+ + +
+
+ +
+ + +
+

::_("Selections")::

+ + + +
+ + +
+

::_("Waiting list")::

+

+ ::raw __("There is ::waitingList:: people on waiting list.",{waitingList:waitingList}):: +
+ ::_("Display the list"):: +

+
+ + + ::if select!="newusers":: +
+

::_("New members")::

+ ::raw __("There is ::newUsers:: members who never logged in",{newUsers:newUsers}):: +
+ ::_("Display the list"):: +
+ ::end:: + +
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/member/import.mtt b/lang/master/tpl/member/import.mtt new file mode 100644 index 0000000..1171b81 --- /dev/null +++ b/lang/master/tpl/member/import.mtt @@ -0,0 +1,118 @@ +::use 'design.mtt':: + +
+
+

::_("Import")::

+ + + ::if step==1:: +
+

+ +

+ +
+ ::elseif step==2:: +

::_("Users to import")::

+
+ ::if (data!=null && data.length>0) || (data2!=null && data2.length>0):: + + + + + + + + + + + + + + + + ::foreach d data:: + + ::foreach r d:: + + ::end:: + + ::end:: +
::_("First name")::::_("Last name")::::_("Email")::::_("Phone")::::_("Partner first name")::::_("Partner last name")::::_("Partner email")::::_("phone")::::_("Address 1")::::_("Address 2")::::_("zip code")::::_("City")::
+ ::if r!=null:: ::r:: ::end:: +
+ +

::_("Existing accounts")::

+ + + + + + + + + + + + + + + + ::foreach d data2:: + + ::foreach r d:: + + ::end:: + + ::end:: +
::_("First name")::::_("Last name")::::_("Email")::::_("Phone")::::_("Partner first name")::::_("Partner last name")::::_("Partner email")::::_("phone")::::_("Address 1")::::_("Address 2")::::_("zip code")::::_("City")::
+ ::if r!=null:: ::r:: ::end:: +
+ +
+ ::_("Warning"):: +

::_("Before validating this import, please check that this table est correctly filled.
If some datas are shifted or are not linked to the right column, please check your CSV file."):: +

+ ::_("Try again"):: +
+ ::_("Finalize import"):: + ::else:: +
+ ::_("Oups !"):: +

::_("It looks like there is no data to import. Please check your CSV file or check that the users you want to import are not already registred.")::

+ ::_("Try again"):: +
+ + ::end:: +
+ + + ::elseif step==3:: + ::if numImported>0:: + +
+ ::raw __("Congratulations ! You just imported ::numImported:: users in your group.",{numImported:numImported}):: +
+

+ ::raw _("You may now consider inviting them to log into Cagette.net"):: +

+ ::else:: +

::_("You did'nt import anything in your database because your file was empty, or contained already registred users")::

+ ::end:: + ::_("Go back to the main page"):: + + + ::end:: + +
+ +
+

::_("Help")::

+ ::raw _("Here you can import a large amount of members in your database from a simple Excel or Libre Office file.
  • The first step is to download this demo file.
  • Fill out the table, of modify your existing file to striclty match the columns order.
  • Once your file is completed, click on \"save as\" in your spreadsheet software, and select \"CSV\" format
  • Select the following options : separator : coma or dot-coma, encoding : Unicode UTF-8
  • Save the file
  • finally, click on \"select a file\" on this page and upload it.
"):: +
+ + +
+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/member/lastMessages.mtt b/lang/master/tpl/member/lastMessages.mtt new file mode 100644 index 0000000..d465423 --- /dev/null +++ b/lang/master/tpl/member/lastMessages.mtt @@ -0,0 +1,30 @@ + + + + + + + + ::foreach m emails:: + + ::if m.failure!=null:: + ::set class = "danger":: + ::else:: + ::set class = "success":: + ::end:: + + + + + + + + + +::end::
::_("Date")::::_("Subject")::::_("Status")::
::hDate(m.date)::::m.subject:: + ::if m.failure!=null:: + ::m.failure:: + ::else:: + ::m.success:: + ::end:: +
\ No newline at end of file diff --git a/lang/master/tpl/member/payments.mtt b/lang/master/tpl/member/payments.mtt new file mode 100644 index 0000000..4e57a59 --- /dev/null +++ b/lang/master/tpl/member/payments.mtt @@ -0,0 +1,121 @@ +::use 'design.mtt':: + +
+

::_("Contact informations")::

+
+ +
::member.firstName:: ::member.lastName:: + ::if member.firstName2!=null && member.lastName2!=null:: + & ::member.firstName2:: ::member.lastName2:: + + ::end::
+ + $$nullSafe(::member.address1::)
+ $$nullSafe(::member.address2::)
+ $$nullSafe(::member.zipCode::) + $$nullSafe(::member.city::)
+
+ + ::if member.phone!=null:: + ::_("Phone"):: : ::member.phone::
+ ::end:: + + ::if member.email!=null:: + ::_("Email"):: : ::member.email::
+ ::end:: + + ::if member.email2!=null:: + ::_("Email"):: : ::member.email2::
+ ::end:: + + +
+ +
+ +
+

::_("Payments")::

+
+ + + + + + + ::if balance<0:: + ::set color = "C00":: + ::else:: + ::set color = "0C0":: + ::end:: + + + + + + ::foreach t rb.current():: + + $$operation(::t::) + + + + ::end:: + + + + + +
::_("Balance"):: : + ::if balance<0:: + - + ::end:: + + ::Math.abs(balance):: ::currency():: + +
+ ::if t.type==2:: +
+ $$edit(::_("Edit")::,/transaction/edit/::t.id::) + $$delete(::_("Del.")::,/transaction/delete/::t.id::?token=::token::) +
+ ::elseif isAdmin:: + + + + ::end:: + +
+
    + ::if rb.paginationStartPage != 1:: +
  • + + + +
  • + ::end:: + ::foreach i loopList(rb.paginationStartPage,rb.paginationEndPage):: + ::if rb.page==i:: +
  • ::i::
  • + ::else:: +
  • ::i::
  • + ::end:: + ::end:: + ::if rb.paginationEndPage < rb.pages + 1:: +
  • + + + +
  • + ::end:: +
+
+ + + $$insert(::_("Insert a payement")::,/transaction/insertPayment/::member.id::) + +
+
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/member/view.mtt b/lang/master/tpl/member/view.mtt new file mode 100644 index 0000000..14e28e1 --- /dev/null +++ b/lang/master/tpl/member/view.mtt @@ -0,0 +1,223 @@ +::use 'design.mtt':: + +
+

::_("Contact informations")::

+
+ +
+ ::member.firstName:: ::member.lastName:: + + ::if member.firstName2!=null && member.lastName2!=null:: + & ::member.firstName2:: ::member.lastName2:: + ::end:: +
+ + $$nullSafe(::member.address1::)
+ $$nullSafe(::member.address2::)
+ $$nullSafe(::member.zipCode::) + $$nullSafe(::member.city::)
+
+ + ::if member.phone!=null:: + ::_("Téléphone"):: : ::member.phone::
+ ::end:: + + ::if member.email!=null:: + ::_("E-mail"):: : ::member.email::
+ ::end:: + + ::if member.email2!=null:: + ::_("E-mail"):: : ::member.email2::
+ ::end:: + +
+

+ ::_("Last login"):: : + ::if member.ldate!=null:: + ::member.ldate.toString():: + ::else:: + ::_("This member never logged in"):: + ::end:: +
+ ::_("Registred since"):: : + ::if member.cdate!=null:: + ::member.cdate.toString():: + ::else:: + ??? + ::end:: +
+ ::if member.pass=="":: +

::_("This member has not set a password")::
+ ::end:: +

+

+ $$edit(::_("Edit")::,/member/edit/::member.id::) + $$delete(::_("Remove from group")::,/member/delete/::member.id::?token=::token::) + ::_("Put on waiting list"):: + + + + ::_("Received messages"):: + + ::if !member.isFullyRegistred():: + + + ::_("Send an invitation"):: + ::end:: +

+ +

+ ::_("Log in as this member"):: +

+
+ + + ::if user._amap.hasMembership():: +

::_("Membership fee")::

+
+ ::if userAmap.hasValidMembership():: + $$bool(true) ::_("Membership up to date"):: + ::else:: + $$bool(false) ::_("Membership to renew"):: + ::end:: + ::_("for the period||full sentence : Membership up to date for the period 2017-2018"):: ::user._amap.getPeriodName(null):: +
+ ::_("Manage membership fees"):: + +
+ ::end:: + + +
+ +
+

::_("Current orders")::

+
+ ::set o = userContracts:: + ::if o.constOrders.length>0:: +

::_("CSA contracts")::

+ + + + + + + + ::foreach p o.constOrders:: + + + + + + + ::end:: +
::_("Qty||short version for quantity")::::_("Product")::::_("Price")::::_("Contract")::
::raw p.smartQt::::p.productName:: + + ::if p.userId2 != null:: +
::_("Order alternated with"):: + ::if member.id == p.userId:: + ::p.userName2:: + ::else:: + ::p.userName:: + ::end:: + ::end:: + +
::p.subTotal:: ::currency():: + ::p.contractName:: +
+ ::end:: + + ::if count(o.varOrders)>0:: + ::foreach d o.varOrders.keys():: +

::__("Order for ::date::",{date:d})::

+ + + + + + + + + + + + + ::set total = 0:: + ::foreach p o.varOrders.get(d):: + + + + + + + + + + + ::end:: + + + + + +
::_("Qty||short version for quantity")::::_("Product")::::_("U.P||short version of unit price")::::_("Sub-total")::::_("Fees")::::_("Total")::::_("Contract")::
::raw p.smartQt::::p.productName::::p.productPrice:: ::currency()::::p.subTotal:: ::currency():: + + ::if p.percentageValue!=null:: + + ::formatNum(p.fees):: ::currency():: + + ::end:: + + + ::formatNum(p.total):: ::currency():: + ::set total = total + p.total:: + + + ::p.contractName:: +
::_("TOTAL")::::formatNum(total):: ::currency()::
+ ::end:: + ::end:: + + +
+
+ + +
+
+ + + ::_("Balance"):: : + ::if userAmap.balance<0:: + ::set color = "C00":: + ::else:: + ::set color = "0C0":: + ::end:: + + ::userAmap.balance:: ::currency():: + + + + + + + ::_("Payments details"):: + + + +
+
+ + +
+

::_("Rights")::

+
+
    + ::foreach r userAmap._rights:: +
  • ::userAmap.getRightName(r)::
  • + ::end:: +
+
+
+ +::end:: diff --git a/lang/master/tpl/member/waiting.mtt b/lang/master/tpl/member/waiting.mtt new file mode 100644 index 0000000..ebf4182 --- /dev/null +++ b/lang/master/tpl/member/waiting.mtt @@ -0,0 +1,47 @@ +::use 'design.mtt':: +
+
+ +

::_("Waiting list")::

+ + + + + + + + + + + + + ::foreach w waitingList:: + + ::set u = w._user:: + + + + + + + + + ::end:: + +
::_("Name")::::_("Email")::::_("Phone")::::_("Registration date")::::_("Message")::
::u.getName()::::u.email::$$nullSafe(::u.phone::)::hDate(w.date)::"::w.message::" + + + ::_("Accept membership request"):: + + + + + ::_("Deny request"):: +
+ +
+
+ + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/membership/default.mtt b/lang/master/tpl/membership/default.mtt new file mode 100644 index 0000000..7f6e2d9 --- /dev/null +++ b/lang/master/tpl/membership/default.mtt @@ -0,0 +1,32 @@ +::use 'design.mtt':: + +
+
+

::_("Subscription payment of"):: ::member.getCoupleName()::

+ + + + + + + ::foreach m memberships:: + + + + + + ::end:: +
::_("Period")::::_("Subscription payment date")::Actions
::user._amap.getPeriodNameFromYear(m.year)::::hDate(m.date):: + $$delete(::_("Delete")::,/membership/delete/::member.id::/::m.year::?token=::token::) +
+
+
+ +
+
+

::_("Key-in a subscription payment")::

+ ::raw form:: +
+
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/messages/default.mtt b/lang/master/tpl/messages/default.mtt new file mode 100644 index 0000000..b4e4d09 --- /dev/null +++ b/lang/master/tpl/messages/default.mtt @@ -0,0 +1,55 @@ +::use 'design.mtt':: + + + + + +
+ +
+ ::if user.isAmapManager():: + ::_("All sent messages"):: + + ::else:: + ::_("Last sent messages"):: + ::end:: +
+ ::foreach m sentMessages:: + ::m.title:: +
::hDate(m.date)::
+
+ ::end:: +
+ +
+
+ + +
+
+

::_("Send an e-mail")::

+ + ::raw form:: +
+
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/messages/message.mtt b/lang/master/tpl/messages/message.mtt new file mode 100644 index 0000000..edc2c7d --- /dev/null +++ b/lang/master/tpl/messages/message.mtt @@ -0,0 +1,77 @@ +::use 'design.mtt':: +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
::_("Subject:")::::msg.title::
::_("Sent on the")::::hDate(msg.date)::
::_("Sender"):: + ::if msg._sender==null:: + inconnu + ::else:: + ::msg._sender.firstName:: ::msg._sender.lastName:: + ::end:: +
::_("Receiver(s)"):: + ::if list!=null:: + ::list:: + ::else:: + ::msg._recipients:: + ::end:: +
::_("Status"):: +
+ + ::foreach m status:: + + + ::if m.failure!=null:: + + + + + + ::else:: + + + + + ::end:: + + ::end:: +
::m.email::::m.failure::
::m.email::::m.success::
+
+
::raw msg.body::
+ + ::_("Back"):: + +
+
+ + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/place/view.mtt b/lang/master/tpl/place/view.mtt new file mode 100644 index 0000000..5e4b33e --- /dev/null +++ b/lang/master/tpl/place/view.mtt @@ -0,0 +1,25 @@ +::use "design.mtt":: + +
+
+ + +
+
+
+
+ + ::place.name::
+ $$nullSafe(::place.address1::)
+ $$nullSafe(::place.address2::)
+ $$nullSafe(::place.zipCode::) $$nullSafe(::place.city::) + +
+ +
+ + +::end:: + diff --git a/lang/master/tpl/product/addimage.mtt b/lang/master/tpl/product/addimage.mtt new file mode 100644 index 0000000..8db7fe7 --- /dev/null +++ b/lang/master/tpl/product/addimage.mtt @@ -0,0 +1,34 @@ +::use 'contractadmin/design.mtt':: + + +

::_("Import a photo of the product")::

+ + +
+ +
+
+ + +
+ ::_("Size of the photo: 300 x 300px")::
+ ::_("Format : JPEG")::
+
+ + +
+
+ ::_("Back to the management of products"):: + +
+ +
+ + +
+ +
+ + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/product/categorize.mtt b/lang/master/tpl/product/categorize.mtt new file mode 100644 index 0000000..e0db06a --- /dev/null +++ b/lang/master/tpl/product/categorize.mtt @@ -0,0 +1,57 @@ +::use 'contractadmin/design.mtt':: + +

::_("Categories")::

+ +

+ ::raw _("You can link your products to categories, in order to better organize them in the \"shop mode\".
To add categories: select checkboxes of products and then select a category in the list at the bottom of the page. Click then on \"add\".
To remove a category: click on it."):: +

+ + + +
+ +
+ +
+ + ::_("Select all"):: + + + + ::_("Add the category"):: + ::_("Save modifications"):: + +
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/product/compose.mtt b/lang/master/tpl/product/compose.mtt new file mode 100644 index 0000000..82ab5ea --- /dev/null +++ b/lang/master/tpl/product/compose.mtt @@ -0,0 +1,20 @@ +::use 'design.mtt':: + +
+
+ + + + +
+ + + + +
+
+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/product/import.mtt b/lang/master/tpl/product/import.mtt new file mode 100644 index 0000000..90e75cf --- /dev/null +++ b/lang/master/tpl/product/import.mtt @@ -0,0 +1,111 @@ +::use 'design.mtt':: + +
+
+

::_("Import products")::

+ + ::if step==1:: + +
+

+ +

+

+ +

+
+ + ::elseif step==2:: + +

Produits à importer

+
+ ::if !csv.isEmpty():: + + + + + ::foreach h csv.getHeaders():: + + ::end:: + + + ::foreach d csv.getDatas():: + + ::foreach r d:: + + ::end:: + + ::end:: +
::_(h)::
+ ::if r!=null:: ::r:: ::end:: +
+
+ ::_("Warning"):: +

+ ::raw _("Before validating the import, please check if the table above is correctly filled.
If data are not in line or not corresponding to the right column, it means that your CSV file is not correct."):: +

+ Recommencer +
+ ::_("Import these products in the database"):: + ::else:: + +
+ ::_("Ouch!"):: +

::_("It seems that there is no data to import.")::

+ Recommencer +
+ + ::end:: +
+ + + ::elseif step==3:: + ::if numImported>0:: + +
+ ::__("Congratulations! You have imported ::num:: products in your database", {num:numImported}):: +
+ + ::else:: +

::_("You did not import anything in your database because your file was empty or contained products already present in the database.")::

+ ::end:: + ::_("Back to the management of products"):: + + + ::end:: +
+ +
+

::_("Help")::

+ ::_("This page will allow you to import a large number of products in your database from an Excel file."):: +
+
    +
  • ::raw _("To start, Download this demo file")::
  • +
  • ::raw _("Fill correctly this file, or modify your existing file to respect strictly the order of columns")::
  • +
  • ::raw _("When your file is ready, click on\"Save as\" in your Excel sheet and choose the format CSV")::
  • +
  • ::raw _("Choose the following options: separation characters: comma, encoding: Unicode UTF8")::
  • +
  • ::raw _("Save your file")::
  • +
  • ::raw _("Then click on the button above \"Choose a file\" and send your file")::
  • +
+
+ +
+::end:: \ No newline at end of file diff --git a/lang/master/tpl/shop/choose.mtt b/lang/master/tpl/shop/choose.mtt new file mode 100644 index 0000000..1950aba --- /dev/null +++ b/lang/master/tpl/shop/choose.mtt @@ -0,0 +1,108 @@ +::use 'design.mtt':: +
+ +

::_("Choose a delivery")::

+ +
+ + ::if distribs.length==0:: +
+ ::_("There is currently no delivery planned."):: +
+ ::end:: + + ::foreach d distribs:: + +
+
+ ::set active ="":: + ::if d.active==false:: ::set active="disabled":: ::end:: + +
+ + ::set s = getDate( d.startDate):: + +
+
::s.dow::
+
::s.d::
+
::s.m::
+
+ + +
+ + ::set e = getDate(d.endDate):: + de ::s.h+":"+s.i:: à ::e.h+":"+e.i:: +
+ + + ::if d.place!=null:: + +
+ $$nullSafe(::d.place.address1::) + $$nullSafe(::d.place.address2::) + $$nullSafe(::d.place.zipCode::) + $$nullSafe(::d.place.city::) + +
+ ::end:: + + + + +
+ + +
+ +
+ ::foreach p d.products:: + + +
+ +
+ + + ::end:: + + + +
+ + + +
+ + + + +
+ +
+ ::end:: + +
+ +
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/shop/default.mtt b/lang/master/tpl/shop/default.mtt new file mode 100644 index 0000000..5735ef3 --- /dev/null +++ b/lang/master/tpl/shop/default.mtt @@ -0,0 +1,205 @@ +::use 'design.mtt':: +
+ +
+ + +
+ ::if(contracts!=null):: + ::foreach c contracts:: + ::c.name:: + ::end:: + ::end:: +
+ +
+ +
+ ::if place!=null:: + + ::place:: + + ::end:: + + ::if place!=null:: +
+ $$nullSafe(::place.address1::) + $$nullSafe(::place.address2::) + $$nullSafe(::place.zipCode::) + $$nullSafe(::place.city::) +
+ ::end:: +
+ + +
+ ::set hdate = hDate(date):: +
::__("Distribution on ::date::",{date:hdate})::
+ + +
+ + ::_("Orders will close on"):: + + ::if ArrayTool.mapLength(infos) == 1:: + ::hDate( infos.iterator().next()[0].orderEndDate ):: + ::else:: +
    + ::foreach k infos.keys():: +
  • + ::set dists = infos.get(k):: + + ::if dists.length==1:: + ::dists[0]._contract.name:: + ::else:: + ::set tt = "":: + ::foreach d dists:: + ::set tt = tt + d._contract.name + ". ":: + ::end:: + ::_("Autres"):: + ::end:: + + : ::hDate(Date.fromString(k)):: +
  • + ::end:: +
+ ::end:: +
+
+ + + +
+ +
+ + + +
+ ::foreach p products:: + + ::end:: +
+ +
+
+ +
+ + +
+ + + + +
+ ::_("All products")::
+ + ::foreach g group.getCategoryGroups():: +
+

::g.name::

+ ::foreach c g.categs:: + ::c.name::
+ ::end:: +
+ ::end:: + +
+ + + ::if blocks!=null:: + ::foreach b blocks:: +
+

::b.title::

+

::raw b.html::

+
+ ::end:: + ::end:: + + +
+ + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/shop/default2.mtt b/lang/master/tpl/shop/default2.mtt new file mode 100644 index 0000000..379a88d --- /dev/null +++ b/lang/master/tpl/shop/default2.mtt @@ -0,0 +1,8 @@ +::use 'design.mtt':: +
+ +::end:: diff --git a/lang/master/tpl/shop/needLogin.mtt b/lang/master/tpl/shop/needLogin.mtt new file mode 100644 index 0000000..50eb64e --- /dev/null +++ b/lang/master/tpl/shop/needLogin.mtt @@ -0,0 +1,43 @@ +::use 'design.mtt':: + +
+
+
+
+ +::if(group!=null && group.hasPhoneRequired()):: + ::set phoneRequired=true:: +::else:: + ::set phoneRequired=false:: +::end:: + + +::end:: diff --git a/lang/master/tpl/shop/productInfo.mtt b/lang/master/tpl/shop/productInfo.mtt new file mode 100644 index 0000000..803e317 --- /dev/null +++ b/lang/master/tpl/shop/productInfo.mtt @@ -0,0 +1,111 @@ + + +
+ +
+ +
+ +
+ +
+ + ::formatNum(p.price):: ::currency():: ::_("incl. VAT"):: + + ::if (p.vat!=null && p.vat!=0) || p.contractTax!=0:: +
+ + ::if p.vat!=null && p.vat!=0:: + ::p.vat:: ::_("VAT included")::
+ ::end:: + + ::if p.contractTax!=null:: + ::p.contractTax:: % ::p.contractTaxName:: ::_("included")::
+ ::end:: +
+ ::end:: + +
+ +
+ ::if p.unitType!=null && p.qt!=null:: + + + ::set qt = formatNum(p.qt):: + ::set u = unit(p.unitType,qt>1):: + + ::if(p.variablePrice):: + ::__("about ::qt:: ::unit::||product has a variable weight",{qt:qt,unit:u}):: + ::else:: + ::qt:: ::u:: + ::end:: + + +
::raw pricePerUnit(p.price,p.qt,p.unitType)::
+ + ::end:: +
+ +
+ +

+ ::_("Organic agriculture"):: +

+ +

+ ::_("Variable price depending on weight"):: +

+ +

+ ::raw p.desc:: +

+ +
+ + +
+ + +
+ +
+ +
+ +
+ +
+ +

::vendor.name::

+ + ::vendor.city:: (::vendor.zipCode::) + +

+ ::raw nl2br(vendor.desc):: +

+ + ::if vendor.linkUrl!=null:: +

+ ::if vendor.linkText!=null:: + ::vendor.linkText:: + ::else:: + ::_("Read more"):: + ::end:: +

+ ::end:: + +
+ + ::if(product!=null):: + ::set cats = product.getCategories():: +
+

::_("Categories")::

+
+ ::foreach c cats:: + ::c.name::
+ ::end:: +
+
+ ::end:: +
+
\ No newline at end of file diff --git a/lang/master/tpl/stats/default.mtt b/lang/master/tpl/stats/default.mtt new file mode 100644 index 0000000..f087e3d --- /dev/null +++ b/lang/master/tpl/stats/default.mtt @@ -0,0 +1,30 @@ +::use 'design.mtt':: + + + + +
+
+ + titre + + +
+ + blabla +
+ + +
+
+ + + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/transaction/cash.mtt b/lang/master/tpl/transaction/cash.mtt new file mode 100644 index 0000000..2b9d1ee --- /dev/null +++ b/lang/master/tpl/transaction/cash.mtt @@ -0,0 +1,35 @@ +::use 'design.mtt':: +
+
+ + +
+ + ::_("Your order has been recorded !"):: + +
+ +
+ + +
+ +

+ ::_("You chose to pay by cash at the product distribution"):: : +

+ +

+ ::_("Please prepare the following amount for the distribution :"):: : ::formatNum(amount):: ::currency():: +

+ +

+ + ::_("Check my orders"):: +

+ +
+ +
+ +
+::end:: \ No newline at end of file diff --git a/lang/master/tpl/transaction/check.mtt b/lang/master/tpl/transaction/check.mtt new file mode 100644 index 0000000..7084650 --- /dev/null +++ b/lang/master/tpl/transaction/check.mtt @@ -0,0 +1,42 @@ +::use 'design.mtt':: +
+
+ + +
+ + ::_("Your order has been recorded !"):: + +
+ +
+ + +
+ +

+ ::_("You chose to pay by check at the product distribution"):: : +

+ + +

+ ::raw __("Prepare a check of ::amount:: ::currency:: payable to : ::checkOrder::",{checkOrder:user._amap.checkOrder,amount:formatNum(amount),currency:currency()}):: +

+

+ ::_("Mention this reference at the back of the check in order to facilitate its process:")::
+ ::code:: +

+ +

+ + + ::_("Check my orders"):: + +

+ +
+ +
+ +
+::end:: \ No newline at end of file diff --git a/lang/master/tpl/transaction/moneypot.mtt b/lang/master/tpl/transaction/moneypot.mtt new file mode 100644 index 0000000..c240474 --- /dev/null +++ b/lang/master/tpl/transaction/moneypot.mtt @@ -0,0 +1,42 @@ +::use 'design.mtt':: +
+
+ + +
+ + ::_("Your order has been recorded !"):: + +
+ +
+ + +
+ +

+ ::_("You chose to pay with your money pot."):: : +

+ +

+ ::_("Please remember to clear your balance at the time agreed with the coordinator of your group."):: +

+ +

+ ::__("Your current balance is : ::balance:: ::currency::",{balance: balance,currency: currency() }):: +

+ +

+ + ::_("Check my orders"):: + + + ::_("Check my payments history"):: +

+ +
+ +
+ +
+::end:: \ No newline at end of file diff --git a/lang/master/tpl/transaction/pay.mtt b/lang/master/tpl/transaction/pay.mtt new file mode 100644 index 0000000..24ac164 --- /dev/null +++ b/lang/master/tpl/transaction/pay.mtt @@ -0,0 +1,60 @@ +::use 'design.mtt':: +
+
+ +

::_("Order payment")::

+ + + +
+

+ ::_("Amount to pay"):: : ::formatNum(amount):: ::currency():: +

+

+ ::_("Your basket has been recorded, please select a payment method to confirm it."):: +

+
+ + + + +
+ ::if(paymentTypes==null || paymentTypes.length==0):: +
+
+ ::_("Currently no payment method has been defined for this group."):: +
+
+ ::end:: + + + ::foreach p paymentTypes:: +
+ ::if(p.type != "moneypot" || allowMoneyPotWithNegativeBalance || futurebalance >= 0):: + ::set class = "clickable":: + ::set href = p.link:: + ::else:: + ::set class = "clickable disabled":: + ::set href = "#":: + ::set warning = _("You do not have sufficient funds to pay this order with your money pot."):: + ::end:: + +

::p.name::

+ ::raw p.icon:: + ::if( warning ):: +
::warning:: + ::end:: +
+
+ + ::end:: +
+ +
+
+ + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/transaction/transfer.mtt b/lang/master/tpl/transaction/transfer.mtt new file mode 100644 index 0000000..a12231d --- /dev/null +++ b/lang/master/tpl/transaction/transfer.mtt @@ -0,0 +1,40 @@ +::use 'design.mtt':: +
+
+ +
+ + ::_("Your order has been recorded !"):: + +
+ +
+ +
+ +

+ ::_("You chose to pay by transfer"):: : +

+

+ ::_("Please make a transfer of"):: + ::formatNum(amount):: ::currency():: + + ::_("to the following bank account:")::
+ ::_("IBAN:"):: ::user._amap.IBAN:: +

+

+ ::_("Mention this reference in the label of the transfer in order to facilitate its process:")::
+ ::code:: +

+ +

+ + ::_("Check my orders"):: +

+ +
+ +
+ +
+::end:: diff --git a/lang/master/tpl/transaction/view.mtt b/lang/master/tpl/transaction/view.mtt new file mode 100644 index 0000000..afe40b0 --- /dev/null +++ b/lang/master/tpl/transaction/view.mtt @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + +
Operation ID::op.id::
Name::op.name::
Data::op._data::
Infos + + ::if infos!=null:: + ::foreach h infos.HPAY:: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID::h.ID::
DATE::h.DATE::
SEN::h.SEN::
REC::h.REC::
DEB::h.DEB::
CRED::h.CRED::
REFUND::h.REFUND::
COM::h.COM::
MSG::h.MSG::
STATUS::h.STATUS::
EXTRA + IS3DS : ::h.EXTRA.IS3DS::
+ CTRY : ::h.EXTRA.CTRY::
+ AUTH : ::h.EXTRA.AUTH::
+ NUM : ::h.EXTRA.NUM::
+ EXP : ::h.EXTRA.EXP::
+ TYP : ::h.EXTRA.TYP::
+
INT_MSG::h.INT_MSG::
MLABEL::h.MLABEL::
INT_STATUS::h.INT_STATUS::
+ ::end:: + ::end:: +
+ + + diff --git a/lang/master/tpl/user/choose.mtt b/lang/master/tpl/user/choose.mtt new file mode 100644 index 0000000..3d9106b --- /dev/null +++ b/lang/master/tpl/user/choose.mtt @@ -0,0 +1,108 @@ +::use 'design.mtt':: + +
+

::_("Hello"):: + ::if whichUser()==0:: + ::user.firstName:: + ::else:: + ::user.firstName2:: + ::end:: + ! +

+
+ +
0::> +

::raw _("Connect to your Cagette Pro account to manage your products, catalogs and deliveries.")::

+
+ + + +
+
+ ::foreach p pros:: + + + ::end:: +
+
+ +
+

::raw _("Please click on the Cagette group you want to connect to.")::

+
+ + +
+
+ ::foreach ua amaps:: + ::set group = ua._amap:: + + ::end:: + + ::if amaps.length==0:: +
+
+

+ ::_("You don't belong to any group yet"):: +

+ ::_("Create my own group"):: +
+
+ ::end:: +
+ +
+ + +
+ ::if session.lang!="de":: + ::_("Find other groups near me"):: + ::end:: + ::_("Create my own group"):: + +
+ + + +
0::> +
+

::_("Subscriptions on waiting list")::

+ + ::foreach w wl:: + + + + + + ::end:: +
+ ::w._group.name:: + + ::__("Request sent on ::date::",{ date:hDate(w.date) }):: + + ::_("Cancel"):: +
+
+
+ + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/user/default.mtt b/lang/master/tpl/user/default.mtt new file mode 100644 index 0000000..cc0b4b2 --- /dev/null +++ b/lang/master/tpl/user/default.mtt @@ -0,0 +1,11 @@ +::use 'design.mtt':: + + + + + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/user/definePassword.mtt b/lang/master/tpl/user/definePassword.mtt new file mode 100644 index 0000000..d2682ab --- /dev/null +++ b/lang/master/tpl/user/definePassword.mtt @@ -0,0 +1,39 @@ +:: use 'design.mtt':: + +

::_("Change of password")::

+ +::if error!=null:: +
+ ::error:: +
+::end:: + + ::if step==1 :: +

+ ::_("If you have forgotten your password or if you would like to change it, please key-in below your e-mail address:"):: + + ::raw form:: +

+ ::elseif step==2:: +

+ ::_("An e-mail was sent to you to allow you changing your password."):: +

+ ::elseif step==3:: +

+ ::_("Key-in your new password:"):: + + ::raw form:: +

+ ::else:: +

+ ::_("Congratulations, your password have been modified!"):: +

+

+ ::_("Log in with"):: +

+ + ::end:: + +::end:: + + diff --git a/lang/master/tpl/user/forgottenPassword.mtt b/lang/master/tpl/user/forgottenPassword.mtt new file mode 100644 index 0000000..5098f00 --- /dev/null +++ b/lang/master/tpl/user/forgottenPassword.mtt @@ -0,0 +1,42 @@ +:: use 'design.mtt':: +
+
+

Changement de mot de passe

+ + ::if error!=null:: +
+ ::error:: +
+ ::else:: + + ::if step==1 :: +

+ ::_("If you have forgotten your password or if you would like to change it, please key-in below your e-mail address:"):: + + ::raw form:: +

+ ::elseif step==2:: +
+ ::_("An e-mail was sent to you to allow you changing your password."):: +
+ ::elseif step==3:: +

+ ::_("Key-in your new password:"):: + + ::raw form:: +

+ ::else:: +
+ ::_("Congratulations, your password have been modified!"):: +
+

+ ::_("Log in with your new password"):: +

+ + ::end:: + ::end:: +
+
+::end:: + + diff --git a/lang/master/tpl/user/insert.mtt b/lang/master/tpl/user/insert.mtt new file mode 100644 index 0000000..3ecb884 --- /dev/null +++ b/lang/master/tpl/user/insert.mtt @@ -0,0 +1,35 @@ +::use 'design.mtt':: +
+
+

::title::

+ +

0:: > + ::_("These persons are already in the database, but are not member of this group:"):: +

::foreach u userSims:: + ::u::
+ ::end:: +

+

+ + +

0:: > + ::_("These persons are already member of this group:"):: +

+ ::foreach u userAmaps:: + ::u::
+ ::end:: +

+

+ + +
+ ::raw text:: +
+ + ::if form != null:: + ::raw form:: + ::end:: +
+
+ +::end:: diff --git a/lang/master/tpl/user/login.mtt b/lang/master/tpl/user/login.mtt new file mode 100644 index 0000000..1d07b70 --- /dev/null +++ b/lang/master/tpl/user/login.mtt @@ -0,0 +1,8 @@ +::use 'design.mtt':: + +
+ +
+::end:: \ No newline at end of file diff --git a/lang/master/tpl/user/register.mtt b/lang/master/tpl/user/register.mtt new file mode 100644 index 0000000..06e3099 --- /dev/null +++ b/lang/master/tpl/user/register.mtt @@ -0,0 +1,11 @@ +::use 'design.mtt':: + +
+ +

::_("Sign up!")::

+ + ::raw form:: + +
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/validate/user.mtt b/lang/master/tpl/validate/user.mtt new file mode 100644 index 0000000..e764b69 --- /dev/null +++ b/lang/master/tpl/validate/user.mtt @@ -0,0 +1,284 @@ +::use 'design.mtt':: + +
+
+ +
+

+ ::_("Validate the delivery of the"):: ::hDate(date):: +

+

::place:: +

+ +
+ + ::set total = 0:: + + + + + + + + + + + + + + + + + + + ::foreach o orders:: + + + + + + + + + + + + + ::end:: + + + + + + + + +
Commande
+ +
+ N°::basket.num:: +
+ + ::member.getName():: +
::_("Contract")::::_("Qty")::::_("Ref")::::_("Product")::::_("U.P.")::::_("Sub-total")::::_("Fees")::::_("Total")::::_("Paid")::
+ + ::short(o.contractName,40):: + + + ::raw o.smartQt: + + $$nullSafe(::o.productRef::) + + ::short(o.productName,40):: + + ::formatNum(o.productPrice):: ::currency():: + + ::formatNum(o.subTotal):: ::currency():: + + + ::if o.percentageValue!=null:: + + ::formatNum(o.fees):: ::currency():: + + ::end:: + + + ::formatNum(o.total):: ::currency():: + ::set total = total + o.total:: + + + $$check(::o.paid::) +
::_("Total ordered")::::formatNum(total):: ::currency()::
+ + + ::if (user._amap.hasShopMode()):: + + + + + + + ::set op = basket.getOrderOperation(false):: + + ::if op!=null:: + + $$operation(::op::) + + + + ::set tpaid = 0:: + ::foreach tt op.getRelatedPayments():: + + $$operation(::tt::) + ::if !tt.pending:: + ::set tpaid = tpaid + tt.amount:: + ::end:: + + + ::end:: + + ::set amount = numClean(op.amount):: + ::set tpaid = numClean(tpaid):: + + + + + + + + + ::else:: + + ::end:: +
::_("Payment")::
+ ::if(tt.pending):: + + + ::_("This payment was received"):: + + $$delete(::_("Del.")::,/validate/::date::/::place.id::/::member.id::/deleteOp/::tt.id::?token=::token::) + ::end:: +
::_("Total paid"):: + ::tpaid:: ::currency():: + + +
+
+ ::_("This order has been made when payments were disabled"):: +
+
+ + + ::if(tpaid!=null && amount!=null):: + + ::if(tpaid+amount != 0):: + ::set class="danger":: + ::else:: + ::set class="success":: + ::end:: +
+ ::if(tpaid+amount > 0):: + + $$check(::false::) + ::set str = Math.abs(tpaid+amount)+" "+currency():: + ::raw __("Paid too much, credit of ::amount::",{amount:str}):: + + ::elseif(tpaid+amount < 0):: + + $$check(::false::) + ::_("Missing"):: ::Math.abs(tpaid+amount):: ::currency():: + + ::else:: + + $$check(::true::) ::_("Everything is allright !"):: + + ::end:: +
+ ::end:: + +

+ $$insert(::_("Key-in a refund")::,/validate/::date::/::place.id::/::member.id::/addRefund/) + $$insert(::_("Key-in a payment")::,/validate/::date::/::place.id::/::member.id::/addPayment/) +

+ +

+ + + + ::_("Validate this order"):: + + +

+ + ::else:: + + + + + + + + ::foreach op operations:: + + $$operation(::op::) + + + ::end:: + + + + ::if balance<0:: + ::set color = "C00":: + ::else:: + ::set color = "0C0":: + ::end:: + + + +
+ Solde de l'adhérent ::_("Details"):: +
+ ::if(op.pending && op.type==2):: + + + ::_("This payment has been received"):: + + $$delete(Suppr.,/validate/::date::/::place.id::/::member.id::/deleteOp/::op.id::?token=::token::) + ::end:: +
Solde : + ::balance:: ::currency():: +
+ +

+ $$insert(::_("Key-in a refund")::,/validate/::date::/::place.id::/::member.id::/addRefund/) + $$insert(::_("Key-in a payment")::,/validate/::date::/::place.id::/::member.id::/addPayment/) +

+ +

+ ::if(balance < 0):: + + + ::_("Validate this order"):: + + +

+ ::raw _("You cannot validate this order
because the balance of the member is negative"):: +
+ + ::else:: + + + + ::_("Validate this order"):: + + ::end:: +

+ + ::end:: + + + +

+ + ::_("Back to the delivery validation"):: + + ::_("Payment history of this member"):: +

+ +
+
+::end:: diff --git a/lang/master/tpl/vendor/addimage.mtt b/lang/master/tpl/vendor/addimage.mtt new file mode 100644 index 0000000..f07e124 --- /dev/null +++ b/lang/master/tpl/vendor/addimage.mtt @@ -0,0 +1,38 @@ +::use 'design.mtt':: + + + + + + + +
+

Importer une photo pour ::vendor.name::

+ + +
+
+ + +
+ ::_("Recommended size of the photo: 300 x 300px")::
+ ::_("Format : JPEG")::
+
+ + +
+
+ ::_("Back"):: +
+
+ +
+
+ + +
+
+ + + +::end:: \ No newline at end of file diff --git a/lang/master/tpl/vendor/default.mtt b/lang/master/tpl/vendor/default.mtt new file mode 100644 index 0000000..9c10f7a --- /dev/null +++ b/lang/master/tpl/vendor/default.mtt @@ -0,0 +1,54 @@ +::use 'design.mtt':: +
+
+ +

::_("Suppliers")::

+ + + + + + ::foreach m vendors.current():: + + + + + + ::end:: + + + + + +
+ + ::m.name:: + + + +
+ << +   + page ::vendors.page::/::vendors.pages:: +   + >> +
+ + + +
+ +
+
+ +
+ + +
+ ::_("On this page you can manage the list of suppliers of your group."):: +
+
+ +::end:: \ No newline at end of file diff --git a/lang/master/tpl/vendor/view.mtt b/lang/master/tpl/vendor/view.mtt new file mode 100644 index 0000000..7939dde --- /dev/null +++ b/lang/master/tpl/vendor/view.mtt @@ -0,0 +1,22 @@ +::use 'design.mtt':: +
+
+ +

::vendor.name::

+ ::_("Supplier")::
+ + + $$edit(Modifier,/vendor/edit/::vendor.id::) + + +
+
+ +
+ + +
+
+
+ +::end:: \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8ccc6ba --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6149 @@ +{ + "name": "Cagette.net", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@types/react": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.0.0.tgz", + "integrity": "sha512-lmu8zYXNhkZrIAgFgJK2/tGNXfdL//tKbtDEeph+doEAsBm+C2vnD+mAkR17bumu9K8ivYufySkkX4vrq9L8Sw==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "ajv": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz", + "integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + } + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "array-filter": { + "version": "0.0.1", + "resolved": false, + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-map": { + "version": "0.0.0", + "resolved": false, + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": false, + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "asap": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz", + "integrity": "sha1-UidltQw1EEkOUtfc/ghe+bqWlY8=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + } + } + }, + "babel-core": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", + "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.0", + "debug": "^2.6.8", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.7", + "slash": "^1.0.0", + "source-map": "^0.5.6" + } + }, + "babel-generator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", + "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.6", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + } + } + }, + "babel-helper-builder-react-jsx": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", + "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "esutils": "^2.0.2" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-syntax-flow": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", + "dev": true + }, + "babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", + "dev": true + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", + "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-flow-strip-types": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", + "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", + "dev": true, + "requires": { + "babel-plugin-syntax-flow": "^6.18.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-display-name": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", + "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", + "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", + "dev": true, + "requires": { + "babel-helper-builder-react-jsx": "^6.24.1", + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx-self": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", + "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx-source": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", + "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "babel-preset-flow": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", + "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", + "dev": true, + "requires": { + "babel-plugin-transform-flow-strip-types": "^6.22.0" + } + }, + "babel-preset-react": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", + "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", + "dev": true, + "requires": { + "babel-plugin-syntax-jsx": "^6.3.13", + "babel-plugin-transform-react-display-name": "^6.23.0", + "babel-plugin-transform-react-jsx": "^6.24.1", + "babel-plugin-transform-react-jsx-self": "^6.22.0", + "babel-plugin-transform-react-jsx-source": "^6.22.0", + "babel-preset-flow": "^6.23.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + }, + "dependencies": { + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "dev": true + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "dev": true + } + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babelify": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz", + "integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=", + "dev": true, + "requires": { + "babel-core": "^6.0.14", + "object-assign": "^4.0.0" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "binary-extensions": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", + "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", + "dev": true + }, + "blacklist": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/blacklist/-/blacklist-1.1.4.tgz", + "integrity": "sha1-st0J1hd2JbLKppg1o3somV+povI=" + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "~2.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "dev": true, + "requires": { + "hoek": "4.x.x" + } + }, + "bootstrap": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.3.4.tgz", + "integrity": "sha1-AjeLSmts56VeOt0e2uQa1590ZGo=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "browserify": { + "version": "14.5.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-14.5.0.tgz", + "integrity": "sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g==", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^1.11.0", + "browserify-zlib": "~0.2.0", + "buffer": "^5.0.2", + "cached-path-relative": "^1.0.0", + "concat-stream": "~1.5.1", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.0", + "domain-browser": "~1.1.0", + "duplexer2": "~0.1.2", + "events": "~1.1.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.0.0", + "labeled-stream-splicer": "^2.0.0", + "module-deps": "^4.0.8", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "~0.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^2.0.0", + "stream-http": "^2.0.0", + "string_decoder": "~1.0.0", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "~0.0.0", + "url": "~0.11.0", + "util": "~0.10.1", + "vm-browserify": "~0.0.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, + "asn1.js": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz", + "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + } + }, + "astw": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", + "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", + "dev": true, + "requires": { + "acorn": "^4.0.3" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-pack": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.2.tgz", + "integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "combine-source-map": "~0.7.1", + "defined": "^1.0.0", + "through2": "^2.0.0", + "umd": "^3.0.0" + } + }, + "browser-resolve": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", + "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "browserify-aes": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", + "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz", + "integrity": "sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cached-path-relative": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", + "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "combine-source-map": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", + "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=", + "dev": true, + "requires": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~2.0.0", + "typedarray": "~0.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "deps-sort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", + "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "shasum": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detective": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.5.0.tgz", + "integrity": "sha1-blqMaybmx6JUsca210kNmOyR7dE=", + "dev": true, + "requires": { + "acorn": "^4.0.3", + "defined": "^1.0.0" + } + }, + "diffie-hellman": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "^1.0.2" + } + }, + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "dev": true, + "requires": { + "inherits": "^2.0.1" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "dev": true + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "~0.5.3" + } + }, + "insert-module-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz", + "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "combine-source-map": "~0.7.1", + "concat-stream": "~1.5.1", + "is-buffer": "^1.1.0", + "lexical-scope": "^1.2.0", + "process": "~0.11.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "json-stable-stringify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "labeled-stream-splicer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz", + "integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "isarray": "~0.0.1", + "stream-splicer": "^2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "lexical-scope": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", + "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=", + "dev": true, + "requires": { + "astw": "^2.0.0" + } + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "module-deps": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", + "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "browser-resolve": "^1.7.0", + "cached-path-relative": "^1.0.0", + "concat-stream": "~1.5.0", + "defined": "^1.0.0", + "detective": "^4.0.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.3", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, + "requires": { + "path-platform": "~0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" + } + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", + "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", + "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" + } + }, + "resolve": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", + "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + }, + "ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "dev": true, + "requires": { + "hash-base": "^2.0.0", + "inherits": "^2.0.1" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "sha.js": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz", + "integrity": "sha512-G8zektVqbiPHrylgew9Zg1VRB1L/DtXNUVAM6q4QLy8NE3qtHlFXTf8VLL4k1Yl6c7NMjtZUTdXV+X44nFaT6A==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shasum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", + "dev": true, + "requires": { + "json-stable-stringify": "~0.0.0", + "sha.js": "~2.4.4" + } + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true, + "requires": { + "array-filter": "~0.0.0", + "array-map": "~0.0.0", + "array-reduce": "~0.0.0", + "jsonify": "~0.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.2.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-splicer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", + "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "requires": { + "minimist": "^1.1.0" + } + }, + "syntax-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.3.0.tgz", + "integrity": "sha1-HtkmbE1AvnXcVb+bsct3Biu5bKE=", + "dev": true, + "requires": { + "acorn": "^4.0.3" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, + "requires": { + "process": "~0.11.0" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "umd": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz", + "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" + } + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", + "dev": true + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-react-class": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", + "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "cross-env": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.1.1.tgz", + "integrity": "sha512-Wtvr+z0Z06KO1JxjfRRsPC+df7biIOiuV4iZ73cThjFGkH+ULBZq1MkBdywEcJC4cTDbO6c8IjgRjfswx3YTBA==", + "dev": true, + "requires": { + "cross-spawn": "^5.1.0", + "is-windows": "^1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "dev": true, + "requires": { + "boom": "5.x.x" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "requires": { + "hoek": "4.x.x" + } + } + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "^2.0.5", + "object-keys": "^1.0.8" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "dom-helpers": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.3.1.tgz", + "integrity": "sha512-2Sm+JaYn74OiTM2wHvxJOo3roiq/h25Yi69Fqk269cNUwIXsCvATB6CRSFC9Am/20G2b28hGv/+7NiWydIrPvg==" + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "eonasdan-bootstrap-datetimepicker": { + "version": "4.7.14", + "resolved": "https://registry.npmjs.org/eonasdan-bootstrap-datetimepicker/-/eonasdan-bootstrap-datetimepicker-4.7.14.tgz", + "integrity": "sha1-xAtNCEBJGBa17qhi0WRvuLwaOiA=", + "requires": { + "bootstrap": "^3.0", + "jquery": "^3.3.1", + "moment": "^2.9.0" + }, + "dependencies": { + "jquery": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", + "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + } + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.9.0.tgz", + "integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q==", + "dev": true, + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fbjs": { + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.12.tgz", + "integrity": "sha1-ELXZL3bUVXX9Y6IX1OoCvqL47QQ=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.9" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^1.1.3", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" + } + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", + "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.3.0", + "node-pre-gyp": "^0.6.39" + }, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "dev": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.x.x" + } + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^0.4.1", + "concat-map": "0.0.1" + } + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.x.x" + } + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fstream": "^1.0.0", + "inherits": "2", + "minimatch": "^3.0.0" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true, + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true, + "dev": true, + "optional": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "mime-db": "1.27.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.39", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "hawk": "3.1.3", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "request": "2.81.0", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^2.2.1", + "tar-pack": "^3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "~0.4.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "bundled": true, + "dev": true, + "requires": { + "buffer-shims": "~1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~1.0.0", + "util-deprecate": "~1.0.1" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" + } + }, + "rimraf": { + "version": "2.6.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "dev": true, + "requires": { + "hoek": "2.x.x" + } + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jodid25519": "^1.0.0", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.2.0", + "fstream": "^1.0.10", + "fstream-ignore": "^1.0.5", + "once": "^1.3.3", + "readable-stream": "^2.1.4", + "rimraf": "^2.5.1", + "tar": "^2.2.1", + "uid-number": "^0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + } + } + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", + "dev": true, + "requires": { + "globule": "^1.0.0" + } + }, + "geolib": { + "version": "2.0.24", + "resolved": "https://registry.npmjs.org/geolib/-/geolib-2.0.24.tgz", + "integrity": "sha512-NR0AyYyEnGrFS9JvSFmmotQDxVCORJgDHdvBwSatxl5aHarOLMh3KuGI83bCvCfObjfoEiDe8Ung8GGLGAtthw==" + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + } + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globule": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", + "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", + "dev": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.4", + "minimatch": "~3.0.2" + }, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "graphlib": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.1.tgz", + "integrity": "sha1-QjUsUrovTQNctWbrkfc5X3bryVE=", + "requires": { + "lodash": "^4.11.1" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "^1.0.2" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "dev": true, + "requires": { + "boom": "4.x.x", + "cryptiles": "3.x.x", + "hoek": "4.x.x", + "sntp": "2.x.x" + } + }, + "haxe-modular": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/haxe-modular/-/haxe-modular-0.7.0.tgz", + "integrity": "sha1-MAXknwMXtVlyt9PQXNGr1PLSCPU=", + "requires": { + "acorn": "^4.0.3", + "graphlib": "^2.1.1", + "react-deep-force-update": "^2.0.1", + "react-proxy": "^2.0.8", + "source-map": "^0.5.6" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "history": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", + "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", + "requires": { + "invariant": "^2.2.1", + "loose-envify": "^1.2.0", + "resolve-pathname": "^2.2.0", + "value-equal": "^0.4.0", + "warning": "^3.0.0" + } + }, + "hoek": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", + "dev": true + }, + "hoist-non-react-statics": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz", + "integrity": "sha512-6Bl6XsDT1ntE0lHbIhr4Kp2PGcleGZ66qu5Jqk8lc0Xc/IeG6gVLmwUGs/K0Us+L8VWoKgj0uWdPMataOsm31w==" + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "invariant": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha512-miqftL8E53hH0dtQqLdN+3JwClyJiITcif3gy+RiUlnLJUhEwdyRC29/gpYbuC9IhazGSnP8TjbvxWw2AZylWQ==", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.1.tgz", + "integrity": "sha1-MQ23D3QtJZoWo2kgK1GvhCMzENk=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jquery": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz", + "integrity": "sha1-LInWiJterFIqfuoywUUhVZxsvwI=" + }, + "js-base64": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz", + "integrity": "sha512-Y2/+DnfJJXT1/FCwUebUhLWb3QihxiSC42+ctHLGogmW2jPY6LCapMdFZXRvVP2z6qyKW7s6qncE/9gSqZiArw==", + "dev": true + }, + "js-tokens": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", + "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz", + "integrity": "sha512-xyQpxeWWMKyJps9CuGJYeng6ssI5bpqS9ltQpdVQ90t4ql6NdnxFKh95JcRt2cun/DjMVNrdjniLPuMA69xmCw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": false, + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "leaflet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.3.1.tgz", + "integrity": "sha512-adQOIzh+bfdridLM1xIgJ9VnJbAUY3wqs/ueF+ITla+PLQ1z47USdBKUf+iD9FuUA8RtlT6j6hZBfZoA6mW+XQ==" + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "lodash-es": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.5.tgz", + "integrity": "sha512-Ez3ONp3TK9gX1HYKp6IhetcVybD+2F+Yp6GS9dfH8ue6EOCEzQtQEh4K0FYWBP9qLv+lzeQAYXw+3ySfxyZqkw==" + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "lodash.mergewith": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", + "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=", + "dev": true + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "requires": { + "js-tokens": "^3.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + }, + "dependencies": { + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + } + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", + "dev": true + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "dev": true, + "requires": { + "mime-db": "~1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "moment": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", + "dev": true + }, + "node-fetch": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.1.tgz", + "integrity": "sha512-j8XsFGCLw79vWXkZtMSmmLaOk9z5SQ9bV/tkbZVCqvgwzrjAGq66igobLofHtF63NvMTp2WjytpsNTGKa+XRIQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "node-gyp": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", + "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=", + "dev": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "2", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + } + } + }, + "node-sass": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.5.3.tgz", + "integrity": "sha1-0JydEXlkEjnRuX/8YjH9zsU+FWg=", + "dev": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.3.2", + "node-gyp": "^3.3.1", + "npmlog": "^4.0.0", + "request": "^2.79.0", + "sass-graph": "^2.1.1", + "stdout-stream": "^1.4.0" + }, + "dependencies": { + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "npm-run-all": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.2.tgz", + "integrity": "sha512-Z2aRlajMK4SQ8u19ZA75NZZu7wupfCNQWdYosIi8S6FgBdGf/8Y6Hgyjdc8zU2cYmIRVCx1nM80tJPkdEd+UYg==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "chalk": "^2.1.0", + "cross-spawn": "^5.1.0", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "ps-tree": "^1.1.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true, + "requires": { + "array-filter": "~0.0.0", + "array-map": "~0.0.0", + "array-reduce": "~0.0.0", + "jsonify": "~0.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + } + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "outpipe": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/outpipe/-/outpipe-1.1.1.tgz", + "integrity": "sha1-UM+GFjZeh+Ax4ppeyTOaPaRyX6I=", + "dev": true, + "requires": { + "shell-quote": "^1.4.2" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + } + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "~2.3" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": false, + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "prop-types": { + "version": "15.5.10", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1" + } + }, + "prop-types-extra": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz", + "integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==", + "requires": { + "react-is": "^16.3.2", + "warning": "^3.0.0" + } + }, + "ps-tree": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz", + "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", + "dev": true, + "requires": { + "event-stream": "~3.3.0" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "randomfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.3.tgz", + "integrity": "sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + }, + "dependencies": { + "randombytes": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", + "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + } + } + }, + "react": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react/-/react-15.6.1.tgz", + "integrity": "sha1-uqhDTsZ4C96ZfNw4C3nNM7ljk98=", + "requires": { + "create-react-class": "^15.6.0", + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + }, + "react-bootstrap-typeahead": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/react-bootstrap-typeahead/-/react-bootstrap-typeahead-1.4.2.tgz", + "integrity": "sha1-GRPjLOpNeb7NA6yrvZM2EWiEgVs=", + "requires": { + "classnames": "^2.2.0", + "create-react-class": "^15.5.2", + "invariant": "^2.2.1", + "lodash": "^4.17.2", + "prop-types": "^15.5.8", + "react-highlighter": "^0.4.0", + "react-input-autosize": "^1.1.4", + "react-onclickoutside": "^5.7.0", + "react-overlays": "^0.7.0", + "react-prop-types": "^0.4.0", + "warning": "^3.0.0" + } + }, + "react-datetime": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-datetime/-/react-datetime-2.9.0.tgz", + "integrity": "sha512-gyfogbSC7xF8aK8wIBxrptNchPfM1H72JT1hz1pTTn0VwodKkaIWNTA9KIIsPocxjGpOjvEum5Rt9MvrZz8AtQ==", + "requires": { + "@types/react": ">=15", + "object-assign": "^3.0.0", + "prop-types": "^15.5.7", + "react-onclickoutside": "^5.9.0" + }, + "dependencies": { + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + } + } + }, + "react-deep-force-update": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-deep-force-update/-/react-deep-force-update-2.1.1.tgz", + "integrity": "sha1-jqQmPNZFWgULN0RbPwj9g52G6Qk=" + }, + "react-dom": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.1.tgz", + "integrity": "sha1-LLDtQZEDjlPCCes6eaI+Kkz5lHA=", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + }, + "react-highlighter": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/react-highlighter/-/react-highlighter-0.4.3.tgz", + "integrity": "sha512-dwItRaGRHBceuzZd5NXeroapdmZ2JCAWZ3AdwdthRlSkdtPCY18DWrd6mPmiMCfSB6lgVwwCPQl4unZzG5sXXw==", + "requires": { + "blacklist": "^1.1.4", + "create-react-class": "^15.6.2", + "escape-string-regexp": "^1.0.5", + "prop-types": "^15.6.0" + }, + "dependencies": { + "create-react-class": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", + "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "prop-types": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", + "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "requires": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + } + } + }, + "react-input-autosize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-1.2.0.tgz", + "integrity": "sha1-hyQQcRWfdCEjiXaR2meW7DO1fQU=", + "requires": { + "create-react-class": "^15.5.2", + "prop-types": "^15.5.8" + } + }, + "react-is": { + "version": "16.5.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.5.1.tgz", + "integrity": "sha512-Q7S+9y2lJA9oJCMqLt045f+kLRhsMLA1wW2DAGXA6b7wcTQRHnUDMc5oR49tn0Z4swvnfV+/t8iZFXY74IQmpA==" + }, + "react-leaflet": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-1.8.0.tgz", + "integrity": "sha512-/2mm/ZQodvcknMJpHm0UMbsQIeQVe9TNA62TUYzTWEX6yAqfLxjQmKIio0xKWSoMIRuQ0R1lROvVZFgiHT7KNw==", + "requires": { + "lodash": "^4.0.0", + "lodash-es": "^4.0.0", + "warning": "^3.0.0" + } + }, + "react-onclickoutside": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-5.11.1.tgz", + "integrity": "sha1-ADFOUlZ89V+rqUyrus0RlhkHBiM=", + "requires": { + "create-react-class": "^15.5.x" + } + }, + "react-overlays": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.7.4.tgz", + "integrity": "sha512-7vsooMx3siLAuEfTs8FYeP/lAORWWFXTO8PON3KgX0Htq1Oa+po6ioSjGyO0/GO5CVSMNhpWt6V2opeexHgBuQ==", + "requires": { + "classnames": "^2.2.5", + "dom-helpers": "^3.2.1", + "prop-types": "^15.5.10", + "prop-types-extra": "^1.0.1", + "warning": "^3.0.0" + } + }, + "react-places-autocomplete": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-places-autocomplete/-/react-places-autocomplete-6.0.2.tgz", + "integrity": "sha512-aWCfUL4auCx2Hhq7eabB4EOnK/GtAwFyQaqmnPMY2dLJtSQcKPMu5UMeVU2RXYUy6ySvN1UarYDHbPufSn8IFg==", + "requires": { + "lodash.debounce": "^4.0.8", + "prop-types": "^15.5.8" + } + }, + "react-prop-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", + "integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=", + "requires": { + "warning": "^3.0.0" + } + }, + "react-proxy": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-2.0.8.tgz", + "integrity": "sha1-dq54yb5OVNQJz+x7t9UT7H8bKEw=", + "requires": { + "lodash": "^4.6.1" + } + }, + "react-router": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz", + "integrity": "sha512-DY6pjwRhdARE4TDw7XjxjZsbx9lKmIcyZoZ+SDO7SBJ1KUeWNxT22Kara2AC7u6/c2SYEHlEDLnzBCcNhLE8Vg==", + "requires": { + "history": "^4.7.2", + "hoist-non-react-statics": "^2.3.0", + "invariant": "^2.2.2", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.5.4", + "warning": "^3.0.0" + } + }, + "react-router-dom": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.2.2.tgz", + "integrity": "sha512-cHMFC1ZoLDfEaMFoKTjN7fry/oczMgRt5BKfMAkTu5zEuJvUiPp1J8d0eXSVTnBh6pxlbdqDhozunOOLtmKfPA==", + "requires": { + "history": "^4.7.2", + "invariant": "^2.2.2", + "loose-envify": "^1.3.1", + "prop-types": "^15.5.4", + "react-router": "^4.2.0", + "warning": "^3.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", + "integrity": "sha512-SfBHKFbV8XMg3KaaCxkdxQZ76Si5/ELNRSgg+FDa0fbSLwGxvQDx+aJey1bjEa4FYo1svwdf9q/BsgPuvvkH7w==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.0", + "string_decoder": "~1.0.0", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "regenerate": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", + "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", + "dev": true + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "hawk": "~6.0.2", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "stringstream": "~0.0.5", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve-pathname": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", + "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha512-V0iQEZ/uoem3NmD91rD8XiuozJnq9/ZJnbHVXHnWqP1ucAhS3yJ7sLIIzEi57wFFcK3oi3kFUC46uSyWr35mxg==", + "dev": true, + "requires": { + "array-filter": "~0.0.0", + "array-map": "~0.0.0", + "array-reduce": "~0.0.0", + "jsonify": "~0.0.0" + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "dev": true, + "requires": { + "hoek": "4.x.x" + } + }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true, + "requires": { + "spdx-license-ids": "^1.0.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" + } + }, + "stdout-stream": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", + "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.0.3", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + } + } + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha512-tmNYYHFqXmaKSSlOU4ZbQ82cxmFQa5LRWKFtWCNkGIiZ3/VHmOffCeWfBRZZRyXAhNP9itVMR+cuvomBOPlm8g==", + "dev": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "dev": true, + "requires": { + "punycode": "^1.4.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + } + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "ua-parser-js": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.13.tgz", + "integrity": "sha1-zZ3S+GSTs/RNvu7zeA/adMXuFL4=" + }, + "uglify-js": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.1.9.tgz", + "integrity": "sha512-ari2E89bD7f+fMU173NgF12JBcOhgoxeyuCs97h5K58IBENrnG9eVj2lFadrOPdqf0KifsxVmUQfzA2cHNxCZQ==", + "dev": true, + "requires": { + "commander": "~2.11.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true, + "requires": { + "spdx-correct": "~1.0.0", + "spdx-expression-parse": "~1.0.0" + } + }, + "value-equal": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", + "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + } + } + }, + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchify": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/watchify/-/watchify-3.9.0.tgz", + "integrity": "sha1-8HX9LoqGrN6Eztum5cKgvt1SPZ4=", + "dev": true, + "requires": { + "anymatch": "^1.3.0", + "browserify": "^14.0.0", + "chokidar": "^1.0.0", + "defined": "^1.0.0", + "outpipe": "^1.1.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "whatwg-fetch": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", + "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "dev": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha512-iTwvhNBRetXWe81+VcIw5YeadVSWyze7uA7nVnpP13ulrpnJ3UfQm5ApGnrkmxDJFdrblRdZs0EvaTCIfei5oQ==", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..378164c --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "Cagette.net", + "description": "le logiciel libre du circuit court", + "author": "François Barbut ", + "license": "GPL-2.0", + "homepage": "http://www.cagette.net", + "dependencies": { + "bootstrap": "^3.3.4", + "eonasdan-bootstrap-datetimepicker": "^4.7.14", + "geolib": "^2.0.24", + "haxe-modular": "^0.7.0", + "jquery": "^2.2.4", + "leaflet": "^1.3.1", + "react": "^15.6.1", + "react-bootstrap-typeahead": "1.4.2", + "react-datetime": "^2.9.0", + "react-dom": "^15.6.1", + "react-leaflet": "^1.8.0", + "react-places-autocomplete": "^6.0.2", + "react-router": "^4.2.0", + "react-router-dom": "^4.2.2" + }, + "devDependencies": { + "babel-preset-es2015": "^6.24.1", + "babel-preset-react": "^6.24.1", + "babelify": "^7.3.0", + "browserify": "^14.4.0", + "cross-env": "^5.1.1", + "node-sass": "^4.5.3", + "npm-run-all": "^4.1.2", + "uglify-js": "^3.1.9", + "watchify": "^3.9.0" + }, + "scripts": { + "build": "npm-run-all --parallel build:*", + "build:js": "haxe cagetteJs.hxml", + "build:sass": "node-sass www/css -o www/css", + "watch:sass": "npm run build:sass -- --watch", + "build2": "babel src -d lib", + "libs:dev": "browserify js/libs/libs.js -o www/js/libs.js -d", + "libs:prod": "cross-env NODE_ENV=production browserify js/libs/libs.js | uglifyjs -c -m > www/js/libs.js", + "test": "haxe testsJS.hxml && node js/test/tests.js", + "prepush": "npm test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bablukid/cagette.git" + }, + "bugs": { + "url": "https://github.com/bablukid/cagette/issues" + } +} diff --git a/potGeneration.hxml b/potGeneration.hxml new file mode 100644 index 0000000..6e7ccb9 --- /dev/null +++ b/potGeneration.hxml @@ -0,0 +1,21 @@ +-lib dbadmin +-lib templo +-lib sugoi +-lib hxevents +-lib thx.semver +-lib thx.csv +-lib datetime +-lib hscript +-lib smtpmailer +-lib record-macros +-cp common +-cp src +-cp lang/master/tpl +-neko www/index.n +-main App + +#POT GENERATION +-D i18n_parsing +-D no-deprecation-warnings + + diff --git a/src/App.hx b/src/App.hx new file mode 100755 index 0000000..63cc2e7 --- /dev/null +++ b/src/App.hx @@ -0,0 +1,306 @@ +import db.User; +import thx.semver.Version; +import Common; + +class App extends sugoi.BaseApp { + + public static var current : App = null; + public static var t : sugoi.i18n.translator.ITranslator; + public static var config = sugoi.BaseApp.config; + + public var eventDispatcher :hxevents.Dispatcher; + public var plugins : Array; + + /** + * Version management + * @doc https://github.com/fponticelli/thx.semver + */ + //public static var VERSION = ([0,9,2] : Version).withPre("july"); + public static var VERSION = ([0,9,2] : Version).withPre(MyMacros.getGitShortSHA(), MyMacros.getGitCommitDate()); + + public function new(){ + super(); + } + + public static function main() { + + App.t = sugoi.form.Form.translator = new sugoi.i18n.translator.TMap(getTranslationArray(), "fr"); + sugoi.BaseApp.main(); + } + + /** + * Init plugins and event dispatcher just before launching the app + */ + override public function mainLoop() { + eventDispatcher = new hxevents.Dispatcher(); + plugins = []; + //internal plugins + plugins.push(new plugin.Tutorial()); + + //optionnal plugins + #if plugins + plugins.push( new hosted.HostedPlugIn() ); + plugins.push( new pro.ProPlugIn() ); + plugins.push( new connector.ConnectorPlugIn() ); + plugins.push( new pro.LemonwayEC() ); + plugins.push( new who.WhoPlugIn() ); + #end + + super.mainLoop(); + } + + public function getCurrentGroup(){ + if (session == null) return null; + if (session.data == null ) return null; + var a = session.data.amapId; + if (a == null) { + return null; + }else { + return db.Amap.manager.get(a,false); + } + } + + override function beforeDispatch() { + + //send "current page" event + event( Page(this.uri) ); + + super.beforeDispatch(); + } + + public function getPlugin(name:String):sugoi.plugin.IPlugIn { + for (p in plugins) { + if (p.getName() == name) return p; + } + return null; + } + + public static function log(t:Dynamic) { + if(App.config.DEBUG) { + neko.Web.logMessage(Std.string(t)); //write in Apache error log + #if weblog + Weblog.log(t); //write en Weblog console (https://lib.haxe.org/p/weblog/) + #end + } + } + + public function event(e:Event) { + if(e==null) return null; + this.eventDispatcher.dispatch(e); + return e; + } + + /** + * Translate DB objects fields in forms + */ + public static function getTranslationArray() { + //var t = sugoi.i18n.Locale.texts; + var out = new Map(); + //out.set("firstName", t._("First name") ); + //out.set("lastName", t._("Last name")); + out.set("firstName2", "Prénom du conjoint"); + out.set("lastName2", "Nom du conjoint"); + out.set("email2", "e-mail du conjoint"); + //out.set("pass", t._("Password") ); + //out.set("address1", t._("address") ); + //out.set("address2", t._("address") ); + out.set("zipCode", "code postal"); + out.set("city", "commune"); + out.set("phone", "téléphone"); + out.set("phone2", "téléphone du conjoint"); + out.set("select", "sélectionnez"); + out.set("contract", "Contrat"); + out.set("place", "Lieu"); + out.set("name", "Nom"); + out.set("cdate", "Date d'entrée dans le groupe"); + out.set("quantity", "Quantité"); + out.set("paid", "Payé"); + out.set("user2", "(facultatif) partagé avec "); + out.set("product", "Produit"); + out.set("user", "Adhérent"); + out.set("txtIntro", "Texte de présentation du groupe"); + out.set("txtHome", "Texte en page d'accueil pour les adhérents connectés"); + out.set("txtDistrib", "Texte à faire figurer sur les listes d'émargement lors des distributions"); + out.set("extUrl", "URL du site du groupe."); + + out.set("distributor1", "Distributeur 1"); + out.set("distributor2", "Distributeur 2"); + out.set("distributor3", "Distributeur 3"); + out.set("distributor4", "Distributeur 4"); + out.set("distributorNum", "Nbre de distributeurs nécessaires (de 0 à 4)"); + + out.set("startDate", "Date de début"); + out.set("endDate", "Date de fin"); + + out.set("orderStartDate", "Date ouverture des commandes"); + out.set("orderEndDate", "Date fermeture des commandes"); + out.set("openingHour", "Heure d'ouverture"); + out.set("closingHour", "Heure de fermeture"); + + out.set("date", "Date de distribution"); + out.set("active", "actif"); + + out.set("contact", "Reponsable"); + out.set("vendor", "Producteur"); + out.set("text", "Texte"); + out.set("flags", "Options"); + out.set("4h", "Recevoir des notifications par email 4h avant les distributions"); + out.set("HasEmailNotif4h", "Recevoir des notifications par email 4h avant les distributions"); + out.set("24h", "Recevoir des notifications par email 24h avant les distributions"); + out.set("HasEmailNotif24h", "Recevoir des notifications par email 24h avant les distributions"); + out.set("Ouverture", "Recevoir des notifications par email pour l'ouverture des commandes"); + out.set("Tuto", "Activer tutoriels"); + out.set("HasMembership", "Gestion des adhésions"); + out.set("DayOfWeek", "Jour de la semaine"); + out.set("Monday", "Lundi"); + out.set("Tuesday", "Mardi"); + out.set("Wednesday", "Mercredi"); + out.set("Thursday", "Jeudi"); + out.set("Friday", "Vendredi"); + out.set("Saturday", "Samedi"); + out.set("Sunday", "Dimanche"); + out.set("cycleType", "Récurrence"); + out.set("Weekly", "hebdomadaire"); + out.set("Monthly", "mensuelle"); + out.set("BiWeekly", "toutes les deux semaines"); + out.set("TriWeekly", "toutes les 3 semaines"); + out.set("price", "prix TTC"); + out.set("uname", "Nom"); + out.set("pname", "Produit"); + out.set("organic", "Agriculture biologique"); + out.set("hasFloatQt", "Autoriser quantités \"à virgule\""); + + out.set("membershipRenewalDate", "Adhésions : Date de renouvellement"); + out.set("membershipPrice", "Adhésions : Coût de l'adhésion"); + out.set("UsersCanOrder", "Les adhérents peuvent saisir leur commande en ligne"); + out.set("StockManagement", "Gestion des stocks"); + out.set("contact", "Responsable"); + out.set("PercentageOnOrders", "Ajouter des frais au pourcentage de la commande"); + out.set("percentageValue", "Pourcentage des frais"); + out.set("percentageName", "Libellé pour ces frais"); + out.set("fees", "frais"); + out.set("AmapAdmin", "Administrateur du groupe"); + out.set("Membership", "Accès à la gestion des adhérents"); + out.set("Messages", "Accès à la messagerie"); + out.set("vat", "TVA"); + out.set("desc", "Description"); + out.set("ShopMode", "Mode boutique"); + out.set("ComputeMargin", "Appliquer une marge à la place des pourcentages"); + out.set("ShopCategoriesFromTaxonomy", "Catégoriser automatiquement les produits"); + out.set("HidePhone", "Masquer le téléphone du responsable sur la page publique"); + out.set("PhoneRequired", "Saisie du numéro de téléphone obligatoire"); + out.set("ref", "Référence"); + out.set("linkText", "Intitulé du lien"); + out.set("linkUrl", "URL du lien"); + + out.set("Amap", "AMAP"); + out.set("GroupedOrders", "Groupement d'achat"); + out.set("ProducerDrive", "Collectif de producteurs"); + out.set("FarmShop", "Vente à la ferme"); + + out.set("regOption", "Inscription de nouveaux adhérents"); + out.set("Closed", "Fermé : Le coordinateur ajoute les nouveaux adhérents"); + out.set("WaitingList", "Liste d'attente"); + out.set("Open", "Ouvert : tout le monde peut s'inscrire"); + out.set("Full", "Complet : Le groupe n'accepte plus de nouveaux adhérents"); + out.set("percent", "Pourcentage"); + out.set("pinned", "Mets en avant les produits"); + + out.set("CagetteNetwork", "Me lister dans l'annuaire des groupes Cagette.net"); + out.set("unitType", "Unité"); + out.set("qt", "Quantité"); + out.set("Unit", "Pièce"); + out.set("Kilogram", "Kilogrammes"); + out.set("Gram", "Grammes"); + out.set("Litre", "Litres"); + out.set("htPrice", "Prix H.T"); + out.set("amount", "Montant"); + + out.set("HasPayments", "Gestion des paiements"); + + out.set("byMember", "Par adhérent"); + out.set("byProduct", "Par produit"); + + out.set("variablePrice", "Variable price based on weight"); + return out; + } + + public function populateAmapMembers() { + return user.amap.getMembersFormElementData(); + } + + public static function getMailer():sugoi.mail.IMailer { + + var mailer : sugoi.mail.IMailer = new sugoi.mail.BufferedMailer(); + + if(App.config.DEBUG){ + + //Dev env : emails are written to tmp folder + mailer = new sugoi.mail.DebugMailer(); + }else{ + + if (sugoi.db.Variable.get("mailer") == null){ + var msg = sugoi.i18n.Locale.texts._("Please configure the email settings in a this section"); + throw sugoi.ControllerAction.ErrorAction("/",msg); + } + + if (sugoi.db.Variable.get("mailer") == "mandrill"){ + //Buffered emails with Mandrill + untyped mailer.defineFinalMailer("mandrill"); + }else{ + //Buffered emails with SMTP + untyped mailer.defineFinalMailer("smtp"); + } + } + return mailer; + } + + /** + * Send an email + */ + public static function sendMail(m:sugoi.mail.Mail, ?group:db.Amap, ?listId:String, ?sender:db.User){ + + if (group == null) group = App.current.user == null ? null:App.current.user.getAmap(); + + current.event(SendEmail(m)); + + var params = group==null ? null : {remoteId:group.id}; + + getMailer().send(m,params,function(o){}); + + } + + public static function quickMail(to:String, subject:String, html:String,?group:db.Amap){ + var e = new sugoi.mail.Mail(); + e.setSubject(subject); + e.setRecipient(to); + e.setSender(App.config.get("default_email"),"Cagette.net"); + var html = App.current.processTemplate("mail/message.mtt", {text:html,group:group}); + e.setHtmlBody(html); + App.sendMail(e); + } + + /** + * process a template and returns the generated string + * @param tpl + * @param ctx + */ + public function processTemplate(tpl:String, ctx:Dynamic):String { + + Reflect.setField(ctx, 'HOST', App.config.HOST); + Reflect.setField(ctx, 'hDate', App.current.view.hDate); + //i18n functions + ctx._ = App.current.view._; + ctx.__ = App.current.view.__; + + var tpl = loadTemplate(tpl); + var html = tpl.execute(ctx); + #if php + if ( html.substr(0, 4) == "null") html = html.substr(4); + #end + return html; + } + + + +} diff --git a/src/Calendar.hx b/src/Calendar.hx new file mode 100644 index 0000000..42470bf --- /dev/null +++ b/src/Calendar.hx @@ -0,0 +1,83 @@ +package; + +typedef CalEvent = { + + name:String, + //start:Date, + //end:Date, + color:Int, + +} + +/** + * Calendar utility + * + * @author fbarbut + */ +class Calendar +{ + + public static var COLOR_CONTRACT = 0xC91F25; + public static var COLOR_DELIVERY = 0x7BAD1C; + public static var COLOR_ORDER = 0xFF9615; + + public function new() + { + + } + + public static function getMonthViewMap():Map> { + + var n = Date.now(); + var m = n.getMonth();//0-11 + var pointer = Date.now(); + + var out = new Map>(); + + //find last monday + for ( i in 0...40) { + if ( pointer.getDay() == 1 ) { + break; + } + pointer = DateTools.delta(pointer, -1000.0 * 60 * 60 * 24); + + } + + //go ahead for at least 27 days + for ( i in 0...28) { + out.set( pointer.toString().substr(0, 10), [] ); + pointer = DateTools.delta(pointer, 1000.0 * 60 * 60 * 24); + } + + //find end + for ( i in 0...40) { + //if ( pointer.getDay() == 1 && pointer.getMonth() != m) break; + out.set( pointer.toString().substr(0, 10), [] ); + pointer = DateTools.delta(pointer, 1000.0 * 60 * 60 * 24); + } + + return out; + } + + /** + * get ordered CalEvents from an unordered stringMap + */ + public static function mapToArray(input : Map>) : Array<{d:Date,events:Array}> { + + var keys = []; + for (k in input.keys()) keys.push(k); + keys.sort(function(a, b) { + return Math.round(Date.fromString(a).getTime()/1000) - Math.round(Date.fromString(b).getTime()/1000); + }); + + + var out = []; + for ( k in keys) { + + var x = input.get(k); + out.push( { d:Date.fromString(k),events:x } ); + } + return out; + } + +} \ No newline at end of file diff --git a/src/MultiDistrib.hx b/src/MultiDistrib.hx new file mode 100644 index 0000000..bd91a02 --- /dev/null +++ b/src/MultiDistrib.hx @@ -0,0 +1,385 @@ +package; +import Common; +using tools.ObjectListTool; +using Lambda; +using tools.ObjectListTool; + +/** + * MultiDistrib represents many db.Distribution + which happen on the same day + same place. + * + * @author fbarbut + */ +class MultiDistrib +{ + public var distributions : Array; + public var contracts : Array; + //public var actions : Array; + public var extraHtml : String; + public var type : Null; //contract type, both contract types cannot be mixed in a same multidistrib. + + public function new(){ + distributions = []; + contracts = []; + extraHtml = ""; + //actions = []; + } + + public static function get(date:Date, place:db.Place,contractType:Int){ + var m = new MultiDistrib(); + + var start = tools.DateTool.setHourMinute(date, 0, 0); + var end = tools.DateTool.setHourMinute(date, 23, 59); + + var contracts = place.amap.getContracts().array(); + + //filter by type + if(contractType==db.Contract.TYPE_VARORDER){ + for(c in contracts.copy() ){ + if(c.type!=db.Contract.TYPE_VARORDER) contracts.remove(c); + } + }else if(contractType==db.Contract.TYPE_CONSTORDERS){ + for(c in contracts.copy() ){ + if(c.type!=db.Contract.TYPE_CONSTORDERS) contracts.remove(c); + } + } + var cids = contracts.getIds(); + m.distributions = db.Distribution.manager.search(($contractId in cids) && ($date >= start) && ($date <= end) && $place==place, { orderBy:date }, false).array(); + m.type = contractType; + return m; + } + + /** + Get multidistribs from a time range + place + type + **/ + public static function getFromTimeRange(group:db.Amap,from:Date,to:Date,?contractType:Int):Array{ + var multidistribs = []; + var start = tools.DateTool.setHourMinute(from, 0, 0); + var end = tools.DateTool.setHourMinute(to, 23, 59); + var cids = group.getContracts().getIds(); + var distributions = db.Distribution.manager.search(($contractId in cids) && ($date >= start) && ($date <= end) , { orderBy:date }, false).array(); + + //sort by day-place-type + var multidistribs = new Map(); + for ( d in distributions){ + + //filter by contractType + if(contractType!=null){ + if(d.contract.type!=contractType) continue; + } + + var key = d.getKey() + "-" + d.contract.type; + + if(multidistribs[key]==null){ + var m = new MultiDistrib(); + m.distributions.push(d); + m.type = d.contract.type; + multidistribs[key] = m; + }else{ + multidistribs[key].distributions.push(d); + } + } + var multidistribs = Lambda.array(multidistribs); + + //trigger event + for(md in multidistribs) App.current.event(MultiDistribEvent(md)); + + //sort by date desc + multidistribs.sort(function(x,y){ + return Math.round( x.getDate().getTime()/1000 ) - Math.round(y.getDate().getTime()/1000 ); + }); + + return multidistribs; + } + + /** + * TODO : refacto this to use getFromTimeRange(); + */ + /*public static function getNextMultiDeliveries(group:db.Amap){ + + var out = new Map < String, { + place:db.Place, //common delivery place + startDate:Date, //global delivery start + endDate:Date, //global delivery stop + orderStartDate:Date, //global orders opening date + orderEndDate:Date, //global orders closing date + active:Bool, + products:Array, //available products ( if no order ) + myOrders:Array<{distrib:db.Distribution,orders:Array}> //my orders + }>(); + + var n = Date.now(); + var now = new Date(n.getFullYear(), n.getMonth(), n.getDate(), 0, 0, 0); + + var contracts = db.Contract.getActiveContracts(group); + var cids = Lambda.map(contracts, function(p) return p.id); + + //var pids = Lambda.map(db.Product.manager.search($contractId in cids,false), function(x) return x.id); + //var out = UserContract.manager.search(($userId == id || $userId2 == id) && $productId in pids, lock); + + //available deliveries + var inSixMonth = DateTools.delta(now, 1000.0 * 60 * 60 * 24 * 30 * 6); + var distribs = db.Distribution.manager.search(($contractId in cids) && $date >= now && $date <= inSixMonth , { orderBy:date }, false); + + for (d in distribs) { + + //we had the distribution key ( place+date ) and the contract type in order to separate constant and varying contracts + var key = d.getKey() + "|" + d.contract.type; + var o = out.get(key); + if (o == null) o = {place:d.place, startDate:d.date, active:null, endDate:d.end, products:[], myOrders:[], orderStartDate:null,orderEndDate:null}; + + //user orders + var orders = []; + if(App.current.user!=null) orders = d.contract.getUserOrders(App.current.user,d); + if (orders.length > 0){ + o.myOrders.push({distrib:d,orders:service.OrderService.prepare(orders)}); + }else{ + //no "order block" if no shop mode + if (!group.hasShopMode() ) { + continue; + } + + //if its a constant order contract, skip this delivery + if (d.contract.type == db.Contract.TYPE_CONSTORDERS){ + continue; + } + + //products preview if no orders + for ( p in d.contract.getProductsPreview(9)){ + o.products.push( p.infos(null,false) ); + } + } + + if (d.contract.type == db.Contract.TYPE_VARORDER){ + + //old distribs may have an empty orderStartDate + if (d.orderStartDate == null) { + continue; + } + + //if order opening is more far than 1 month, skip it + // if (d.orderStartDate.getTime() > inOneMonth.getTime() ){ + // continue; + // } + + //display closest opening date + if (o.orderStartDate == null){ + o.orderStartDate = d.orderStartDate; + }else if (o.orderStartDate.getTime() > d.orderStartDate.getTime()){ + o.orderStartDate = d.orderStartDate; + } + + //display most far closing date + if (o.orderEndDate == null){ + o.orderEndDate = d.orderEndDate; + }else if (o.orderEndDate.getTime() < d.orderEndDate.getTime()){ + o.orderEndDate = d.orderEndDate; + } + + out.set(key, o); + + }else{ + //in constant orders, add block only if there is an order + if(o.myOrders.length>0) out.set(key, o); + + } + } + + //shuffle and limit product lists + for ( o in out){ + o.products = thx.Arrays.shuffle(o.products); + o.products = o.products.slice(0, 9); + } + + //decide if active or not + var now = Date.now(); + for( o in out){ + + if (o.orderStartDate == null) continue; //constant orders + + if (now.getTime() >= o.orderStartDate.getTime() && now.getTime() <= o.orderEndDate.getTime() ){ + //order currently open + o.active = true; + + }else { + o.active = false; + + } + } + + return Lambda.array(out); + }*/ + + public function getPlace(){ + if(distributions.length==0) throw "This multidistrib is empty"; + return distributions[0].place; + } + + public function getDate(){ + if(distributions.length==0) throw "This multidistrib is empty"; + return distributions[0].date; + } + + public function getEndDate(){ + if(distributions.length==0) throw "This multidistrib is empty"; + return distributions[0].end; + } + + public function getProductsExcerpt():Array{ + var key = "productsExcerpt-"+getKey(); + var cache:Array = sugoi.db.Cache.get(key); + if(cache!=null){ + var out = []; + //try{ + for( pid in cache.array()){ + var p = db.Product.manager.get(pid,false); + if(p!=null) out.push(p.infos()); + } + //}catch(e:Dynamic){ + // sugoi.db.Cache.destroy(key); + // } + + return out; + } + + var products = []; + for( d in distributions){ + for ( p in d.contract.getProductsPreview(9)){ + products.push( p.infos(null,false) ); + } + } + products = thx.Arrays.shuffle(products); + products = products.slice(0, 9); + sugoi.db.Cache.set(key, products.map(function(p)return p.id).array(), 3600 ); + return products; + + } + + public function userHasOrders(user:db.User):Bool{ + if(user==null) return false; + for ( d in distributions){ + if(d.getUserOrders(user).length>0) return true; + } + return false; + } + + /** + orders currently open ? + **/ + public function isActive(){ + + if (getOrdersStartDate() == null) return false; //constant orders + + var now = Date.now(); + if (now.getTime() >= getOrdersStartDate().getTime() && now.getTime() <= getOrdersEndDate().getTime() ){ + return true; + }else { + return false; + } + } + + public function getOrdersStartDate(){ + var date = null; + + for( d in distributions ){ + if(d.orderStartDate==null) continue; + //display closest opening date + if (date == null){ + date = d.orderStartDate; + }else if (date.getTime() > d.orderStartDate.getTime()){ + date = d.orderStartDate; + } + } + return date; + } + + /*public function hasOnlyConstantOrders(){ + for(d in distributions){ + if( d.contract.type==db.Contract.TYPE_VARORDER ) { + return false; + } + } + return true; + }*/ + + public function getOrdersEndDate(){ + var date = null; + + for( d in distributions ){ + if(d.orderEndDate==null) continue; + //display most far closing date + if (date == null){ + date = d.orderEndDate; + }else if (date.getTime() < d.orderEndDate.getTime()){ + date = d.orderEndDate; + } + } + return date; + } + + /** + * Get all orders involved in this multidistrib + */ + public function getOrders(){ + var out = []; + for ( d in distributions){ + out = out.concat(d.getOrders().array()); + } + return out; + } + + /** + * Get orders for a user in this multidistrib + * @param user + */ + public function getUserOrders(user:db.User){ + var out = []; + for ( d in distributions){ + var pids = Lambda.map( d.contract.getProducts(false), function(x) return x.id); + var userOrders = db.UserContract.manager.search( $userId == user.id && $distributionId==d.id && $productId in pids , false); + for( o in userOrders ){ + out.push(o); + } + } + return out; + } + + public function getUsers(){ + var users = []; + for ( o in getOrders()) users.push(o.user); + return users.deduplicate(); + } + + + + public function isConfirmed():Bool{ + //cannot be in future + if(getDate().getTime()>Date.now().getTime()) return false; + + return Lambda.count( distributions, function(d) return d.validated) == distributions.length; + } + + public function checkConfirmed():Bool{ + var orders = getOrders(); + var c = Lambda.count( orders, function(d) return d.paid) == orders.length; + + if (c){ + for ( d in distributions){ + if (!d.validated){ + d.lock(); + d.validated = true; + d.update(); + } + } + } + + return c; + } + + //get key by date-place-type + public function getKey(){ + return distributions[0].getKey() + "-" + distributions[0].contract.type; + } + + +} \ No newline at end of file diff --git a/src/MyMacros.hx b/src/MyMacros.hx new file mode 100644 index 0000000..3432729 --- /dev/null +++ b/src/MyMacros.hx @@ -0,0 +1,82 @@ +import haxe.macro.Expr; +import haxe.macro.Context; +import StringTools; + +class MyMacros { + /* based on + http://code.haxe.org/category/macros/add-git-commit-hash-in-build.html + http://stackoverflow.com/questions/8611486/how-to-get-the-last-commit-date-for-a-bunch-of-files-in-git*/ + public static macro function getGitCommitDate():haxe.macro.ExprOf { + #if !display + var process = new sys.io.Process('git', ['log', '-1', '--format=%ci']); + if (process.exitCode() != 0) { + var message = process.stderr.readAll().toString(); + var pos = haxe.macro.Context.currentPos(); + Context.error("Cannot execute `git log -1 --format=%ci`. " + message, pos); + } + + // read the output of the process + var commitDate:String = process.stdout.readLine(); + commitDate = StringTools.replace(commitDate, " ", "."); + commitDate = StringTools.replace(commitDate, "-", "."); + commitDate = StringTools.replace(commitDate, ":", "."); + commitDate = commitDate.substr(0,16); + + // Generates a string expression + return macro $v{commitDate}; + #else + // `#if display` is used for code completion. In this case returning an + // empty string is good enough; We don't want to call git on every hint. + var commitDate:String = ""; + return macro $v{commitDate}; + #end + } + + public static macro function getGitShortSHA():haxe.macro.ExprOf { + #if !display + var process = new sys.io.Process('git', ['log', '-1', '--format=%h']); + if (process.exitCode() != 0) { + var message = process.stderr.readAll().toString(); + var pos = haxe.macro.Context.currentPos(); + Context.error("Cannot execute `git log -1 --format=%h`. " + message, pos); + } + + // read the output of the process + var commitShortSHA:String = process.stdout.readLine(); + commitShortSHA = "C"+commitShortSHA; + + // Generates a string expression + return macro $v{commitShortSHA}; + #else + // `#if display` is used for code completion. In this case returning an + // empty string is good enough; We don't want to call git on every hint. + var commitShortSHA:String = "xxxxxxx"; + return macro $v{commitShortSHA}; + #end + } + + public static macro function getGitBranch():haxe.macro.ExprOf { + #if !display + var process = new sys.io.Process('git', ['symbolic-ref', 'HEAD', '--short']); + if (process.exitCode() != 0) { + var message = process.stderr.readAll().toString(); + var pos = haxe.macro.Context.currentPos(); + Context.error("Cannot execute `git symbolic-ref HEAD --short`. " + message, pos); + } + + // read the output of the process + var commitBranch:String = process.stdout.readLine(); + commitBranch = StringTools.replace(commitBranch, "-", "."); + commitBranch = StringTools.replace(commitBranch, "_", "."); + + // Generates a string expression + return macro $v{commitBranch}; + #else + // `#if display` is used for code completion. In this case returning an + // empty string is good enough; We don't want to call git on every hint. + var commitBranch:String = ""; + return macro $v{commitBranch}; + #end + } + +} diff --git a/src/View.hx b/src/View.hx new file mode 100755 index 0000000..34c1f4c --- /dev/null +++ b/src/View.hx @@ -0,0 +1,318 @@ +using Std; +import Common; +import haxe.Utf8; +import tools.ArrayTool; + +class View extends sugoi.BaseView { + + var t : sugoi.i18n.GetText; + + public function new() { + super(); + this.Std = Std; + this.Date = Date; + this.Web = sugoi.Web; + this.Lambda = Lambda; + this.VERSION = App.VERSION.toString(); + this.ArrayTool = ArrayTool; + this.t = sugoi.i18n.Locale.texts; + } + + public function count(i) { + return Lambda.count(i); + } + + public function abs(n){ + return Math.abs(n); + } + + /** + * init view in main loop, just before rendering + */ + override function init() { + super.init(); + + //tuto widget display + var u = App.current.user; + if (u!=null && u.tutoState!=null) { + //trace("view init "+u.tutoState.name+" , "+u.tutoState.step); + this.displayTuto(u.tutoState.name, u.tutoState.step); + } + } + + function getCurrentGroup(){ + return App.current.getCurrentGroup(); + } + + + function getUser(uid:Int):db.User { + return db.User.manager.get(uid, false); + } + + function getProduct (pid:Int){ + return db.Product.manager.get(pid, false); + } + + + /** + * Round a number to r digits after coma. + * + * @param n + * @param r + */ + public function roundTo(n:Float, r:Int):Float { + return Math.round(n * Math.pow(10,r)) / Math.pow(10,r) ; + } + + + public function color(id:Int) { + if (id == null) throw "color cant be null"; + //try{ + return intToHex(db.CategoryGroup.COLORS[id]); + //}catch (e:Dynamic) return "#000000"; + } + + /** + * convert a RVB color from Int to Hexa + * @param c + * @param leadingZeros=6 + */ + public function intToHex(c:Int, ?leadingZeros=6):String { + var h = StringTools.hex(c); + while (h.length1,80 + if (out.indexOf(".")!=-1 && out.split(".")[1].length == 1) out = out +"0"; + + //french : replace point by comma + return out.split(".").join(","); + } + + /** + * Price per Kg/Liter... + * @param qt + * @param unit + */ + public function pricePerUnit(price:Float,qt:Float, unit:Unit){ + if (unit==null || qt == null || qt == 0 || price==null || price==0) return ""; + var _price = price / qt; + var _unit = unit; + + //turn small prices in Kg + if (_price < 1 ){ + switch(unit){ + case Gram: + _price *= 1000; + _unit = Kilogram; + case Centilitre: + _price *= 100; + _unit = Litre; + default : + } + } + return formatNum(_price) + " " + currency() + "/" + this.unit(_unit); + } + + /** + * clean numbers in views + * to avoid bugs like : 13.79 - 13.79 = 1.77635683940025e-15 + */ + public function numClean(f:Float):Float{ + return Math.round(f * 100) / 100; + } + + /** + * max length for strings, usefull for tables + */ + public function short(text:String, length:Int){ + if (Utf8.length(text) > length){ + + return Utf8.sub(text,0, length)+"…"; + }else{ + return text; + } + } + + public function isToday(d:Date) { + var n = Date.now(); + return d.getDate() == n.getDate() && d.getMonth() == n.getMonth() && d.getFullYear() == n.getFullYear(); + } + + /** + * Prints a measuring unit + */ + public function unit(u:Unit,?plural=false){ + t = sugoi.i18n.Locale.texts; + return switch(u){ + case Kilogram: t._("Kg.||kilogramms"); + case Gram: t._("g.||gramms"); + case null,Piece: if(plural) t._("pieces||unit of a product)") else t._("piece||unit of a product)"); + case Litre: t._("L.||liter"); + case Centilitre: t._("cl.||centiliter"); + } + } + + public function currency(){ + if (App.current.user == null || App.current.user.amap == null){ + return "€"; + }else{ + return App.current.user.amap.getCurrency(); + } + + } + + public static var DAYS = null; + public static var MONTHS = null; + public static var HOURS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]; + public static var MINUTES = [0,5,10,15,20,25,30,35,40,45,50,55]; + + + public function initDate(){ + t = sugoi.i18n.Locale.texts; + DAYS = [t._("Sunday"), t._("Monday"), t._("Tuesday"), t._("Wednesday"), t._("Thursday"), t._("Friday"), t._("Saturday")]; + MONTHS = [t._("January"), t._("February"), t._("March"), t._("April"), t._("May"), t._("June"), t._("July"), t._("August"), t._("September"), t._("October"), t._("November"), t._("December")]; + this.DAYS = DAYS; + this.MONTHS = MONTHS; + this.HOURS = HOURS; + this.MINUTES = MINUTES; + } + + /** + * human readable date + time + */ + public function hDate(date:Date):String { + if (date == null) return t._("no date set"); + if (DAYS == null) initDate(); + + var out = DAYS[date.getDay()] + " " + date.getDate() + " " + MONTHS[date.getMonth()]; + out += " " + date.getFullYear(); + if ( date.getHours() != 0 || date.getMinutes() != 0){ + + out += " " + sugoi.i18n.Locale.texts._("at||time : at 12:30") + " " + StringTools.lpad(Std.string(date.getHours()), "0", 2) + ":" + StringTools.lpad(Std.string(date.getMinutes()), "0", 2); + } + return out; + } + + /** + * Human readable hour + */ + public function hHour(date:Date){ + return StringTools.lpad(date.getHours().string(), "0", 2) + ":" + StringTools.lpad(date.getMinutes().string(), "0", 2); + } + + public function oHour(hour:Int,min:Int){ + return StringTools.lpad(hour.string(), "0", 2) + ":" + StringTools.lpad(min.string(), "0", 2); + } + + /** + * human readable date + */ + public function dDate(date:Date):String { + if (date == null) return t._("no date set"); + if (DAYS == null) initDate(); + + return DAYS[date.getDay()] + " " + date.getDate() + " " + MONTHS[date.getMonth()] + " " + date.getFullYear(); + } + + + public function getDate(date:Date) { + if (date == null) throw "date is null"; + if (DAYS == null) initDate(); + + return { + dow: DAYS[date.getDay()], + d : date.getDate(), + m: MONTHS[date.getMonth()], + y: date.getFullYear(), + h: StringTools.lpad(Std.string(date.getHours()),"0",2), + i: StringTools.lpad(Std.string(date.getMinutes()),"0",2) + }; + } + + + + public function getProductImage(e):String { + return Std.string(e).substr(2).toLowerCase()+".png"; + } + + public function prepare(orders:Iterable){ + return service.OrderService.prepare(orders); + } + + + public function displayTuto(tuto:String, step:Int) { + if (tuto == null) return; + var t = plugin.Tutorial.all().get(tuto); + + //check if we are on the correct page (last step page) + //otherwise the popovers could be displayed on wrong elements + var previous = t.steps[step - 1]; + if (previous != null) { + switch(previous.action) { + case TAPage(uri): + var here = sugoi.Web.getURI(); + if (!plugin.Tutorial.match(uri,here)) { + return; + } + default: + } + } + this.tuto = { name:tuto, step:step }; + } + + /** + * renvoie 0 si c'est user.firstName qui est connecté, + * renvoie 1 si c'est user.firstName2 qui est connecté + * @return + */ + public function whichUser():Int { + if (App.current.session.data == null) return 0; + return App.current.session.data.whichUser == null?0:App.current.session.data.whichUser; + + } + + + public function isAmap(){ + return App.current.user.amap.groupType == db.Amap.GroupType.Amap; + } + + + public function getBasket(userId, placeId, date){ + var user = getUser(userId); + var place = db.Place.manager.get(placeId, false); + return db.Basket.getOrCreate(user, place, date); + } + + public function getPlatform(){ + return #if neko "Neko" #else "PHP" #end ; + } + + /** + * Smart quantity (tm) : displays human readable quantity + * 0.33 x Lemon 12kg => 2kg Lemon + */ + public function smartQt(orderQt:Float, productQt:Float, unit:Unit):String{ + if (orderQt == null) orderQt = 1; + if (productQt == null) productQt = 1; + if (unit == null) unit = Unit.Piece; + if (unit == Unit.Piece && productQt == 1 ){ + return this.formatNum(orderQt); + }else{ + return this.formatNum(orderQt * productQt) + " " + this.unit(unit,orderQt*productQt>1); + } + + } + +} diff --git a/src/controller/Account.hx b/src/controller/Account.hx new file mode 100755 index 0000000..2db71d4 --- /dev/null +++ b/src/controller/Account.hx @@ -0,0 +1,100 @@ +package controller; +import sugoi.form.Form; + +class Account extends Controller +{ + + public function new() + { + super(); + + } + + + function doDefault() { + } + + + @tpl('form.mtt') + function doEdit() { + + var form = sugoi.form.Form.fromSpod(app.user); + form.removeElement(form.getElement("lang")); + form.removeElement(form.getElement("pass")); + form.removeElement(form.getElement("rights")); + form.removeElement(form.getElement("cdate")); + form.removeElement(form.getElement("ldate")); + form.removeElement( form.getElement("apiKey") ); + + if (form.isValid()) { + + if (app.user.id != form.getValueOf("id")) { + throw "access forbidden"; + } + var admin = app.user.isAdmin(); + + form.toSpod(app.user); + + //check email is valid + if (!sugoi.form.validators.EmailValidator.check(app.user.email)){ + throw Error("/account/edit", t._("Email ::em:: is invalid", {em:app.user.email})); + } + + if (app.user.email2!=null && !sugoi.form.validators.EmailValidator.check(app.user.email2)){ + throw Error("/account/edit", t._("Email ::em:: is invalid", {em:app.user.email2})); + } + + //check email is available + var sameEmail = db.User.getSameEmail(app.user.email,app.user.email2); + if( sameEmail.length > 0 && sameEmail.first().id!=app.user.id){ + throw Error("/account/edit", t._("This email is already used by another account.")); + } + + if (!admin) { app.user.rights.unset(Admin); } + + app.user.update(); + throw Ok('/contract', t._("Your account has been updated")); + } + + view.form = form; + view.title = t._("Modify my account"); + } + + function doQuit(){ + + if (checkToken()){ + + var name = app.user.amap.name; + + var ua = db.UserAmap.get(app.user, app.user.amap,true); + ua.delete(); + + App.current.session.data.amapId = null; + throw Ok("/user/choose?show=1", t._("You left the group ::groupName::", {groupName:name})); + + } + + } + + /** + * user payments history + */ + @tpl('account/payments.mtt') + function doPayments(){ + var m = app.user; + var browse:Int->Int->List; + + //default display + browse = function(index:Int, limit:Int) { + return db.Operation.getOperationsWithIndex(m,app.user.amap,index,limit,true); + } + + var count = db.Operation.countOperations(m,app.user.amap); + var rb = new sugoi.tools.ResultsBrowser(count, 10, browse); + view.rb = rb; + view.member = m; + view.balance = db.UserAmap.get(m,app.user.amap).balance; + } + + +} \ No newline at end of file diff --git a/src/controller/Amap.hx b/src/controller/Amap.hx new file mode 100755 index 0000000..443f98e --- /dev/null +++ b/src/controller/Amap.hx @@ -0,0 +1,53 @@ +package controller; +import db.UserContract; +import sugoi.form.Form; + +class Amap extends Controller +{ + + public function new() + { + super(); + } + + @tpl("amap/default.mtt") + function doDefault() { + var contracts = db.Contract.getActiveContracts(app.user.amap, true, false); + for ( c in Lambda.array(contracts).copy()) { + if (c.endDate.getTime() < Date.now().getTime() ) contracts.remove(c); + } + view.contracts = contracts; + } + + @tpl("form.mtt") + function doEdit() { + + if (!app.user.isAmapManager()) throw t._("You don't have access to this section"); + + var group = app.user.amap; + + var form = Form.fromSpod(group); + + if (form.checkToken()) { + + if(form.getValueOf("id") != app.user.amap.id) { + var editedGroup = db.Amap.manager.get(form.getValueOf("id"),false); + throw Error("/amap/edit",'Erreur, vous êtes en train de modifier "${editedGroup.name}" alors que vous êtes connecté à "${app.user.amap.name}"'); + } + + form.toSpod(group); + + if (group.extUrl != null){ + if ( group.extUrl.indexOf("http://") ==-1 && group.extUrl.indexOf("https://") ==-1 ){ + group.extUrl = "http://" + group.extUrl; + } + } + + group.update(); + throw Ok("/amapadmin", t._("The group has been updated.")); + } + + view.form = form; + } + +} \ No newline at end of file diff --git a/src/controller/AmapAdmin.hx b/src/controller/AmapAdmin.hx new file mode 100755 index 0000000..18990e0 --- /dev/null +++ b/src/controller/AmapAdmin.hx @@ -0,0 +1,367 @@ +package controller; +import db.UserAmap; +import haxe.Http; +import neko.Web; +import sugoi.form.Form; +import Common; +import sugoi.form.elements.IntSelect; +import sugoi.form.elements.StringInput; + + +class AmapAdmin extends Controller +{ + + public function new() + { + super(); + if (!app.user.isAmapManager()) throw Error("/", t._("Access forbidden")); + + //lance un event pour demander aux plugins si ils veulent ajouter un item dans la nav + var nav = new Array(); + + if (app.user.amap.hasPayments()){ + nav.push({id:"payments",link:"/amapadmin/payments",name: t._("Payments") }); + } + + var e = Nav(nav,"groupAdmin"); + app.event(e); + view.nav = e.getParameters()[0]; + } + + + @tpl("amapadmin/default.mtt") + function doDefault() { + view.membersNum = UserAmap.manager.count($amap == app.user.amap); + view.contractsNum = app.user.amap.getActiveContracts().length; + + //ping cagette groups directory + if (Std.random(10) == 0 && app.user.amap.flags.has(db.Amap.AmapFlags.CagetteNetwork)){ + var req = new Http("http://annuaire.cagette.net/api/ping?url="+StringTools.urlEncode( "http://" + App.config.HOST ) ); + try{ + req.request(); + }catch (e:Dynamic){ + App.current.logError("Error while contacting annuaire.cagette.net : "+e); + } + + } + + } + + @tpl("amapadmin/addimage.mtt") + function doAddimage() { + + if (!app.user.isAmapManager()) throw Error("/", t._("Access forbidden")); + + var user = app.user; + view.image = user.amap.image; + + var request = new Map(); + try { + request = sugoi.tools.Utils.getMultipart(1024 * 1024 * 12); //12Mb + }catch (e:Dynamic) { + throw Error("/amapadmin", t._("The sent image was too big. The maximum allowed size is 12MB")); + } + + if (request.exists("image")) { + + //Image + var image = request.get("image"); + + if (image != null && image.length > 0) { + + var img : sugoi.db.File = null; + if ( Sys.systemName() == "Windows") { + img = sugoi.db.File.create(request.get("image"), request.get("image_filename")); + }else { + img = sugoi.tools.UploadedImage.resizeAndStore(request.get("image"), request.get("image_filename"), 400, 400); + } + + user.amap.lock(); + + if (user.amap.image != null) { + //delete previous file + user.amap.image.lock(); + user.amap.image.delete(); + } + + user.amap.image = img; + user.amap.update(); + + throw Ok('/amapadmin/', t._("Image updated")); + } + } + + } + + + @tpl("amapadmin/rights.mtt") + public function doRights() { + + //liste les gens qui ont des droits dans le groupe + var users = db.UserAmap.manager.search($rights != null && $amap == app.user.amap, false); + + //cleaning + for ( u in Lambda.array(users)) { + + //rights peut etre null (null seralisé) et pas null en DB + if (u.rights == null || u.rights.length == 0) { + u.lock(); + Reflect.setField(u, "rights", null); + u.update(); + users.remove(u); + continue; + } + + //rights on a deleted contract + for ( r in u.rights) { + switch(r) { + case ContractAdmin(cid): + if (cid == null) continue; + var c = db.Contract.manager.get(cid); + if (c == null) { + u.lock(); + u.removeRight(r); + u.update(); + } + default : + } + } + } + + view.users = users; + } + + + @tpl("form.mtt") + public function doEditRight(?u:db.User) { + + var form = new sugoi.form.Form("editRight"); + + if (u == null) { + form.addElement( new IntSelect("user", t._("Member") , app.user.amap.getMembersFormElementData(), null, true) ); + } + + var data = []; + //for (r in db.UserAmap.Right.getConstructors()) { + //if (r == "ContractAdmin") continue; //managed later + //data.push({label:r,value:r}); + //} + data.push({label:t._("Group administrator"), value:"GroupAdmin"}); + data.push({label:t._("Membership management"),value:"Membership"}); + data.push({label:t._("Messages"),value:"Messages"}); + + var ua : db.UserAmap = null; + var populate :Array = null; + if (u != null) { + ua = db.UserAmap.get(u, app.user.amap, true); + if (ua == null) throw "no user"; + if (ua.rights == null) ua.rights = []; + //populate form + populate = ua.rights.map(function(x) return x.getName()); + } + + form.addElement( new sugoi.form.elements.CheckboxGroup("rights", t._("Rights"), data, populate, true, true) ); + form.addElement( new sugoi.form.elements.Html("html","
")); + + //Rights on contracts + var data = []; + var populate :Array = []; + data.push({value:"contractAll",label:t._("All contracts")}); + for (r in app.user.amap.getActiveContracts(true)) { + data.push( { label:r.name , value:"contract"+Std.string(r.id) } ); + } + + if(ua!=null && ua.rights!=null){ + for ( r in ua.rights) { + switch(r) { + case Right.ContractAdmin(cid): + if (cid == null) { + populate.push("contractAll"); + }else { + populate.push("contract"+cid); + } + + default:// + } + } + } + + + form.addElement( new sugoi.form.elements.CheckboxGroup("rights", t._("Contracts management") , data, populate, true, true) ); + + if (form.checkToken()) { + + var wasManager = app.user.isAmapManager(); + + if (u == null) { + ua = db.UserAmap.manager.select($userId == Std.parseInt(form.getValueOf("user")) && $amapId == app.user.amap.id, true); + } + ua.rights = []; + + var arr : Array = cast form.getElement("rights").value; + for ( r in arr) { + if (r.substr(0, 8) == "contract") { + if (r == "contractAll") { + ua.rights.push( Right.ContractAdmin() ); + }else { + ua.rights.push( Right.ContractAdmin(Std.parseInt(r.substr(8)) ) ); + } + + }else { + ua.rights.push( db.UserAmap.Right.createByName(r) ); + } + } + + //avoid "cut my own hands" problem + if (ua.user.id == app.user.id && wasManager ) { + var isManager = false; + for ( r in ua.rights) { + if (r.equals(db.UserAmap.Right.GroupAdmin)) { + isManager = true; + break; + } + } + if (isManager == false) { + throw Error("/amapadmin/rights", t._("You cannot strip yourself of admin rights.")); + } + } + + + if (ua.rights.length == 0) ua.rights = null; + ua.update(); + if (ua.rights == null) { + throw Ok("/amapadmin/rights", t._("Rights removed")); + }else { + throw Ok("/amapadmin/rights", t._("Rights created or modified")); + } + + } + + if (u == null) { + view.title = t._("Give rights to a user"); + }else { + view.title = t._("Modify the rights of ::user::",{user:u.getName()}); + } + + view.form = form; + + } + + @tpl('form.mtt') + public function doVatRates() { + + var f = new sugoi.form.Form("vat"); + var a = app.user.amap; + + if (a.vatRates == null) { + a.lock(); + var x = new db.Amap(); + a.vatRates = x.vatRates; + a.update(); + } + + var i = 1; + for (k in a.vatRates.keys()) { + f.addElement(new StringInput(i+"-k", t._("Name ")+i, k)); + f.addElement(new StringInput(i + "-v", t._("Rate ")+i, Std.string(a.vatRates.get(k)) )); + //f.addElement(new sugoi.form.elements.Html("
")); + i++; + } + var j = i; + + for (x in 0...5 - i) { + f.addElement(new StringInput(i+"-k", t._("Name ")+i, "")); + f.addElement(new StringInput(i + "-v", t._("Rate ")+i, "")); + //f.addElement(new sugoi.form.elements.Html("
")); + i++; + } + + if (f.isValid()) { + var d = f.getData(); + var vats = new Map(); + var filter = new sugoi.form.filters.FloatFilter(); + for (i in 1...5) { + if (d.get(i + "-k") == null) continue; + vats.set(d.get(i + "-k"), filter.filter( d.get(i + "-v")) ); + } + a.lock(); + a.vatRates = vats; + a.update(); + throw Ok("/amapadmin", t._("Rate updated")); + + } + view.title = t._("Edit VAT rates"); + view.form = f; + + } + + function doCategories(d:haxe.web.Dispatch) { + d.dispatch(new controller.Categories()); + } + + /** + * Set up group currency. Default is EURO + */ + @tpl("form.mtt") + function doCurrency(){ + + view.title = t._("Currency used by your group."); + + var f = new sugoi.form.Form("curr"); + f.addElement(new sugoi.form.elements.StringInput("currency", t._("Currency symbol"), app.user.amap.getCurrency())); + f.addElement(new sugoi.form.elements.StringInput("currencyCode", t._("3 digit ISO code"), app.user.amap.currencyCode)); + + if ( f.isValid()){ + + app.user.amap.lock(); + app.user.amap.currency = f.getValueOf("currency"); + app.user.amap.currencyCode = f.getValueOf("currencyCode"); + app.user.amap.update(); + + throw Ok("/amapadmin/currency", t._("Currency updated")); + } + + view.form = f; + } + + /** + * payment configuration + */ + @tpl("form.mtt") + function doPayments(){ + + var f = new sugoi.form.Form("paymentTypes"); + var types = service.PaymentService.getAllPaymentTypes(); + var formdata = [for (t in types){label:t.name, value:t.type}]; + var selected = app.user.amap.allowedPaymentsType; + f.addElement(new sugoi.form.elements.CheckboxGroup("paymentTypes", t._("Authorized payment types"),formdata, selected) ); + + if (app.user.amap.checkOrder == ""){ + app.user.amap.lock(); + app.user.amap.checkOrder = app.user.amap.name; + app.user.amap.update(); + } + f.addElement( new sugoi.form.elements.StringInput("checkOrder", t._("Make the check payable to"), app.user.amap.checkOrder, false)); + f.addElement( new sugoi.form.elements.StringInput("IBAN", t._("IBAN of your bank account for transfers"), app.user.amap.IBAN, false)); + f.addElement(new sugoi.form.elements.Checkbox("allowMoneyPotWithNegativeBalance", t._("Allow money pots with negative balance"), app.user.amap.allowMoneyPotWithNegativeBalance)); + + if (f.isValid()){ + + var p = f.getValueOf("paymentTypes"); + var a = app.user.amap; + a.lock(); + a.allowedPaymentsType = p; + a.checkOrder = f.getValueOf("checkOrder"); + a.IBAN = f.getValueOf("IBAN"); + a.allowMoneyPotWithNegativeBalance = f.getValueOf("allowMoneyPotWithNegativeBalance"); + a.update(); + + throw Ok("/amapadmin/payments", t._("Payment options updated")); + + } + + view.title = t._("Options of payment"); + view.form = f; + } + +} diff --git a/src/controller/Api.hx b/src/controller/Api.hx new file mode 100644 index 0000000..9726266 --- /dev/null +++ b/src/controller/Api.hx @@ -0,0 +1,111 @@ +package controller; +import db.UserAmap; +import haxe.Json; +import neko.Web; + +/** + * REST JSON API + * + * @author fbarbut + */ +class Api extends Controller +{ + + /** + * Public infos about this Cagette.net installation + */ + public function doDefault(){ + + var json : Dynamic = { + version:App.VERSION.toString(), + debug:App.config.DEBUG, + email:App.config.get("webmaster_email"), + groups:[] + + }; + + for ( g in db.Amap.manager.all()){ + + //a strange way to exclude "test" accounts + if ( UserAmap.manager.count($amapId == g.id) > 20){ + + var place = g.getMainPlace(); + + var d = { + name:g.name, + cagetteNetwork:g.flags.has(db.Amap.AmapFlags.CagetteNetwork), + id:g.id, + url:"http://" + Web.getHostName() + "/group/" + g.id, + membersNum : g.getMembersNum(), + contracts: Lambda.array(Lambda.map(g.getActiveContracts(false), function(c) return c.name)), + place : {name:place.name, address1:place.address1,address2:place.address2,zipCode:place.zipCode,city:place.city } + }; + json.groups.push(d); + } + + } + + Sys.print( Json.stringify(json) ); + + } + + /*public function doError(){ + sugoi.Web.setReturnCode(403); + }*/ + + + #if plugins + //cagette-pro + public function doPro(d:haxe.web.Dispatch) { + d.dispatch(new pro.controller.api.Main()); + } + #end + + public function doShop(d:haxe.web.Dispatch) { + d.dispatch(new controller.api.Shop()); + } + + public function doOrder(d:haxe.web.Dispatch) { + d.dispatch(new controller.api.Order()); + } + + /** + * Get distribution planning for this group + * + * @param group + */ + public function doPlanning(group:db.Amap){ + + var contracts = group.getActiveContracts(true); + var cids = Lambda.map(contracts, function(p) return p.id); + var now = Date.now(); + var now = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0); + var twoMonths = new Date(now.getFullYear(), now.getMonth()+2, now.getDate(), 0, 0, 0); + var distribs = db.Distribution.manager.search(($contractId in cids) && ($date >= now) && ($date<=twoMonths), { orderBy:date }, false); + + var out = new Array<{id:Int,start:Date,end:Date,contract:String,contractId:Int,place:Dynamic}>(); + + for ( d in distribs){ + + var place = d.place; + var p = {name:place.name, address1:place.address1,address2:place.address2,zipCode:place.zipCode,city:place.city } + out.push({id:d.id,start:d.date,end:d.end,contract:d.contract.name,contractId:d.contract.id,place:p}); + } + + Sys.print(Json.stringify(out)); + + } + + public function doUser(d:haxe.web.Dispatch){ + d.dispatch(new controller.api.User()); + } + + public function doGroup(d:haxe.web.Dispatch){ + d.dispatch(new controller.api.Group()); + } + + public function doProduct(d:haxe.web.Dispatch){ + d.dispatch(new controller.api.Product()); + } + +} \ No newline at end of file diff --git a/src/controller/Categories.hx b/src/controller/Categories.hx new file mode 100755 index 0000000..2205068 --- /dev/null +++ b/src/controller/Categories.hx @@ -0,0 +1,160 @@ +package controller ; + +class Categories extends controller.Controller +{ + @tpl("categories/default.mtt") + public function doDefault() { + + view.groups = db.CategoryGroup.manager.search($amap == app.user.amap, false); + + checkToken(); + + } + + /** + * genere le set par défaut de catégories + */ + public function doGenerate() { + + if ( db.CategoryGroup.manager.search($amap == app.user.amap, false).length != 0) { + throw Error("/amapadmin/categories", t._("The category list is not empty.") ); + } + + function gen(catGroupName:String,color:Int,cats:Array) { + + var cg = new db.CategoryGroup(); + cg.name = catGroupName; + cg.color = color; + cg.amap = app.user.amap; + cg.insert(); + + for (c in cats) { + var x = new db.Category(); + x.categoryGroup = cg; + x.name = c; + x.insert(); + } + + } + var t = sugoi.i18n.Locale.texts; + gen(t._("Product types"),2, [t._("Vegetables"), t._("Fruits"), t._("Fish"), t._("Red meat"), t._("Breads"), t._("Grocery"), t._("Beverages") ]); + gen(t._("Labels"),0, [t._("Certified organic agriculture"), t._("Uncertified organic agriculture"), t._("Non organic") ]); + + throw Ok("/amapadmin/categories", t._("Default categories have been created") ); + + } + + /** + * modifie un groupe de categories + */ + @tpl('form.mtt') + function doEditGroup(g:db.CategoryGroup) { + + var form = sugoi.form.Form.fromSpod(g); + + form.removeElementByName("color"); + form.removeElementByName("amapId"); + form.addElement(new form.ColorRadioGroup("color", t._("Color") , Std.string(g.color) )); + + if (form.isValid()) { + + form.toSpod(g); + g.update(); + throw Ok("/amapadmin/categories", t._("Group modified")); + + } + + view.title = t._("Modify the group ") + g.name; + view.form = form; + } + + @tpl('form.mtt') + function doInsertGroup() { + var g = new db.CategoryGroup(); + var form = sugoi.form.Form.fromSpod(g ); + + form.removeElementByName("color"); + form.removeElementByName("amapId"); + form.addElement(new form.ColorRadioGroup("color", "Couleur", Std.string(g.color))); + + if (form.isValid()) { + + form.toSpod(g); + g.amap = app.user.amap; + g.insert(); + throw Ok("/amapadmin/categories", t._("Group added")); + + } + + view.title = t._("Create a group of categories"); + view.form = form; + } + + @tpl('form.mtt') + function doInsert(g:db.CategoryGroup) { + var c = new db.Category(); + var form = sugoi.form.Form.fromSpod(c); + + form.removeElementByName("categoryGroupId"); + + if (form.isValid()) { + + form.toSpod(c); + c.categoryGroup = g; + c.insert(); + throw Ok("/amapadmin/categories", t._("Category added")); + + } + + view.title = t._("Create a category"); + view.form = form; + } + + + @tpl('form.mtt') + function doEdit(c:db.Category) { + + var form = sugoi.form.Form.fromSpod(c); + + form.removeElementByName("categoryGroupId"); + + if (form.isValid()) { + + form.toSpod(c); + c.update(); + throw Ok("/amapadmin/categories","Category modified"); + } + + view.title = t._("Modify the category ") + c.name; + view.form = form; + } + + + function doDeleteGroup(g:db.CategoryGroup,args:{token:String}) { + + if ( checkToken()) { + if (g.getCategories().length > 0) throw Error("/amapadmin/categories", t._("All categories must be removed from this group before it can be deleted.")); + + g.lock(); + g.delete(); + throw Ok("/amapadmin/categories", t._("Group deleted")); + }else { + throw Redirect("/amapadmin/categories"); + } + } + + function doDelete(c:db.Category,args:{token:String}) { + + if ( checkToken()) { + c.lock(); + c.delete(); + throw Ok("/amapadmin/categories", t._("Category deleted")); + }else { + throw Redirect("/amapadmin/categories"); + } + } + + + + +} diff --git a/src/controller/Contract.hx b/src/controller/Contract.hx new file mode 100755 index 0000000..27299e7 --- /dev/null +++ b/src/controller/Contract.hx @@ -0,0 +1,619 @@ +package controller; +import db.UserContract; +import sugoi.form.elements.DateDropdowns; +import sugoi.form.elements.Input; +import sugoi.form.elements.Selectbox; +import sugoi.form.Form; +import db.Contract; +import Common; +import plugin.Tutorial; +using Std; +import service.OrderService; + +class Contract extends Controller +{ + + public function new() + { + super(); + } + + @tpl("contract/view.mtt") + public function doView(c:db.Contract) { + view.category = 'amap'; + view.c = c; + } + + /** + * "my account" page + */ + @tpl("contract/default.mtt") + function doDefault() { + + //Create the list of links to change the language + var langs = App.config.get("langs").split(";"); + var langNames = App.config.get("langnames").split(";"); + var i=0; + var langLinks = ""; + for (lang in langs) + { + langLinks += "
  • " + langNames[i] + "
  • "; + i++; + } + view.langLinks = langLinks; + view.langText = langNames[langs.indexOf(app.session.lang)]; + + //change account lang + if (app.params.exists("lang") && app.user!=null){ + app.user.lock(); + app.user.lang = app.params.get("lang"); + app.user.update(); + } + + var ua = db.UserAmap.get(app.user, app.user.amap); + if (ua == null) throw Error("/", t._("You are not a member of this group")); + + var constOrders = null; + var varOrders = new Map>(); + + var a = App.current.user.amap; + var oneMonthAgo = DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 30); + + //constant orders + var contracts = db.Contract.manager.search($type == db.Contract.TYPE_CONSTORDERS && $amap == a && $endDate > oneMonthAgo, false); + constOrders = []; + for ( c in contracts){ + var orders = app.user.getOrdersFromContracts([c]); + if (orders.length == 0) continue; + constOrders.push({contract:c, orders:service.OrderService.prepare(orders) }); + } + + //variable orders, grouped by date + var contracts = db.Contract.manager.search($type == db.Contract.TYPE_VARORDER && $amap == a && $endDate > oneMonthAgo, false); + + for (c in contracts) { + var ds = c.getDistribs(false); + for (d in ds) { + //store orders in a stringmap like "2015-01-01" => [order1,order2,...] + var k = d.date.toString().substr(0, 10); + var orders = app.user.getOrdersFromDistrib(d); + if (orders.length > 0) { + if (!varOrders.exists(k)) { + varOrders.set(k, Lambda.array(orders)); + }else { + var z = varOrders.get(k).concat(Lambda.array(orders)); + varOrders.set(k, z); + } + } + } + } + + //final structure + var varOrders2 = new Array<{date:Date,orders:Array}>(); + for ( k in varOrders.keys()) { + + var d = new Date(k.split("-")[0].parseInt(), k.split("-")[1].parseInt() - 1, k.split("-")[2].parseInt(), 0, 0, 0); + var orders = service.OrderService.prepare( Lambda.list(varOrders[k]) ); + + varOrders2.push({date:d,orders:orders}); + } + + //sort by date desc + varOrders2.sort(function(b, a) { + return Math.round(a.date.getTime()/1000)-Math.round(b.date.getTime()/1000); + }); + + view.varOrders = varOrders2; + view.constOrders = constOrders; + + + // tutorials + if (app.user.isAmapManager()) { + + + //actions + if (app.params.exists('startTuto') ) { + + //start a tuto + app.user.lock(); + var t = app.params.get('startTuto'); + app.user.tutoState = {name:t,step:0}; + app.user.update(); + } + + + //tuto state + var tutos = new Array<{name:String,completion:Float,key:String}>(); + + for ( k in Tutorial.all().keys() ) { + var t = Tutorial.all().get(k); + + var completion = null; + if (app.user.tutoState!=null && app.user.tutoState.name == k) completion = app.user.tutoState.step / t.steps.length; + + tutos.push( { name:t.name, completion:completion , key:k } ); + } + + view.tutos = tutos; + } + + //should be able to stop tuto in any case + if (app.params.exists('stopTuto')) { + //stopped tuto from a tuto window + app.user.lock(); + app.user.tutoState = null; + app.user.update(); + view.stopTuto = true; + } + + checkToken(); + view.userAmap = ua; + } + + /** + * Edit a contract + */ + @tpl("form.mtt") + function doEdit(c:db.Contract) { + + view.category = 'contractadmin'; + if (!app.user.isContractManager(c)) throw Error('/', t._("Forbidden action")); + + view.title = t._("Edit contract ::contractName::",{contractName:c.name}); + + var group = c.amap; + var currentContact = c.contact; + + var form = Form.fromSpod(c); + form.removeElement( form.getElement("amapId") ); + form.removeElement(form.getElement("type")); + form.getElement("userId").required = true; + + app.event(EditContract(c,form)); + + if (form.checkToken()) { + form.toSpod(c); + c.amap = group; + + //checks & warnings + if (c.hasPercentageOnOrders() && c.percentageValue==null) throw Error("/contract/edit/"+c.id, t._("If you would like to add fees to the order, define a rate (%) and a label.")); + + if (c.hasStockManagement()) { + for (p in c.getProducts()) { + if (p.stock == null) { + app.session.addMessage(t._("Warning about management of stock. Please fill the field \"stock\" for all your products"), true); + break; + } + } + } + + //no stock mgmt for constant orders + if (c.hasStockManagement() && c.type==db.Contract.TYPE_CONSTORDERS) { + c.flags.unset(ContractFlags.StockManagement); + app.session.addMessage(t._("Managing stock is not available for CSA contracts"), true); + } + + + c.update(); + + //update rights + if ( c.contact != null && (currentContact==null || c.contact.id!=currentContact.id) ) { + var ua = db.UserAmap.get(c.contact, app.user.amap, true); + ua.giveRight(ContractAdmin(c.id)); + ua.giveRight(Messages); + ua.giveRight(Membership); + ua.update(); + + //remove rights to old contact + if (currentContact != null) { + var x = db.UserAmap.get(currentContact, c.amap, true); + if (x != null) { + x.removeRight(ContractAdmin(c.id)); + x.update(); + } + } + + } + + throw Ok("/contractAdmin/view/"+c.id, t._("Contract updated")); + } + + view.form = form; + } + + @tpl("contract/insertChoose.mtt") + function doInsertChoose() { + //checkToken(); + + } + + /** + * Créé un nouveau contrat + */ + @tpl("form.mtt") + function doInsert(?type:Int) { + if (!app.user.canManageAllContracts()) throw Error('/', t._("Forbidden action")); + if (type == null) throw Redirect('/contract/insertChoose'); + + view.title = if (type == db.Contract.TYPE_CONSTORDERS)t._("Create a contract with fixed orders") else t._("Create a contract with variable orders"); + + var c = new db.Contract(); + + var form = Form.fromSpod(c); + form.removeElement( form.getElement("amapId") ); + form.removeElement(form.getElement("type")); + form.getElement("userId").required = true; + + if (form.checkToken()) { + form.toSpod(c); + c.amap = app.user.amap; + //trace(app.user.amap); + //trace(c.amap); + c.type = type; + c.insert(); + + //right + if (c.contact != null) { + var ua = db.UserAmap.get(c.contact, app.user.amap, true); + ua.giveRight(ContractAdmin(c.id)); + ua.giveRight(Messages); + ua.giveRight(Membership); + ua.update(); + } + + throw Ok("/contractAdmin/view/"+c.id, t._("New contract created")); + } + + view.form = form; + } + + /** + * Delete a contract (... and its products, orders & distributions) + */ + function doDelete(c:db.Contract) { + + if (!app.user.canManageAllContracts()) throw Error("/contractAdmin", t._("You don't have the authorization to remove a contract")); + + if (checkToken()) { + c.lock(); + + //check if there is orders in this contract + var products = c.getProducts(); + + var orders = db.UserContract.manager.search($productId in Lambda.map(products, function(p) return p.id)); + var qt = 0.0; + for ( o in orders) qt += o.quantity; //there could be "zero c qt" orders + if (qt > 0) { + throw Error("/contractAdmin", t._("You cannot delete this contract because some orders are linked to it.")); + } + + //remove admin rights and delete contract + if(c.contact!=null){ + var ua = db.UserAmap.get(c.contact, c.amap, true); + if (ua != null) { + ua.removeRight(ContractAdmin(c.id)); + ua.update(); + } + } + + app.event(DeleteContract(c)); + + c.delete(); + throw Ok("/contractAdmin", t._("Contract deleted")); + } + + throw Error("/contractAdmin", t._("Token error")); + } + + /** + * Make an order by contract ( standard mode ) + * The form is prepopulated if orders have already been made. + * + * It should work for constant orders ( will display one column ) + * or varying orders ( with as many columns as distributions dates ) + * + */ + @tpl("contract/order.mtt") + function doOrder(c:db.Contract ) { + + //checks + if (app.user.amap.hasPayments()) throw Redirect("/contract/orderAndPay/" + c.id); + if (app.user.amap.hasShopMode()) throw Redirect("/shop"); + if (!c.isUserOrderAvailable()) throw Error("/", t._("This contract is not opened for orders")); + + + var distributions = []; + // If its a varying contract, we display a column by distribution + if (c.type == db.Contract.TYPE_VARORDER) { + distributions = db.Distribution.getOpenToOrdersDeliveries(c); + }else{ + distributions = [null]; + } + + //list of distribs with a list of product and optionnaly an order + var userOrders = new Array< {distrib:db.Distribution,datas:Array<{order:db.UserContract,product:db.Product}>} >(); + var products = c.getProducts(); + + if ( c.type == db.Contract.TYPE_VARORDER ){ + + for ( d in distributions){ + var datas = []; + for ( p in products) { + var ua = { order:null, product:p }; + + var order = db.UserContract.manager.select($user == app.user && $productId == p.id && $distributionId==d.id, true); + + if (order != null) ua.order = order; + datas.push(ua); + } + + userOrders.push({distrib:d,datas:datas}); + } + + }else{ + + var datas = []; + for ( p in products) { + var ua = { order:null, product:p }; + + var order = db.UserContract.manager.select($user == app.user && $productId == p.id, true); + + if (order != null) ua.order = order; + datas.push(ua); + } + + userOrders.push({distrib:null,datas:datas}); + + } + + + //form check + if (checkToken()) { + + //get dsitrib if needed + //var distrib : db.Distribution = null; + //if (c.type == db.Contract.TYPE_VARORDER) { + //distrib = db.Distribution.manager.get(Std.parseInt(app.params.get("distribution")), false); + //} + + for (k in app.params.keys()) { + + if (k.substr(0, 1) != "d") continue; + var qt = app.params.get(k); + if (qt == "") continue; + + var pid = null; + var did = null; + try{ + pid = Std.parseInt(k.split("-")[1].substr(1)); + did = Std.parseInt(k.split("-")[0].substr(1)); + }catch (e:Dynamic){trace("unable to parse key "+k); } + + //find related element in userOrders + var uo = null; + for ( x in userOrders){ + if (x.distrib!=null && x.distrib.id != did) { + continue; + }else{ + for (a in x.datas){ + if (a.product.id == pid){ + uo = a; + break; + } + } + } + } + + if (uo == null) throw t._("Could not find the product ::produ:: and delivery ::deliv::", {produ:pid, deliv:did}); + + var q = 0.0; + + if (uo.product.hasFloatQt ) { + var param = StringTools.replace(qt, ",", "."); + q = Std.parseFloat(param); + }else { + q = Std.parseInt(qt); + } + + + if (uo.order != null) { + OrderService.edit(uo.order, q); + }else { + OrderService.make(app.user, q, uo.product, did); + } + + } + throw Ok("/contract/order/"+c.id, t._("Your order has been updated")); + } + + view.c = view.contract = c; + view.userOrders = userOrders; + } + + + /** + * Make an order by contract ( standard mode ) + payment process + */ + @tpl("contract/orderAndPay.mtt") + function doOrderAndPay(c:db.Contract ) { + + //checks + if (!app.user.amap.hasPayments()) throw Redirect("/contract/order/" + c.id); + if (app.user.amap.hasShopMode()) throw Redirect("/"); + if (!c.isUserOrderAvailable()) throw Error("/", t._("This contract is not opened for orders")); + + var distributions = []; + /* If its a varying contract, we display a column by distribution*/ + if (c.type == db.Contract.TYPE_VARORDER) { + distributions = db.Distribution.getOpenToOrdersDeliveries(c); + } + + //list of distribs with a list of product and optionnaly an order + var userOrders = new Array< {distrib:db.Distribution,datas:Array<{order:db.UserContract,product:db.Product}>} >(); + var products = c.getProducts(); + + for ( d in distributions){ + var datas = []; + for ( p in products) { + var ua = { order:null, product:p }; + + var order : db.UserContract = null; + if (c.type == db.Contract.TYPE_VARORDER) { + order = db.UserContract.manager.select($user == app.user && $productId == p.id && $distributionId==d.id, true); + }else { + order = db.UserContract.manager.select($user == app.user && $productId == p.id, true); + } + + if (order != null) ua.order = order; + datas.push(ua); + } + + userOrders.push({distrib:d,datas:datas}); + } + + + //form check + if (checkToken()) { + + //get distrib if needed + var distrib = null; + if (c.type == db.Contract.TYPE_VARORDER) { + distrib = db.Distribution.manager.get(Std.parseInt(app.params.get("distribution")), false); + } + + var orders : OrderInSession = {products:[],userId:app.user.id,total:0}; + + for (k in app.params.keys()) { + + if (k.substr(0, 1) != "d") continue; + var qt = app.params.get(k); + if (qt == "") continue; + + var pid = null; + var did = null; + try{ + pid = Std.parseInt(k.split("-")[1].substr(1)); + did = Std.parseInt(k.split("-")[0].substr(1)); + }catch (e:Dynamic){ + trace("unable to parse key "+k); + } + + //find related element in userOrders + var uo = null; + for ( x in userOrders){ + if (x.distrib!=null && x.distrib.id != did) { + continue; + }else{ + for (a in x.datas){ + if (a.product.id == pid){ + uo = a; + break; + } + } + } + } + + if (uo == null) throw t._("Could not find the product ::produ:: and delivery ::deliv::", {produ:pid, deliv:did}); + + //quantity + var q = 0.0; + if (uo.product.hasFloatQt ) { + var param = StringTools.replace(qt, ",", "."); + q = Std.parseFloat(param); + }else { + q = Std.parseInt(qt); + } + + orders.products.push({productId:pid, quantity:q, distributionId:did}); + + var p = db.Product.manager.get(pid, false); + orders.total += p.getPrice() * q; + + } + + App.current.session.data.order = orders; + + //Go to payments page + if (c.type == db.Contract.TYPE_CONSTORDERS) { + throw Ok("/contract/order/"+c.id, t._("Your CSA order has been saved")); + }else{ + throw Ok("/transaction/pay/", t._("In order to save your order, please choose a means of payment.")); + } + + + + } + + view.c = view.contract = c; + view.userOrders = userOrders; + } + + + /** + * A user edit an order for a multidistrib. + */ + @tpl("contract/orderByDate.mtt") + function doEditOrderByDate(date:Date) { + + if (app.user.amap.hasPayments()) { + //when payments are active, the user cannot modify his order + throw Redirect("/"); + } + + // cannot edit order if date is in the past + if (Date.now().getTime() > date.getTime()) { + + var msg = t._("This delivery has already taken place, you can no longer modify the order."); + if (app.user.isContractManager()) msg += t._("
    As the manager of the contract you can modify the order from this page: Management of contracts"); + + throw Error("/contract", msg); + } + + // Il faut regarder le contrat de chaque produit et verifier si le contrat est toujours ouvert à la commande. + var d1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); + var d2 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59); + + var cids = Lambda.map(app.user.amap.getActiveContracts(true), function(c) return c.id); + var distribs = db.Distribution.manager.search(($contractId in cids) && $date >= d1 && $date <=d2 , false); + var orders = db.UserContract.manager.search($userId==app.user.id && $distributionId in Lambda.map(distribs,function(d)return d.id) ); + view.orders = service.OrderService.prepare(orders); + view.date = date; + + //form check + if (checkToken()) { + + var orders_out = []; + + for (k in app.params.keys()) { + var param = app.params.get(k); + if (k.substr(0, "product".length) == "product") { + + //trouve le produit dans userOrders + var pid = Std.parseInt(k.substr("product".length)); + var order = Lambda.find(orders, function(uo) return uo.product.id == pid); + if (order == null) throw t._("Error, could not find the order"); + + var q = 0.0; + if (order.product.hasFloatQt ) { + param = StringTools.replace(param, ",", "."); + q = Std.parseFloat(param); + }else { + q = Std.parseInt(param); + } + + var quantity = Math.abs( q==null?0:q ); + + if ( order.distribution.canOrderNow() ) { + //met a jour la commande + var o = OrderService.edit(order, quantity); + if(o!=null) orders_out.push( o ); + } + } + } + + app.event(MakeOrder(orders_out)); + + throw Ok("/contract", t._("Your order has been updated")); + } + } +} diff --git a/src/controller/ContractAdmin.hx b/src/controller/ContractAdmin.hx new file mode 100755 index 0000000..8b5e859 --- /dev/null +++ b/src/controller/ContractAdmin.hx @@ -0,0 +1,1027 @@ +package controller; +import db.UserContract; +import sugoi.form.elements.Checkbox; +import sugoi.form.elements.Selectbox; +import sugoi.form.Form; +import sugoi.form.elements.StringInput; +import Common; +import datetime.DateTime; +using tools.ObjectListTool; +using tools.DateTool; +import service.OrderService; + +class ContractAdmin extends Controller +{ + + public function new() + { + super(); + if (!app.user.isContractManager()) throw Error("/", t._("You don't have the authorization to manage contracts")); + view.nav = ["contractadmin"]; + + } + + public function sendNav(c){ + var navbar = new Array(); + var e = Nav(navbar,"contractAdmin",c.id); + app.event(e); + view.navbar = e.getParameters()[0]; + } + + /** + * Contract admin main page + */ + @tpl("contractadmin/default.mtt") + function doDefault(?args:{old:Bool}) { + + view.nav.push("default"); + + var now = Date.now(); + + var contracts; + if (args != null && args.old) { + contracts = db.Contract.manager.search($amap == app.user.amap && $endDate < Date.now() ,{orderBy:-startDate},false); + }else { + contracts = db.Contract.getActiveContracts(app.user.amap, true, false); + } + + //filter if current user is not manager + if (!app.user.isAmapManager()) { + for ( c in Lambda.array(contracts).copy()) { + if(!app.user.canManageContract(c)) contracts.remove(c); + } + } + + view.contracts = contracts; + view.vendors = app.user.amap.getVendors(); + view.places = app.user.amap.getPlaces(); + checkToken(); + + //Multidistribs to validate + if(app.user.isAmapManager() && app.user.amap.hasPayments()){ + var twoMonthAgo = tools.DateTool.deltaDays(now,-60); + var multidistribs = MultiDistrib.getFromTimeRange(app.user.amap,twoMonthAgo,now,db.Contract.TYPE_VARORDER); + /*for( md in multidistribs.copy()){ + if(md.hasOnlyConstantOrders()) multidistribs.remove(md); + }*/ + view.multidistribs = multidistribs; + + }else{ + view.multidistribs = []; + } + } + + /** + * Manage products + */ + @tpl("contractadmin/products.mtt") + function doProducts(contract:db.Contract,?args:{?enable:String,?disable:String}) { + view.nav.push("products"); + sendNav(contract); + + if (!app.user.canManageContract(contract)) throw Error("/", t._("Access forbidden") ); + view.c = contract; + + //checks + if (app.user.amap.hasShopMode() && !app.user.amap.hasTaxonomy() ) { + for ( p in contract.getProducts(false)) { + if (p.getCategories().length == 0) { + app.session.addMessage(t._("Warning, at least one product does not have any category. Click here to add categories", {contractid:contract.id}), true); + break; + } + } + } + + //batch enable / disable products + if (args != null){ + + if (args.disable != null){ + var pids = Lambda.array(Lambda.map(args.disable.split("|"), function(x) return Std.parseInt(x))); + service.ProductService.batchDisableProducts(pids); + } + + if (args.enable != null){ + var pids = Lambda.array(Lambda.map(args.enable.split("|"), function(x) return Std.parseInt(x))); + service.ProductService.batchEnableProducts(pids); + } + + } + + //generate a token + checkToken(); + } + + + /** + * - hidden page - + * copy products from a contract to an other + */ + @admin @tpl("form.mtt") + function doCopyProducts(contract:db.Contract) { + view.title = t._("Copy products in: ")+contract.name; + var form = new Form("copy"); + var contracts = app.user.amap.getActiveContracts(); + var contracts = Lambda.map(contracts, function(c) return {key:Std.string(c.id),value:Std.string(c.name) } ); + form.addElement(new sugoi.form.elements.Selectbox("source", t._("Copy products from: "),Lambda.array(contracts))); + form.addElement(new sugoi.form.elements.Checkbox("delete", t._("Delete existing products (all orders will be deleted!)"))); + if (form.checkToken()) { + + if (form.getValueOf("delete") == "1") { + for ( p in contract.getProducts()) { + p.lock(); + p.delete(); + } + } + + var source = db.Contract.manager.get(Std.parseInt(form.getValueOf("source")), false); + var prods = source.getProducts(); + for ( source_p in prods) { + var p = new db.Product(); + p.name = source_p.name; + p.price = source_p.price; + //p.type = source_p.type; + p.contract = contract; + p.insert(); + } + + throw Ok("/contractAdmin/products/" + contract.id, t._("Products copied from ") + source.name); + + + } + + + view.form = form; + } + + /** + * displays a calendar of the current month + * with all events ( contracts start and end, deliveries... ) + */ + @tpl('contractadmin/calendar.mtt') + public function doCalendar() { + + var contracts = db.Contract.getActiveContracts(app.user.amap, true, false); + + //Events of the month in a calendar + var cal = Calendar.getMonthViewMap(); + + for ( c in contracts) { + var start = c.startDate.toString().substr(0,10); + var end = c.endDate.toString().substr(0,10); + if (cal.exists( start )) { + var v = cal.get(start); + v.push( { name: t._("Contract start ") + c.name, color:Calendar.COLOR_CONTRACT } ); + cal.set( start, v ); + } + if (cal.exists( end )) { + var v = cal.get(end); + v.push( { name: t._("Contract end ") +c.name, color:Calendar.COLOR_CONTRACT } ); + cal.set( end, v ); + } + + //deliveries + for ( d in c.getDistribs(false)) { + var start = d.date.toString().substr(0,10); + + if (cal.exists( start )) { + var v = cal.get( start ); + v.push( { name: t._("Distribution") +" "+d.contract.name, color:Calendar.COLOR_DELIVERY } ); + cal.set( start, v ); + } + + if ( d.orderStartDate != null && d.orderStartDate != null ) { + var k = d.orderStartDate.toString().substr(0,10); + if (cal.exists( k )) { + var v = cal.get( k ); + v.push( { name: t._("Opening of orders ") +d.contract.name, color:Calendar.COLOR_ORDER } ); + cal.set( k , v ); + } + + var k = d.orderEndDate.toString().substr(0,10); + if (cal.exists( k )) { + var v = cal.get( k ); + v.push( { name: t._("End of orders") +d.contract.name, color:Calendar.COLOR_ORDER } ); + cal.set( k , v ); + } + + } + + } + + } + + var n = Date.now(); + view.now = new Date(n.getFullYear(),n.getMonth(),n.getDate(),0,0,0).getTime(); + view.calendar = Calendar.mapToArray( cal ); + + + } + + /** + * global view on orders within a timeframe + */ + @tpl('contractadmin/ordersByTimeFrame.mtt') + function doOrdersByTimeFrame(?from:Date, ?to:Date/*, ?place:db.Place*/){ + + if (from == null) { + + var f = new sugoi.form.Form("listBydate", null, sugoi.form.Form.FormMethod.GET); + + var now = DateTime.now(); + var from = now.snap(Month(Down)).getDate(); + var to = now.snap(Month(Up)).add(Day(-1)).getDate(); + + + var el = new sugoi.form.elements.DatePicker("from", t._("Start date"), from,true); + el.format = 'LL'; + f.addElement(el); + + var el = new sugoi.form.elements.DatePicker("to", t._("End date"), to,true); + el.format = 'LL'; + f.addElement(el); + + //var places = Lambda.map(app.user.amap.getPlaces(), function(p) return {label:p.name,value:p.id} ); + //f.addElement(new sugoi.form.elements.IntSelect("placeId", "Lieu", Lambda.array(places),app.user.amap.getMainPlace().id,true)); + + view.form = f; + view.title = t._("Global view of orders"); + app.setTemplate("form.mtt"); + + if (f.checkToken()) { + + var url = '/contractAdmin/ordersByTimeFrame/' + f.getValueOf("from").toString().substr(0, 10) +"/"+f.getValueOf("to").toString().substr(0, 10); + //var p = f.getValueOf("placeId"); + //if (p != null) url += "/"+p; + throw Redirect( url ); + } + + return; + + }else { + + var d1 = tools.DateTool.setHourMinute(from,0,0); + var d2 = tools.DateTool.setHourMinute(to,23,59); + var contracts = app.user.amap.getActiveContracts(true); + var cconst = []; + var cvar = []; + for ( c in contracts) { + if (c.type == db.Contract.TYPE_CONSTORDERS) cconst.push(c.id); + if (c.type == db.Contract.TYPE_VARORDER) cvar.push(c.id); + } + + //distribs + var vdistribs = db.Distribution.manager.search(($contractId in cvar) && $date >= d1 && $date <= d2 /*&& place.id==$placeId*/, false); + var cdistribs = db.Distribution.manager.search(($contractId in cconst) && $date >= d1 && $date <= d2 /*&& place.id==$placeId*/, false); + + if (vdistribs.length == 0 && cdistribs.length == 0) throw Error("/contractAdmin/ordersByDate", t._("There is no delivery at this date")); + + //varying orders + var varorders = db.UserContract.manager.search($distributionId in vdistribs.getIds() , { orderBy:userId } ); + + //constant orders + var constorders = []; + for ( d in cdistribs) { + var orders2 = db.UserContract.manager.search($productId in d.contract.getProducts().getIds(), { orderBy:userId } ); + constorders = constorders.concat(Lambda.array(orders2)); + } + + //merge 2 lists + var orders = Lambda.array(varorders).concat(Lambda.array(constorders)); + var orders = service.OrderService.prepare(Lambda.list(orders)); + + view.orders = orders; + view.from = from; + view.to = to; + view.ctotal = app.params.exists("ctotal"); + + } + + + + } + + /** + * Global view on orders in one day + * + * @param date + */ + @tpl('contractadmin/ordersByDate.mtt') + function doOrdersByDate(?date:Date,?place:db.Place){ + if (date == null) { + + var f = new sugoi.form.Form("listBydate", null, sugoi.form.Form.FormMethod.GET); + var el = new sugoi.form.elements.DatePicker("date", t._("Delivery date"), true); + el.format = 'LL'; + f.addElement(el); + + var places = Lambda.map(app.user.amap.getPlaces(), function(p) return {label:p.name,value:p.id} ); + f.addElement(new sugoi.form.elements.IntSelect("placeId", "Lieu", Lambda.array(places),app.user.amap.getMainPlace().id,true)); + + view.form = f; + view.title = t._("Global view of orders"); + view.text = t._("This page allows you to have a global view on orders of all contracts"); + view.text += t._("
    Select a delivery date:"); + app.setTemplate("form.mtt"); + + if (f.checkToken()) { + + var url = '/contractAdmin/ordersByDate/' + f.getValueOf("date").toString().substr(0, 10); + var p = f.getValueOf("placeId"); + if (p != null) url += "/"+p; + throw Redirect( url ); + } + + return; + + }else { + + var d1 = date.setHourMinute(0, 0); + var d2 = date.setHourMinute(23,59); + var contracts = app.user.amap.getActiveContracts(true); + var cconst = []; + var cvar = []; + for ( c in contracts) { + if (c.type == db.Contract.TYPE_CONSTORDERS) cconst.push(c.id); + if (c.type == db.Contract.TYPE_VARORDER) cvar.push(c.id); + } + + //distribs + var vdistribs = db.Distribution.manager.search(($contractId in cvar) && $date >= d1 && $date <= d2 && place.id==$placeId, false); + var cdistribs = db.Distribution.manager.search(($contractId in cconst) && $date >= d1 && $date <= d2 && place.id==$placeId, false); + + if (vdistribs.length == 0 && cdistribs.length == 0) throw Error("/contractAdmin/ordersByDate", t._("There is no delivery at this date")); + + //varying orders + var varorders = db.UserContract.manager.search($distributionId in vdistribs.getIds() , { orderBy:userId } ); + + //constant orders + var constorders = []; + for ( d in cdistribs) { + var orders2 = db.UserContract.manager.search($productId in d.contract.getProducts().getIds(), { orderBy:userId } ); + constorders = constorders.concat(Lambda.array(orders2)); + } + + //merge 2 lists + var orders = Lambda.array(varorders).concat(Lambda.array(constorders)); + var orders = service.OrderService.prepare(Lambda.list(orders)); + + view.orders = orders; + view.date = date; + view.place = place; + view.ctotal = app.params.exists("ctotal"); + } + } + + + /** + * Global view on orders, producer view + */ + @tpl('contractadmin/vendorsByDate.mtt') + function doVendorsByDate(date:Date,place:db.Place){ + + var d1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); + var d2 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59); + var contracts = app.user.amap.getActiveContracts(true); + var cids = Lambda.map(contracts, function(c) return c.id); + + //distribs for both types in active contracts + var distribs = db.Distribution.manager.search(($contractId in cids) && $date >= d1 && $date <= d2 && $place==place , false); + + + if ( distribs.length == 0 ) throw Error("/contractAdmin/ordersByDate", t._("There is no delivery at this date")); + + var out = new Map();//key : vendor id + + for (d in distribs){ + var vid = d.contract.vendor.id; + var o = out.get(vid); + + if (o == null){ + out.set( vid, {contract:d.contract,distrib:d,orders:service.ReportService.getOrdersByProduct( d )}); + }else{ + + //add orders with existing ones + for ( x in service.ReportService.getOrdersByProduct( d ) ){ + + //find record in existing orders + var f : Dynamic = Lambda.find(o.orders, function(a) return a.pid == x.pid); + if (f == null){ + //new product order + o.orders.push(x); + }else{ + //increment existing + f.quantity += untyped x.quantity; + f.total += untyped x.total; + } + } + out.set(vid, o); + } + } + + view.orders = Lambda.array(out); + view.date = date; + } + + + /** + * Global view on orders, producer view + */ + @tpl('contractadmin/vendorsByTimeFrame.mtt') + function doVendorsByTimeFrame(from:Date,to:Date){ + + var d1 = tools.DateTool.setHourMinute(from,0,0); + var d2 = tools.DateTool.setHourMinute(to,23,59); + var contracts = app.user.amap.getActiveContracts(true); + var cids = contracts.getIds(); + + //distribs for both types in active contracts + var distribs = db.Distribution.manager.search(($contractId in cids) && $date >= d1 && $date <= d2 /*&& $place==place*/, false); + if ( distribs.length == 0 ) throw Error("/contractAdmin/", t._("There is no delivery during this period")); + + var out = new Map}>();//key : vendor id + + for (d in distribs){ + var vid = d.contract.vendor.id; + var o = out.get(vid); + + if (o == null){ + out.set( vid, {contract:d.contract,distrib:d,orders:service.ReportService.getOrdersByProduct(d) }); + }else{ + + //add orders with existing ones + for ( x in service.ReportService.getOrdersByProduct(d) ){ + + //find record in existing orders + var f : OrderByProduct = Lambda.find(o.orders, function(a:OrderByProduct) return a.pid == x.pid); + if (f == null){ + //new product order + o.orders.push(x); + }else{ + //increment existing + f.quantity += x.quantity; + f.totalHT += x.totalHT; + f.totalTTC += x.totalTTC; + } + } + out.set(vid, o); + } + } + + view.orders = Lambda.array(out); + + if ( app.params.exists("csv") ){ + var totalHT = 0.0; + var totalTTC = 0.0; + + var orders = []; + for ( x in out){ + //empty line + orders.push({"quantity":null, "pname":null, "ref":null, "priceHT":null, "priceTTC":null, "totalHT":null, "totalTTC":null}); + orders.push({"quantity":null, "pname":x.contract.vendor.name, "ref":null, "priceHT":null, "priceTTC":null, "totalHT":null, "totalTTC":null}); + + for (o in x.orders){ + if(o.vat==null) o.vat = 0; + orders.push({ + "quantity":view.formatNum(o.quantity), + "pname":o.pname, + "ref":o.ref, + "priceHT":view.formatNum(o.priceTTC / (1 + o.vat / 100) ), + "priceTTC":view.formatNum(o.priceTTC), + "totalHT":view.formatNum(o.totalHT), + "totalTTC":view.formatNum(o.totalTTC) + }); + totalTTC += o.totalTTC; + totalHT += o.totalHT; + } + + //total line + orders.push({ + "quantity":null, + "pname":null, + "ref":null, + "priceHT":null, + "priceTTC":null, + "totalHT":view.formatNum(totalHT) + "", + "totalTTC":view.formatNum(totalTTC)+"" + }); + totalTTC = 0; + totalHT = 0; + + } + var fileName = t._("Orders from the ::fromDate:: to the ::toDate:: per supplier.csv", {fromDate:from.toString().substr(0, 10), toDate:to.toString().substr(0, 10)}); + sugoi.tools.Csv.printCsvDataFromObjects(orders, ["quantity", "pname", "ref", "priceHT", "priceTTC", "totalHT","totalTTC"], fileName); + return; + } + + view.from = from; + view.to = to; + } + + + /** + * Overview of orders for this contract in backoffice + */ + @tpl("contractadmin/orders.mtt") + function doOrders(contract:db.Contract, args:{?d:db.Distribution,?delete:db.UserContract}) { + view.nav.push("orders"); + sendNav(contract); + + //Checking permissions + if (!app.user.canManageContract(contract)) throw Error("/", t._("You do not have the authorization to manage this contract")); + if (contract.type == db.Contract.TYPE_VARORDER && args != null && args.d == null ) { + throw Redirect("/contractAdmin/selectDistrib/" + contract.id); + } + + //Delete specified order with quantity of zero + if (checkToken() && args != null && args.delete != null ) { + try { + service.OrderService.delete(args.delete); + } catch(e:tink.core.Error){ + throw Error("/contractAdmin/orders/" + contract.id, e.message); + } + if(args.d!=null){ + throw Ok("/contractAdmin/orders/" + contract.id + "?d="+args.d.id, t._("The order has been deleted.")); + }else{ + throw Ok("/contractAdmin/orders/" + contract.id, t._("The order has been deleted.")); + } + + } + + var d = null; + if (contract.type == db.Contract.TYPE_VARORDER ){ + view.distribution = args.d; + d = args.d; + } + view.c = contract; + var orders = db.UserContract.getOrders(contract, d, app.params.exists("csv")); + + if ( !app.params.exists("csv") ){ + + //show orders on disabled products + var disabledProducts = 0; + for ( o in orders ){ + if ( !db.Product.manager.get(o.productId, false).active ) { + disabledProducts++; + Reflect.setField(o, "disabled", true); + } + } + + view.disabledProducts = disabledProducts; + view.orders = orders; + } + + } + + + /** + * hidden feature : updates orders by setting current product price. + */ + function doUpdatePrices(contract:db.Contract, args:{?d:db.Distribution}) { + + sendNav(contract); + + if (!app.user.canManageContract(contract)) throw Error("/", t._("You do not have the authorization to manage this contract")); + if (contract.type == db.Contract.TYPE_VARORDER && args.d == null ) { + throw Redirect("/contractAdmin/selectDistrib/" + contract.id); + } + var d = null; + if (contract.type == db.Contract.TYPE_VARORDER ){ + view.distribution = args.d; + d = args.d; + } + + for ( o in contract.getOrders(d)){ + o.lock(); + o.productPrice = o.product.price; + if (contract.hasPercentageOnOrders()){ + o.feesRate = contract.percentageValue; + } + o.update(); + + } + throw Ok("/contractAdmin/orders/"+contract.id+"?d="+args.d.id, t._("Prices are now up to date.")); + } + + /** + * Duplicate a contract + */ + @tpl("form.mtt") + function doDuplicate(contract:db.Contract) { + + sendNav(contract); + if (!app.user.isAmapManager()) throw Error("/", t._("You do not have the authorization to manage this contract")); + + view.title = "Dupliquer le contrat '"+contract.name+"'"; + var form = new Form("duplicate"); + + form.addElement(new StringInput("name", t._("Name of the new contract"), contract.name.substr(0,50) + " - copy")); + form.addElement(new Checkbox("copyProducts", t._("Copy products"),true)); + form.addElement(new Checkbox("copyDeliveries", t._("Copy deliveries"),true)); + + if (form.checkToken()) { + + var nc = new db.Contract(); + nc.name = form.getValueOf("name"); + nc.startDate = contract.startDate; + nc.endDate = contract.endDate; + nc.amap = contract.amap; + nc.contact = contract.contact; + nc.description = contract.description; + nc.distributorNum = contract.distributorNum; + nc.flags = contract.flags; + nc.type = contract.type; + nc.vendor = contract.vendor; + nc.percentageName = contract.percentageName; + nc.percentageValue = contract.percentageValue; + nc.insert(); + + //give right to this contract + if(contract.contact!=null){ + var ua = db.UserAmap.get(contract.contact, contract.amap); + ua.giveRight(ContractAdmin(nc.id)); + } + + + if (form.getValueOf("copyProducts") == true) { + var prods = contract.getProducts(); + for ( source_p in prods) { + var p = new db.Product(); + p.name = source_p.name; + p.price = source_p.price; + p.contract = nc; + p.image = source_p.image; + p.desc = source_p.desc; + p.ref = source_p.ref; + p.stock = source_p.stock; + p.vat = source_p.vat; + p.organic = source_p.organic; + p.txpProduct = source_p.txpProduct; + p.unitType = source_p.unitType; + p.multiWeight = source_p.multiWeight; + p.variablePrice = source_p.variablePrice; + p.insert(); + + for (source_cat in source_p.getCategories()){ + var cat = new db.ProductCategory(); + cat.product = p; + cat.category = source_cat; + cat.insert(); + } + } + } + + if (form.getValueOf("copyDeliveries") == true) { + for ( ds in contract.getDistribs()) { + var d = new db.Distribution(); + d.contract = nc; + d.date = ds.date; + d.distributor1 = ds.distributor1; + d.distributor2 = ds.distributor2; + d.distributor3 = ds.distributor3; + d.distributor4 = ds.distributor4; + d.orderStartDate = ds.orderStartDate; + d.orderEndDate = ds.orderEndDate; + d.end = ds.end; + d.place = ds.place; + d.insert(); + } + } + + app.event(DuplicateContract(contract)); + + throw Ok("/contractAdmin/view/" + nc.id, t._("The contract has been duplicated")); + } + + view.form = form; + } + + + + /** + * Orders grouped by product + */ + @tpl("contractadmin/ordersByProduct.mtt") + function doOrdersByProduct(contract:db.Contract, args:{?d:db.Distribution}) { + + sendNav(contract); + if (!app.user.canManageContract(contract)) throw Error("/", t._("You do not have the authorization to manage this contract")); + if (contract.type == db.Contract.TYPE_VARORDER && args.d == null ) throw Redirect("/contractAdmin/selectDistrib/" + contract.id); + + var d = args != null ? args.d : null; + if (d == null) d = contract.getDistribs(false).first(); + if (d == null) throw Error("/contractAdmin/orders/"+contract.id,t._("There is no delivery in this contract, please create at least one distribution.")); + + var orders = service.ReportService.getOrdersByProduct(d,app.params.exists("csv")); + view.orders = orders; + view.distribution = d; + view.c = contract; + + } + + /** + * Purchase order to print + */ + @tpl("contractadmin/ordersByProductList.mtt") + function doOrdersByProductList(contract:db.Contract, args:{?d:db.Distribution}) { + + sendNav(contract); + if (!app.user.canManageContract(contract)) throw Error("/", t._("You do not have the authorization to manage this contract")); + if (contract.type == db.Contract.TYPE_VARORDER && args.d == null ) throw Redirect("/contractAdmin/selectDistrib/" + contract.id); + + if (contract.type == db.Contract.TYPE_VARORDER ) view.distribution = args.d; + view.c = contract; + view.group = contract.amap; + var d = args != null ? args.d : null; + if (d == null) d = contract.getDistribs(false).first(); + if (d == null) throw t._("No delivery in this contract"); + + var orders = service.ReportService.getOrdersByProduct(d,false); + view.orders = orders; + } + + /** + * Lists deliveries for this contract + */ + @tpl("contractadmin/deliveries.mtt") + function doDistributions(contract:db.Contract, ?args: { old:Bool } ) { + view.nav.push("distributions"); + sendNav(contract); + + if (!app.user.canManageContract(contract)) throw Error("/", t._("You do not have the authorization to manage this contract")); + view.c = contract; + + if (args != null && args.old) { + //display also old deliveries + view.deliveries = contract.getDistribs(false); + }else { + view.deliveries = db.Distribution.manager.search($end > DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 30) && $contract == contract, { orderBy:date} ); + } + + view.cycles = db.DistributionCycle.manager.search( $contract==contract && $endDate > Date.now() ,false); + + } + + /** + * Attendance to distribution + */ + @tpl("contractadmin/distributionp.mtt") + function doDistributionp(contract:db.Contract) { + view.nav.push("distributions"); + sendNav(contract); + + if (!app.user.canManageContract(contract)) throw Error("/", t._("You do not have the authorization to manage this contract")); + + var distribs = contract.getDistribs(false); + var userCount = new Map(); + var suscribers = contract.getUsers(); + for( u in suscribers) userCount[u.id] = 0; + //populate + + var increment = function(user:db.User){ + if(user==null) return; + var x = userCount[user.id]; + if(x==null){ + userCount[user.id] = 1; + }else{ + userCount[user.id] = x+1; + } + } + + for ( d in distribs) { + increment(d.distributor1); + increment(d.distributor2); + increment(d.distributor3); + increment(d.distributor4); + } + + var out1 = new Array<{user:db.User,count:Int}>();//suscribers + var out2 = new Array<{user:db.User,count:Int}>();//not suscribers + for( k in userCount.keys()){ + if(Lambda.find(suscribers,function(s) return s.id==k)!=null){ + out1.push( { user:db.User.manager.get(k), count:userCount[k] } ); + }else{ + out2.push( { user:db.User.manager.get(k), count:userCount[k] } ); + } + + } + + var num = (distribs.length*contract.distributorNum) / suscribers.length; + view.num = Std.string(num).substr(0,4); + view.numRounded = Math.round(num); + view.users = suscribers.length; + view.distributorNum = contract.distributorNum; + view.distribs = distribs.length; + view.c = contract; + view.participations = out1; + view.extParticipations = out2; + } + + @tpl("contractadmin/view.mtt") + function doView(contract:db.Contract) { + view.nav.push("view"); + sendNav(contract); + + if (!app.user.canManageContract(contract)) throw Error("/", t._("You do not have the authorization to manage this contract")); + view.c = view.contract = contract; + } + + + @tpl("contractadmin/stats.mtt") + function doStats(contract:db.Contract, ?args: { stat:Int } ) { + sendNav(contract); + if (!app.user.canManageContract(contract)) throw Error("/", t._("You do not have the authorization to manage this contract")); + view.c = contract; + + if (args == null) args = { stat:0 }; + view.stat = args.stat; + var pids = contract.getProducts().map(function(x) return x.id); + switch(args.stat) { + case 0 : + //ancienneté des amapiens + + if(pids.length==0){ + view.anciennete = new List(); + }else{ + view.anciennete = sys.db.Manager.cnx.request("select YEAR(u.cdate) as uyear ,count(DISTINCT u.id) as cnt from User u, UserContract up where up.userId=u.id and up.productId IN (" + pids.join(",") + ") group by uyear;").results(); + } + + case 1 : + //repartition des commandes + var repartition = new List(); + var total = 0; + var totalPrice = 0; + if(pids.length!=0){ + var repartition = sys.db.Manager.cnx.request("select sum(quantity) as quantity,productId,p.name,p.price from UserContract up, Product p where up.productId IN (" + contract.getProducts().map(function(x) return x.id).join(",") + ") and up.productId=p.id group by productId").results(); + for ( r in repartition) { + total += r.quantity; + totalPrice += r.price*r.quantity; + } + for (r in repartition) { + Reflect.setField(r, "percent", Math.round((r.quantity/total)*100) ); + } + + if ( app.params.exists("csv") ){ + sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(repartition), ["quantity","productId","name","price","percent"], "stats-" + contract.name+".csv"); + } + } + + view.repartition = repartition; + view.totalQuantity = total; + view.totalPrice = totalPrice; + + } + + } + + + + /** + * Efface une commande + * @param uc + */ + function doDelete(uc:UserContract) { + if (!app.user.canManageContract(uc.product.contract)) throw Error("/", t._("You do not have the authorization to manage this contract")); + uc.lock(); + uc.delete(); + throw Ok('/contractAdmin/orders/'+uc.product.contract.id, t._("The contract has been canceled")); + } + + + @tpl("contractadmin/selectDistrib.mtt") + function doSelectDistrib(c:db.Contract, ?args:{old:Bool}) { + view.nav.push("orders"); + sendNav(c); + + view.c = c; + if (args != null && args.old){ + view.distributions = c.getDistribs(false); + }else{ + view.distributions = c.getDistribs(true); + } + + } + + /** + * Edit a user's orders + */ + @tpl("contractadmin/edit.mtt") + function doEdit(c:db.Contract, ?user:db.User, args:{?d:db.Distribution}) { + view.nav.push("orders"); + sendNav(c); + + if (!app.user.canManageContract(c)) throw Error("/", t._("You do not have the authorization to manage this contract")); + if (args.d != null && args.d.validated) throw Error("/contractAdmin/orders/" + c.id + "?d=" + args.d.id, t._("This delivery has been already validated")); + + view.c = view.contract = c; + view.u = user; + view.distribution = args.d; + + //need to select a distribution for varying orders contracts + if (c.type == db.Contract.TYPE_VARORDER && args.d == null ) { + + throw Redirect("/contractAdmin/orders/" + c.id); + + }else { + + //members of the group + view.users = app.user.amap.getMembersFormElementData(); + + var userOrders = new Array<{order:db.UserContract,product:db.Product}>(); + var products = c.getProducts(false); + + for ( p in products) { + var ua = { order:null, product:p }; + + var order : db.UserContract = null; + if (c.type == db.Contract.TYPE_VARORDER) { + order = db.UserContract.manager.select($user == user && $productId == p.id && $distributionId==args.d.id, true); + }else { + order = db.UserContract.manager.select($user == user && $productId == p.id, true); + } + + if (order != null) ua.order = order; + userOrders.push(ua); + } + + //form check + if (checkToken()) { + + //it's a new order, the user has been defined in the form. + if (user == null) { + user = db.User.manager.get(Std.parseInt(app.params.get("user"))); + if (user == null){ + var user = app.params.get("user"); + throw t._("Unable to find user #::num::",{num:user}); + } + if (!user.isMemberOf(app.user.amap)) throw user + " is not member of this group"; + } + + //get distrib if needed + var distrib : db.Distribution = null; + if (c.type == db.Contract.TYPE_VARORDER) { + distrib = db.Distribution.manager.get(Std.parseInt(app.params.get("distribution")), false); + } + + var orders = []; + + for (k in app.params.keys()) { + var param = app.params.get(k); + if (k.substr(0, "product".length) == "product") { + + //trouve le produit dans userOrders + var pid = Std.parseInt(k.substr("product".length)); + var uo = Lambda.find(userOrders, function(uo) return uo.product.id == pid); + if (uo == null) throw t._("Unable to find product ::pid::", {pid:pid}); + + //user2 ? + var user2 : db.User = null; + var invert = false; + if (app.params.get("user2" + pid) != null && app.params.get("user2" + pid) != "0") { + user2 = db.User.manager.get(Std.parseInt(app.params.get("user2"+pid))); + if (user2 == null) { + var user = app.params.get("user2"); + throw t._("Unable to find user #::num::",{num:user}); + } + if (!user2.isMemberOf(app.user.amap)) throw t._("::user:: is not part of this group",{user:user2}); + if (user.id == user2.id) throw t._("Both selected accounts must be different ones"); + + invert = app.params.get("invert" + pid) == "1"; + } + + //filter quantity + var q = 0.0; + if (uo.product.hasFloatQt ) { + param = StringTools.replace(param, ",", "."); + q = Std.parseFloat(param); + }else { + q = Std.parseInt(param); + } + if(q==null) continue; //ignore bad qt input + + //record order + if (uo.order != null) { + //existing record + var o = OrderService.edit(uo.order, q, (app.params.get("paid" + pid) == "1"), user2, invert); + if (o != null) orders.push(o); + }else { + //new record + var o = OrderService.make(user, q, uo.product, distrib == null ? null : distrib.id, (app.params.get("paid" + pid) == "1"), user2, invert); + if (o != null) orders.push(o); + } + } + } + + app.event(MakeOrder(orders)); + db.Operation.onOrderConfirm(orders); + + if (distrib != null) { + throw Ok("/contractAdmin/orders/" + c.id +"?d="+distrib.id, t._("The order has been updated")); + }else { + throw Ok("/contractAdmin/orders/" + c.id, t._("The order has been updated")); + } + } + view.userOrders = userOrders; + } + } + +} diff --git a/src/controller/Controller.hx b/src/controller/Controller.hx new file mode 100755 index 0000000..b590ab2 --- /dev/null +++ b/src/controller/Controller.hx @@ -0,0 +1,28 @@ +package controller; + +/** + * Base Cagette.net Controller + * @author fbarbut + */ +class Controller extends sugoi.BaseController +{ + + var t: sugoi.i18n.GetText; + + public function new() + { + super(); + + //gettext translator + this.t = sugoi.i18n.Locale.texts; + + } + + public function checkIsLogged(){ + if(app.user==null) { + throw new tink.core.Error(t._("You should be logged in to perform this action.")); + } + } + + +} \ No newline at end of file diff --git a/src/controller/Cron.hx b/src/controller/Cron.hx new file mode 100755 index 0000000..2eea8b0 --- /dev/null +++ b/src/controller/Cron.hx @@ -0,0 +1,477 @@ +package controller; +import sugoi.db.Cache; +import sugoi.Web; +import sugoi.mail.Mail; +import Common; +using Lambda; +using tools.DateTool; + +class Cron extends Controller +{ + + public function doDefault() + { + + } + + /** + * CLI only en prod + */ + function canRun() { + if (App.current.user != null && App.current.user.isAdmin()){ + return true; + }else if (App.config.DEBUG) { + return true; + }else { + + if (Web.isModNeko) { + Sys.print("only CLI."); + return false; + }else { + return true; + } + } + } + + public function doMinute() { + + print("Cron.doMinute is called"); + + if (!canRun()) return; + + app.event(MinutelyCron); + + sendEmailsfromBuffer(); + + } + + /** + * Hourly Cron + * + * this function can be locally tested with `neko index.n cron/hour > cron.log` + */ + public function doHour() { + + app.event(HourlyCron); + + distribNotif(4,db.User.UserFlags.HasEmailNotif4h); //4h before + distribNotif(24,db.User.UserFlags.HasEmailNotif24h); //24h before + distribNotif(0, db.User.UserFlags.HasEmailNotifOuverture); //on command open + + distribValidationNotif(); + //sendOrdersByProductWhenOrdersClose(); + } + + + public function doDaily() { + if (!canRun()) return; + + app.event(DailyCron); + + //ERRORS MONITORING + var n = Date.now(); + var yest24h = new Date(n.getFullYear(), n.getMonth(), n.getDate(), 0, 0, 0); + var yest0h = DateTools.delta(yest24h, -1000 * 60 * 60 * 24); + + var errors = sugoi.db.Error.manager.search( $date < yest24h && $date > yest0h ); + if (errors.length > 0) { + var report = new StringBuf(); + report.add("

    " + App.config.NAME + " : ERRORS

    "); + for (e in errors) { + report.add("
    "+e.error + " at URL " + e.url + " ( user : " + (e.user!=null?e.user.toString():"none") + ", IP : " + e.ip + ")

    "); + } + + var m = new Mail(); + m.setSender(App.config.get("default_email"),"Cagette.net"); + m.addRecipient(App.config.get("webmaster_email")); + m.setSubject(App.config.NAME+" Errors"); + m.setHtmlBody( app.processTemplate("mail/message.mtt", { text:report.toString() } ) ); + App.sendMail(m); + } + + + //DEMO CONTRATS deletion after 7 days ( see controller.Group.doCreate() ) + db.Contract.manager.delete($name == "Contrat AMAP Maraîcher Exemple" && $startDate < DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 7)); + db.Contract.manager.delete($name == "Contrat Poulet Exemple" && $startDate < DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 7)); + + + //Old Messages cleaning + db.Message.manager.delete($date < DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 30 * 6)); + + //DB cleaning : I dont know how, but some people have empty string emails... + /*for ( u in db.User.manager.search($email == "", true)){ + u.email = Std.random(9999) + "@cagette.net"; + u.update(); + } + for ( u in db.User.manager.search($email2 == "", true)){ + u.email2 = null; + u.update(); + }*/ + + + } + + /** + * Send email notifications to users before a distribution + * @param hour + * @param flag + */ + function distribNotif(hour:Int, flag:db.User.UserFlags) { + + //trouve les distrib qui commencent dans le nombre d'heures demandé + //on recherche celles qui commencent jusqu'à une heure avant pour ne pas en rater + var from = DateTools.delta(Date.now(), 1000.0 * 60 * 60 * (hour-1)); + var to = DateTools.delta(Date.now(), 1000.0 * 60 * 60 * hour); + + //if (App.config.DEBUG) from = DateTools.delta(from, 1000.0 * 60 * 60 * 24 * -30); + + // dans le cas HasEmailNotifOuverture la date à prendre est le orderStartDate + // et non pas date qui est la date de la distribution + var distribs; + if ( db.User.UserFlags.HasEmailNotifOuverture == flag ) + distribs = db.Distribution.manager.search( $orderStartDate >= from && $orderStartDate <= to , false); + else + distribs = db.Distribution.manager.search( $date >= from && $date <= to , false); + + //Sys.print("distribNotif "+hour+" from "+from+" to "+to+"
    \n"); + + //on s'arrete immédiatement si aucune distibution trouvée + if (distribs.length == 0) return; + + //cherche plus tard si on a pas une "grappe" de distrib + /*while (true) { + var extraDistribs ; + if ( db.User.UserFlags.HasEmailNotifOuverture != flag ) + extraDistribs = db.Distribution.manager.search( $date >= to && $date = to && $orderStartDate 0) { + //on fait un tour de plus avec une heure plus tard + to = DateTools.delta(h, 1000.0 * 60 * 60); + }else { + //plus de distribs + break; + } + }*/ + + //on vérifie dans le cache du jour que ces distrib n'ont pas deja été traitées lors d'un cron précédent + var cacheId = Date.now().toString().substr(0, 10)+Std.string(flag); + var dist :Array = sugoi.db.Cache.get(cacheId); + if (dist != null) { + for (d in Lambda.array(distribs)) { + if (Lambda.exists(dist, function(x) return x == d.id)) { + // Comment this line in case of local test + distribs.remove(d); + } + } + }else { + dist = []; + } + + //toutes les distribs trouvées ont deja été traitées + if (distribs.length == 0) return; + + //stocke cache + for (d in distribs) dist.push(d.id); + Cache.set(cacheId, dist, 24 * 60 * 60); + + //We have now the distribs we want to notify about. + var distribsByContractId = new Map(); + for (d in distribs) { + if (d == null || d.contract==null) continue; + distribsByContractId.set(d.contract.id, d); + } + + //Boucle sur les distributions pour gerer le cas de plusieurs distributions le même jour sur le même contrat + var orders = []; + for (d in distribs) { + if (d == null || d.contract==null) continue; + //get orders for both type of contracts + for ( x in d.contract.getOrders(d)) orders.push(x); + } + + /* + * Group orders by users-group to receive separate emails by groups for the same user. + * Map key is $userId-$groupId + */ + var users = new Map , + vendors:Array + }>(); + + for (o in orders) { + + var x = users.get(o.user.id+"-"+o.product.contract.amap.id); + if (x == null) x = {user:o.user,distrib:null,products:[],vendors:[]}; + x.distrib = distribsByContractId.get(o.product.contract.id); + x.products.push(o); + users.set(o.user.id+"-"+o.product.contract.amap.id, x); + //trace (o.userId+"-"+o.product.contract.amap.id, x);Sys.print("
    \n"); + + // Prévenir également le deuxième user en cas des commandes alternées + if (o.user2 != null) { + var x = users.get(o.user2.id+"-"+o.product.contract.amap.id); + if (x == null) x = {user:o.user2,distrib:null,products:[],vendors:[]}; + x.distrib = distribsByContractId.get(o.product.contract.id); + x.products.push(o); + users.set(o.user2.id+"-"+o.product.contract.amap.id, x); + //trace (o.user2.id+"-"+o.product.contract.amap.id, x);Sys.print("
    \n"); + } + } + + //remove zero qt orders + for( k in users.keys()){ + var x = users.get(k); + var total = 0.0; + for( o in x.products) total += o.quantity; + if(total==0.0) users.remove(k); + } + + // Dans le cas de l'ouverture de commande, ce sont tous les users qu'il faut intégrer + if ( db.User.UserFlags.HasEmailNotifOuverture == flag ) + { + for (d in distribs) { + var memberList = d.contract.amap.getMembers(); + for (u in memberList) { + var x = users.get(u.id+"-"+d.contract.amap.id); + if (x == null) x = {user:u,distrib:null,products:[],vendors:[]}; + x.distrib = distribsByContractId.get(d.contract.id); + x.vendors.push(d.contract.vendor); + users.set(u.id+"-"+d.contract.amap.id, x); + //print(u.id+"-"+d.contract.amap.id, x); + } + } + } + + for ( u in users) { + + if (u.user.flags.has(flag) ) { + + if (u.user.email != null) { + var group = u.distrib.contract.amap; + this.t = sugoi.i18n.Locale.init(u.user.lang); //switch to the user language + + var text; + if ( db.User.UserFlags.HasEmailNotifOuverture == flag ) + { + //order opening notif + text = t._("Opening of orders for the delivery of ::date::", {date:view.hDate(u.distrib.date)}); + text += "
    "; + text += t._("The following suppliers are involved :"); + text += "
      "; + for ( v in u.vendors) { + text += "
    • " + v + "
    • "; + } + text += "
    "; + + }else{ + //Distribution notif to the users + var d = u.distrib; + text = t._("Do not forget the delivery on ::day:: from ::from:: to ::to::
    ", {day:view.dDate(d.date),from:view.hHour(d.date),to:view.hHour(d.end)}); + text += t._("Your products to collect :") + "
      "; + for ( p in u.products) { + text += "
    • "+p.quantity+" x "+p.product.getName(); + // Gerer le cas des contrats en alternance + if (p.user2 != null) { + text += " " + t._("alternated with") + " "; + if (u.user == p.user) + text += p.user2.getCoupleName(); + else + text += p.user.getCoupleName(); + } + text += "
    • "; + } + text += "
    "; + } + + if (u.distrib.isDistributor(u.user)) { + text += t._("Warning: you are in charge of the delivery ! Do not forget to print the attendance sheet."); + } + + try{ + var m = new Mail(); + m.setSender(App.config.get("default_email"), "Cagette.net"); + if(group.contact!=null) m.setReplyTo(group.contact.email, group.name); + m.addRecipient(u.user.email, u.user.getName()); + if (u.user.email2 != null) m.addRecipient(u.user.email2); + m.setSubject( group.name+" : "+t._("Distribution on ::date::",{date:app.view.hDate(u.distrib.date)}) ); + m.setHtmlBody( app.processTemplate("mail/message.mtt", { text:text,group:group } ) ); + App.sendMail(m , u.distrib.contract.amap); + }catch (e:Dynamic){ + app.logError(e); //email could be invalid + } + + } + } + } + } + + + /** + * Check if there is a multi-distrib to validate. + * + * Autovalidate it after 10 days + */ + function distribValidationNotif(){ + + var now = Date.now(); + + var from = now.setHourMinute( now.getHours(), 0 ); + var to = now.setHourMinute( now.getHours()+1 , 0); + + var explain = t._("

    This step is important in order to:

    "); + explain += t._("
    • Update orders if delivered quantities are different from ordered quantities
    • "); + explain += t._("
    • Confirm the reception of payments (checks, cash, transfers) in order to mark orders as 'paid'
    "); + + /* + * warn administrator if a distribution just ended + */ + var ds = db.Distribution.manager.search( !$validated && ($end >= from) && ($end < to) , false); + + for ( d in Lambda.array(ds)){ + if ( d.contract.type != db.Contract.TYPE_VARORDER ){ + ds.remove(d); + }else if ( !d.contract.amap.hasPayments() ){ + ds.remove(d); + } + } + + var ds = tools.ObjectListTool.deduplicateDistribsByKey(ds); + var view = App.current.view; + + for ( d in ds ){ + // var subj = "["+d.contract.amap.name+"] " + t._("Validation of the ::date:: distribution",{date:view.hDate(d.date)}); + var subj = t._("[::group::] Validation of the ::date:: distribution",{group : d.contract.amap.name , date : view.hDate(d.date)}); + + var url = "http://" + App.config.HOST + "/distribution/validate/"+d.date.toString().substr(0,10)+"/"+d.place.id; + + var html = t._("

    Your distribution just finished, don't forget to validate it

    "); + html += explain; + html += t._("

    Click here to validate the distribution (You must be connected to your group Cagette)", {distriburl:url}); + + App.quickMail(d.contract.amap.contact.email, subj, html); + } + + /* + * warn administrator if a distribution ended 3 days ago + */ + + var from = now.setHourMinute( now.getHours() , 0 ).deltaDays(-3); + var to = now.setHourMinute( now.getHours()+1 , 0).deltaDays(-3); + + //warn administrator if a distribution just ended + var ds = db.Distribution.manager.search( !$validated && ($end >= from) && ($end < to) , false); + + for ( d in Lambda.array(ds)){ + if ( d.contract.type != db.Contract.TYPE_VARORDER ){ + ds.remove(d); + }else if ( !d.contract.amap.hasPayments() ){ + ds.remove(d); + } + } + + var ds = tools.ObjectListTool.deduplicateDistribsByKey(ds); + + for ( d in ds ){ + // var subj = d.contract.amap.name + t._(": Validation of the delivery of the ") + App.current.view.hDate(d.date); + var subj = t._("[::group::] Validation of the ::date:: distribution",{group : d.contract.amap.name , date : view.hDate(d.date)}); + + var url = "http://" + App.config.HOST + "/distribution/validate/"+d.date.toString().substr(0,10)+"/"+d.place.id; + + var html = t._("

    Reminder: you have a delivery to validate.

    "); + html += explain; + html += t._("

    Click here to validate the delivery (You must be connected to your Cagette group)", {distriburl:url}); + + App.quickMail(d.contract.amap.contact.email, subj, html); + } + + + /* + * Autovalidate unvalidated distributions after 10 days + */ + var from = now.setHourMinute( now.getHours() , 0 ).deltaDays( 0 - db.Distribution.DISTRIBUTION_VALIDATION_LIMIT ); + var to = now.setHourMinute( now.getHours() + 1 , 0).deltaDays( 0 - db.Distribution.DISTRIBUTION_VALIDATION_LIMIT ); + print('AUTOVALIDATION'); + print('Find distributions from $from to $to'); + var ds = db.Distribution.manager.search( !$validated && ($end >= from) && ($end < to) , true); + for ( d in Lambda.array(ds)){ + if ( d.contract.type != db.Contract.TYPE_VARORDER ){ + ds.remove(d); + }else if ( !d.contract.amap.hasPayments() ){ + ds.remove(d); + } + } + for ( d in ds){ + print(d.toString()); + + service.PaymentService.validateDistribution(d); + + } + //email + var ds = tools.ObjectListTool.deduplicateDistribsByKey(ds); + for ( d in ds ){ + // var subj = d.contract.amap.name + t._(": Validation of the distribution of the ") + App.current.view.hDate(d.date); + var subj = t._("[::group::] Validation of the ::date:: distribution",{group : d.contract.amap.name , date : view.hDate(d.date)}); + var html = t._("

    As you did not validate it manually after 10 days,
    the delivery of the ::deliveryDate:: has been validated automatically

    ", {deliveryDate:App.current.view.hDate(d.date)}); + App.quickMail(d.contract.amap.contact.email, subj, html); + } + + } + + /** + * Send emails from buffer. + * + * Warning, if the cron is executed each minute, + * you should consider the right amount of emails to send each minute in order to avoid overlaping and getting in concurrency problems. + * (like "SELECT * FROM BufferedMail WHERE sdate IS NULL ORDER BY cdate DESC LIMIT 100 FOR UPDATE Lock wait timeout exceeded; try restarting transaction") + */ + function sendEmailsfromBuffer(){ + print("

    Send Emails from Buffer

    "); + + //send + for( e in sugoi.db.BufferedMail.manager.search($sdate==null,{limit:50,orderBy:-cdate},false) ){ + e.lock(); + if(e.isSent()) continue; + + print('#${e.id} - ${e.title}'); + e.finallySend(); + Sys.sleep(0.1); + } + + //delete old emails + var threeMonthsAgo = DateTools.delta(Date.now(), -1000.0*60*60*24*30*3); + sugoi.db.BufferedMail.manager.delete($cdate < threeMonthsAgo); + + //emails that cannot be sent + for( e in sugoi.db.BufferedMail.manager.search($tries>100,{limit:50,orderBy:-cdate},true) ){ + if(e.sender.email != App.config.get("default_email")){ + var str = t._("Sorry, the email entitled ::title:: could not be sent.",{title:e.title}); + App.quickMail(e.sender.email,t._("Email not sent"),str); + } + e.delete(); + } + } + + /** + * Email product report when orders close + **/ + function sendOrdersByProductWhenOrdersClose(){ + + var range = tools.DateTool.getLastHourRange(); + // Sys.println("Time is "+Date.now()+"
    "); + // Sys.println('Find all distributions that have closed in the last hour from ${range.from} to ${range.to} \n
    '); + + for ( d in db.Distribution.manager.search($orderEndDate >= range.from && $orderEndDate < range.to, false)){ + service.OrderService.sendOrdersByProductReport(d); + } + + } + + + function print(text){ + Sys.println( text + "
    " ); + } +} diff --git a/src/controller/Distribution.hx b/src/controller/Distribution.hx new file mode 100755 index 0000000..9c52481 --- /dev/null +++ b/src/controller/Distribution.hx @@ -0,0 +1,577 @@ +package controller; +import db.UserContract; +import sugoi.form.Form; +import sugoi.form.elements.HourDropDowns; +using tools.DateTool; +import Common; + +class Distribution extends Controller +{ + + + /** + * Attendance sheet by user-product (single distrib) + */ + @tpl('distribution/list.mtt') + function doList(d:db.Distribution) { + view.distrib = d; + view.place = d.place; + view.contract = d.contract; + view.orders = service.OrderService.prepare(d.getOrders()); + + } + + /** + * Attendance sheet by product-user (single distrib) + */ + @tpl('distribution/listByProductUser.mtt') + function doListByProductUser(d:db.Distribution) { + view.distrib = d; + view.place = d.place; + view.contract = d.contract; + // view.orders = UserContract.prepare(d.getOrders()); + + //make a 2 dimensons table : data[userId][productId] + //WARNING : BUGS WILL APPEAR if there is many Order line for the same product + var data = new Map>(); + var products = []; + var uo = d.getOrders(); + + for(o in uo){ + products.push(o.product); + } + + for ( o in service.OrderService.prepare(uo)) { + + var user = data[o.userId]; + if (user == null) user = new Map(); + user[o.productId] = o; + data[o.userId] = user; + + } + + //products + var products = tools.ObjectListTool.deduplicate(products); + products.sort(function(b, a) { + return (a.name < b.name)?1:-1; + }); + view.products = products; + + //users + var users = Lambda.array(d.getUsers()); + // var usersMap = tools.ObjectListTool.toIdMap(users); + users.sort(function(b, a) { + return (a.lastName < b.lastName)?1:-1; + }); + view.users = users; + // view.usersMap = usersMap; + + view.orders = data; + + //total to pay by user + view.totalByUser = function(uid:Int){ + var total = 0.0; + for( o in data[uid]) total+= o.total; + return total; + } + + //total qty of product + view.totalByProduct = function(pid:Int){ + var total = 0.0; + for( uid in data.keys()){ + var x = data[uid][pid]; + if(x!=null) total+= x.quantity; + } + return total; + } + + } + + /** + * Attendance sheet to print ( mutidistrib ) + */ + @tpl('distribution/listByDate.mtt') + function doListByDate(date:Date,place:db.Place, ?type:String, ?fontSize:String) { + + if (!app.user.isContractManager()) throw Error('/', t._("Forbidden action")); + + view.place = place; + + if (type == null) { + + //display form + var f = new sugoi.form.Form("listBydate", null, sugoi.form.Form.FormMethod.GET); + f.addElement(new sugoi.form.elements.RadioGroup("type", "Affichage", [ + { value:"one", label:t._("One person per page") }, + { value:"contract", label:t._("One person per page sorted by contract") }, + { value:"all", label:t._("All") }, + { value:"allshort", label:t._("All but without prices and totals") }, + ],"all")); + f.addElement(new sugoi.form.elements.RadioGroup("fontSize", "Taille de police", [ + { value:"S" , label:"S" }, + { value:"M" , label:"M" }, + { value:"L" , label:"L" }, + { value:"XL", label:"XL" }, + ], "S", "S", false)); + + view.form = f; + app.setTemplate("form.mtt"); + + if (f.checkToken()) { + var suburl = f.getValueOf("type")+"/"+f.getValueOf("fontSize"); + var url = '/distribution/listByDate/' + date.toString().substr(0, 10)+"/"+place.id+"/"+suburl; + throw Redirect( url ); + } + + return; + + }else { + + view.date = date; + view.fontRatio = switch(fontSize){ + case "M" : 125; //100x1.25 + case "L" : 156; //125x1.25 + case "XL": 195; //156x1.25 + default : 100; + }; + + switch(type) { + case "one": + app.setTemplate("distribution/listByDateOnePage.mtt"); + case "allshort" : + app.setTemplate("distribution/listByDateShort.mtt"); + case "contract" : + app.setTemplate("distribution/listByDateOnePageContract.mtt"); + } + + var d1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); + var d2 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59); + var contracts = app.user.amap.getActiveContracts(true); + //var cids = Lambda.map(contracts, function(c) return c.id); + var cconst = []; + var cvar = []; + for ( c in contracts) { + if (c.type == db.Contract.TYPE_CONSTORDERS) cconst.push(c.id); + if (c.type == db.Contract.TYPE_VARORDER) cvar.push(c.id); + } + + //commandes variables + var distribs = db.Distribution.manager.search(($contractId in cvar) && $date >= d1 && $date <= d2 && $place==place, false); + var orders = db.UserContract.manager.search($distributionId in Lambda.map(distribs, function(d) return d.id) , { orderBy:userId } ); + + //commandes fixes + var distribs = db.Distribution.manager.search(($contractId in cconst) && $date >= d1 && $date <= d2 && $place==place, false); + var orders = Lambda.array(orders); + for ( d in distribs) { + var orders2 = db.UserContract.manager.search($productId in Lambda.map(d.contract.getProducts(), function(d) return d.id) , { orderBy:userId } ); + orders = orders.concat(Lambda.array(orders2)); + } + + var orders3 = service.OrderService.prepare(Lambda.list(orders)); + view.orders = orders3; + + if (type == "csv") { + var data = new Array(); + + for (o in orders3) { + data.push( { + "name":o.userName, + "productName":o.productName, + "price":view.formatNum(o.productPrice), + "quantity":o.quantity, + "fees":view.formatNum(o.fees), + "total":view.formatNum(o.total), + "paid":o.paid + }); + } + + sugoi.tools.Csv.printCsvDataFromObjects(data, ["name", "productName", "price", "quantity","fees","total", "paid"],"Export-commandes-"+date.toString().substr(0,10)+"-Cagette"); + return; + } + } + } + + function doDelete(d:db.Distribution) { + + if (!app.user.isContractManager(d.contract)) throw Error('/', t._("Forbidden action")); + + var contractId = d.contract.id; + try { + service.DistributionService.delete(d); + } + catch(e:tink.core.Error){ + throw Error("/contractAdmin/distributions/" + contractId, e.message); + } + + throw Ok("/contractAdmin/distributions/" + contractId, t._("the delivery has been deleted")); + + } + + /** + * Edit a distribution + */ + @tpl('form.mtt') + function doEdit(d:db.Distribution) { + if (!app.user.isContractManager(d.contract)) throw Error('/', t._('Forbidden action') ); + var contract = d.contract; + + var form = sugoi.form.Form.fromSpod(d); + form.removeElement(form.getElement("contractId")); + form.removeElement(form.getElement("end")); + form.removeElement(form.getElement("distributionCycleId")); + var x = new sugoi.form.elements.HourDropDowns("end", t._("End time") ,d.end,true); + form.addElement(x, 3); + + if (d.contract.type == db.Contract.TYPE_VARORDER ) { + form.addElement(new sugoi.form.elements.DatePicker("orderStartDate", t._("Orders opening date"), d.orderStartDate)); + form.addElement(new sugoi.form.elements.DatePicker("orderEndDate", t._("Orders closing date"), d.orderEndDate)); + } + + if (form.isValid()) { + + var orderStartDate = null; + var orderEndDate = null; + + try{ + + if (d.contract.type == db.Contract.TYPE_VARORDER ) { + orderStartDate = form.getValueOf("orderStartDate"); + orderEndDate = form.getValueOf("orderEndDate"); + } + + d = service.DistributionService.edit(d, + form.getValueOf("date"), + form.getValueOf("end"), + form.getValueOf("placeId"), + form.getValueOf("distributor1Id"), + form.getValueOf("distributor2Id"), + form.getValueOf("distributor3Id"), + form.getValueOf("distributor4Id"), + orderStartDate, + orderEndDate); + + } + catch(e:tink.core.Error){ + throw Error('/contractAdmin/distributions/' + contract.id,e.message); + } + + if (d.date == null) { + var msg = t._("The distribution has been proposed to the supplier, please wait for its validation"); + throw Ok('/contractAdmin/distributions/'+contract.id, msg ); + } + else { + throw Ok('/contractAdmin/distributions/'+contract.id, t._("The distribution has been recorded") ); + } + + } + else { + app.event(PreEditDistrib(d)); + } + + view.form = form; + view.title = t._("Edit a distribution"); + } + + @tpl('form.mtt') + function doEditCycle(d:db.DistributionCycle) { + + if (!app.user.isContractManager(d.contract)) throw Error('/', 'Action interdite'); + + var form = sugoi.form.Form.fromSpod(d); + form.removeElement(form.getElement("contractId")); + + if (form.isValid()) { + form.toSpod(d); //update model + d.update(); + throw Ok('/contractAdmin/distributions/'+d.contract.id, t._("The delivery is now up to date")); + } + + view.form = form; + view.title = t._("Modify a delivery"); + } + + @tpl("form.mtt") + public function doInsert(contract:db.Contract) { + + if (!app.user.isContractManager(contract)) throw Error('/', t._('Forbidden action') ); + + var d = new db.Distribution(); + d.place = contract.amap.getMainPlace(); + var form = sugoi.form.Form.fromSpod(d); + form.removeElement(form.getElement("contractId")); + form.removeElement(form.getElement("distributionCycleId")); + form.removeElement(form.getElement("end")); + var x = new sugoi.form.elements.HourDropDowns("end", t._("End time") ); + form.addElement(x, 3); + + //default values + form.getElement("date").value = DateTool.now().deltaDays(30).setHourMinute(19, 0); + form.getElement("end").value = DateTool.now().deltaDays(30).setHourMinute(20, 0); + + if (contract.type == db.Contract.TYPE_VARORDER ) { + form.addElement(new sugoi.form.elements.DatePicker("orderStartDate", t._("Orders opening date"),DateTool.now().deltaDays(10).setHourMinute(8, 0))); + form.addElement(new sugoi.form.elements.DatePicker("orderEndDate", t._("Orders closing date"),DateTool.now().deltaDays(20).setHourMinute(23, 59))); + } + + if (form.isValid()) { + + var createdDistrib = null; + var orderStartDate = null; + var orderEndDate = null; + + try { + + if (contract.type == db.Contract.TYPE_VARORDER ) { + orderStartDate = form.getValueOf("orderStartDate"); + orderEndDate = form.getValueOf("orderEndDate"); + } + + createdDistrib = service.DistributionService.create( + contract, + form.getValueOf("date"), + form.getValueOf("end"), + form.getValueOf("placeId"), + form.getValueOf("distributor1Id"), + form.getValueOf("distributor2Id"), + form.getValueOf("distributor3Id"), + form.getValueOf("distributor4Id"), + orderStartDate, + orderEndDate); + } + catch(e:tink.core.Error){ + throw Error('/contractAdmin/distributions/' + contract.id,e.message); + } + + if (createdDistrib.date == null) { + var html = t._("Your request for a delivery has been sent to ::supplierName::.
    Be patient, you will receive an e-mail indicating if the request has been validated or refused.", {supplierName:contract.vendor.name}); + var btn = "OK"; + App.current.view.extraNotifBlock = App.current.processTemplate("block/modal.mtt",{html:html,title:t._("Distribution request sent"),btn:btn} ); + } else { + throw Ok('/contractAdmin/distributions/'+ createdDistrib.contract.id , t._("The distribution has been recorded") ); + } + + }else{ + //event + app.event(PreNewDistrib(contract)); + + } + + view.form = form; + view.title = t._("Create a distribution"); + } + + /** + * create a distribution cycle for a contract + */ + @tpl("form.mtt") + public function doInsertCycle(contract:db.Contract) { + + if (!app.user.isContractManager(contract)) throw Error('/', t._("Forbidden action")); + + var dc = new db.DistributionCycle(); + dc.place = contract.amap.getMainPlace(); + var form = sugoi.form.Form.fromSpod(dc); + form.removeElementByName("contractId"); + + form.getElement("startDate").value = DateTool.now(); + form.getElement("endDate").value = DateTool.now().deltaDays(30); + + //start hour + form.removeElementByName("startHour"); + var x = new HourDropDowns("startHour", t._("Start time"), DateTool.now().setHourMinute( 19, 0) , true); + form.addElement(x, 5); + + //end hour + form.removeElement(form.getElement("endHour")); + var x = new HourDropDowns("endHour", t._("End time"), DateTool.now().setHourMinute(20, 0), true); + form.addElement(x, 6); + + if (contract.type == db.Contract.TYPE_VARORDER){ + + form.getElement("daysBeforeOrderStart").value = 10; + form.getElement("daysBeforeOrderStart").required = true; + form.removeElementByName("openingHour"); + var x = new HourDropDowns("openingHour", t._("Opening time"), DateTool.now().setHourMinute(8, 0) , true); + form.addElement(x, 8); + + form.getElement("daysBeforeOrderEnd").value = 2; + form.getElement("daysBeforeOrderEnd").required = true; + form.removeElementByName("closingHour"); + var x = new HourDropDowns("closingHour", t._("Closing time"), DateTool.now().setHourMinute(23, 0) , true); + form.addElement(x, 10); + + }else{ + + form.removeElementByName("daysBeforeOrderStart"); + form.removeElementByName("daysBeforeOrderEnd"); + form.removeElementByName("openingHour"); + form.removeElementByName("closingHour"); + } + + if (form.isValid()) { + + var createdDistribCycle = null; + var daysBeforeOrderStart = null; + var daysBeforeOrderEnd = null; + var openingHour = null; + var closingHour = null; + + try{ + + if (contract.type == db.Contract.TYPE_VARORDER) { + daysBeforeOrderStart = form.getValueOf("daysBeforeOrderStart"); + daysBeforeOrderEnd = form.getValueOf("daysBeforeOrderEnd"); + openingHour = form.getValueOf("openingHour"); + closingHour = form.getValueOf("closingHour"); + } + + createdDistribCycle = service.DistributionService.createCycle( + contract, + form.getElement("cycleType").getValue(), + form.getValueOf("startDate"), + form.getValueOf("endDate"), + form.getValueOf("startHour"), + form.getValueOf("endHour"), + daysBeforeOrderStart, + daysBeforeOrderEnd, + openingHour, + closingHour, + form.getValueOf("placeId")); + } + catch(e:tink.core.Error){ + throw Error('/contractAdmin/distributions/' + contract.id,e.message); + } + + if (createdDistribCycle != null) { + throw Ok('/contractAdmin/distributions/'+ contract.id, t._("The delivery has been saved")); + } + + } + else{ + dc.contract = contract; + app.event(PreNewDistribCycle(dc)); + } + + view.form = form; + view.title = t._("Schedule a recurrent delivery"); + } + + /** + * Delete a distribution cycle + */ + public function doDeleteCycle(cycle:db.DistributionCycle){ + + if (!app.user.isContractManager(cycle.contract)) throw Error('/', t._("Forbidden action")); + + var contractId = cycle.contract.id; + var messages = service.DistributionService.deleteCycleDistribs(cycle); + if (messages.length > 0){ + App.current.session.addMessage( messages.join("
    "),true); + } + + throw Ok("/contractAdmin/distributions/" + contractId, t._("Recurrent deliveries deleted")); + } + + /** + * Doodle-like participation planning + */ + @tpl("distribution/planning.mtt") + public function doPlanning(contract:db.Contract) { + + view.contract = contract; + + var doodle = new Map}>(); + var distribs = contract.getDistribs(true, 150); + + for ( d in distribs ) { + for (u in [d.distributor1, d.distributor2, d.distributor3, d.distributor4]) { + if (u != null) { + + var udoodle = doodle.get(u.id); + + if (udoodle == null) udoodle = { user:u, planning:new Map() }; + udoodle.planning.set(d.id, true); + doodle.set(u.id, udoodle); + } + } + } + view.distribs = distribs; + view.doodle = doodle; + + } + + /** + * Ajax service for doPlanning() + */ + public function doRegister(args: { register:Bool, distrib:db.Distribution } ) { + + if (args != null) { + var d = args.distrib; + d.lock(); + + if (args.register) { + + if (d.distributor1 == null) d.distributor1 = app.user; + else if (d.distributor2 == null) d.distributor2 = app.user; + else if (d.distributor3 == null) d.distributor3 = app.user; + else if (d.distributor4 == null) d.distributor4 = app.user; + + }else { + if (d.distributor1 == app.user) d.distributor1 = null; + else if (d.distributor2 == app.user) d.distributor2 = null; + else if (d.distributor3 == app.user) d.distributor3 = null; + else if (d.distributor4 == app.user) d.distributor4 = null; + } + + d.update(); + } + + } + + /** + * Validate a multi-distrib + * @param date + * @param place + */ + @tpl('distribution/validate.mtt') + public function doValidate(date:Date, place:db.Place){ + + if (!app.user.isAmapManager()) throw t._("Forbidden access"); + + var md = MultiDistrib.get(date, place, db.Contract.TYPE_VARORDER); + + view.confirmed = md.checkConfirmed(); + view.users = md.getUsers(); + view.date = date; + view.place = place; + } + + + /** + * Admin can autovalidate a multidistrib + */ + @admin + public function doAutovalidate(date:Date,place:db.Place){ + + var md = MultiDistrib.get(date,place,db.Contract.TYPE_VARORDER); + for ( d in md.distributions){ + if(d.validated) continue; + service.PaymentService.validateDistribution(d); + } + throw Ok("/contractAdmin",t._("This distribution have been validated")); + } + + @admin + public function doUnvalidate(date:Date,place:db.Place){ + + var md = MultiDistrib.get(date,place,db.Contract.TYPE_VARORDER); + for ( d in md.distributions){ + if(!d.validated) continue; + service.PaymentService.unvalidateDistribution(d); + } + throw Ok("/contractAdmin",t._("This distribution have been Unvalidated")); + } + + + +} diff --git a/src/controller/Group.hx b/src/controller/Group.hx new file mode 100644 index 0000000..bd750c3 --- /dev/null +++ b/src/controller/Group.hx @@ -0,0 +1,368 @@ +package controller; +import sugoi.form.elements.StringInput; +import service.OrderService; +import service.WaitingListService; + +/** + * Groups + */ +class Group extends controller.Controller +{ + + /** + * Public page of a group + */ + @tpl('group/view.mtt') + function doDefault(group:db.Amap){ + + if (group.regOption == db.Amap.RegOption.Open) { + app.session.data.amapId = group.id; + throw Redirect("/"); + } + + view.group = group; + view.contracts = group.getActiveContracts(); + view.pageTitle = group.name; + group.getMainPlace(); //just to update cache + if (app.user != null){ + + view.isMember = Lambda.has(app.user.getAmaps(), group); + } + } + + /** + * Register to a waiting list. + * the user can be logged or not ! + */ + @tpl('form.mtt') + function doList(group:db.Amap){ + + //checks + if (group.regOption != db.Amap.RegOption.WaitingList) throw Redirect("/group/" + group.id); + if (app.user != null) { + try{ + WaitingListService.canRegister(app.user,group); + }catch(e:tink.core.Error){ + throw Error("/group/" + group.id,e.message); + } + } + + //build form + var form = new sugoi.form.Form("reg"); + if (app.user == null){ + form.addElement(new StringInput("userFirstName", t._("Your firstname"),"",true)); + form.addElement(new StringInput("userLastName", t._("Your lastname") ,"",true)); + form.addElement(new StringInput("userEmail", t._("Your e-mail"), "", true)); + } + form.addElement(new sugoi.form.elements.TextArea("msg", t._("Leave a message"))); + + if (form.isValid()){ + try{ + if (app.user == null){ + var f = form; + var user = service.UserService.softRegistration(f.getValueOf("userFirstName"),f.getValueOf("userLastName"), f.getValueOf("userEmail") ); + db.User.login(user, user.email); + } + + WaitingListService.registerToWl(app.user,group,form.getValueOf("msg")); + throw Ok("/group/" + group.id,t._("Your subscription to the waiting list has been recorded. You will receive an e-mail as soon as your request is processed.") ); + }catch(e:tink.core.Error){ + throw Error("/group/list/" + group.id,e.message); + } + + } + + view.title = t._("Subscription to \"::groupeName::\" waiting list", {groupeName:group.name}); + view.form = form; + } + + /** + Cancel suscription request + **/ + function doListCancel(group:db.Amap){ + try{ + WaitingListService.removeFromWl(app.user,group); + }catch(e:tink.core.Error){ + throw Error("/group/" + group.id,e.message); + } + throw Ok("/group/" + group.id,t._("You've been removed from the waiting list")); + } + + + /** + * Register direclty in an open group + * + * the user can be logged or not ! + + @tpl('form.mtt') + function doRegister(group:db.Amap){ + + if (group.regOption != db.Amap.RegOption.Open) throw Redirect("/group/" + group.id); + if (app.user != null){ + if ( db.UserAmap.manager.select($amapId == group.id && $user == app.user) != null) throw Error("/group/" + group.id, t._("You are already member of this group.")); + } + + var form = new sugoi.form.Form("reg"); + form.submitButtonLabel = t._("Join the group"); + form.addElement(new sugoi.form.elements.Html("html",t._("Confirm your subscription to \"::groupName::\"", {groupName:group.name}))); + if (app.user == null){ + form.addElement(new StringInput("userFirstName", t._("Your firstname"),"",true)); + form.addElement(new StringInput("userLastName", t._("Your lastname"), "", true)); + var em = new StringInput("userEmail", t._("Your e-mail"), "", true); + em.addValidator(new EmailValidator()); + form.addElement(em); + form.addElement(new StringInput("address", t._("Address"), "", true)); + form.addElement(new StringInput("zipCode", t._("Zip code"), "", true)); + form.addElement(new StringInput("city", t._("City"), "", true)); + form.addElement(new StringInput("phone", t._("Phone"), "", true)); + } + + if (form.isValid()){ + + if (app.user == null){ + var f = form; + var user = new db.User(); + user.email = f.getValueOf("userEmail"); + user.firstName = f.getValueOf("userFirstName"); + user.lastName = f.getValueOf("userLastName"); + user.address1 = f.getValueOf("address"); + user.zipCode = f.getValueOf("zipCode"); + user.city = f.getValueOf("city"); + user.phone = f.getValueOf("phone"); + + if ( db.User.getSameEmail(user.email).length > 0 ) { + throw Ok("/user/login",t._("You already subscribed to Cagette.net, please log in on this page")); + } + + user.insert(); + app.session.setUser(user); + + } + + var w = new db.UserAmap(); + w.user = app.user; + w.amap = group; + w.insert(); + + throw Ok("/user/choose", t._("Your subscription has been taken into account")); + } + + view.title = t._("Subscription to \"::groupName::\"", {groupName:group.name}); + view.form = form; + + }*/ + + /** + * create a new group + */ + @tpl("form.mtt") + function doCreate() { + + view.title = t._("Create a new Cagette Group"); + + var f = new sugoi.form.Form("c"); + f.addElement(new StringInput("name", t._("Name of your group"), "", true)); + + //group type + var data = [ + {label:t._("CSA"),value:"0"}, + {label:t._("Grouped orders"),value:"1"}, + {label:t._("Farmers collective"),value:"2"}, + {label:t._("Farm shop"),value:"3"}, + ]; + var gt = new sugoi.form.elements.RadioGroup("type", t._("Group type"), data ,"1","1",true,true,true); + f.addElement(gt); + + if (f.checkToken()) { + + var user = app.user; + + var g = new db.Amap(); + g.name = f.getValueOf("name"); + g.contact = user; + + var type:db.Amap.GroupType = Type.createEnumIndex(db.Amap.GroupType, Std.parseInt(f.getValueOf("type")) ); + + switch(type){ + case null : + throw "unknown group type"; + case db.Amap.GroupType.Amap : + g.flags.set(db.Amap.AmapFlags.HasMembership); + g.regOption = db.Amap.RegOption.WaitingList; + + case db.Amap.GroupType.GroupedOrders : + g.flags.set(db.Amap.AmapFlags.ShopMode); + g.flags.set(db.Amap.AmapFlags.HasMembership); + g.regOption = db.Amap.RegOption.WaitingList; + + case db.Amap.GroupType.ProducerDrive : + g.flags.set(db.Amap.AmapFlags.ShopMode); + g.regOption = db.Amap.RegOption.Open; + g.flags.set(db.Amap.AmapFlags.PhoneRequired); + + case db.Amap.GroupType.FarmShop : + g.flags.set(db.Amap.AmapFlags.ShopMode); + g.regOption = db.Amap.RegOption.Open; + g.flags.set(db.Amap.AmapFlags.PhoneRequired); + } + + g.groupType = type; + g.insert(); + + var ua = new db.UserAmap(); + ua.user = user; + ua.amap = g; + ua.rights = [db.UserAmap.Right.GroupAdmin,db.UserAmap.Right.Membership,db.UserAmap.Right.Messages,db.UserAmap.Right.ContractAdmin(null)]; + ua.insert(); + + //example datas + var place = new db.Place(); + place.name = t._("Market square"); + place.zipCode = "000"; + place.city = "St Martin de la Cagette"; + place.amap = g; + place.insert(); + + //contrat AMAP + var vendor = new db.Vendor(); + vendor.amap = g; + vendor.name = "Jean Martin EARL"; + vendor.zipCode = "000"; + vendor.city = "Langon"; + vendor.email = "jean@cagette.net"; + vendor.insert(); + + if (type == Amap){ + var contract = new db.Contract(); + contract.name = t._("CSA contract Vegetables - Example"); + contract.description = t._("This contract is an example where the customer has to commit to buy the whole year as with AMAPs"); + contract.amap = g; + contract.type = 0; + contract.vendor = vendor; + contract.startDate = Date.now(); + contract.endDate = DateTools.delta(Date.now(), 1000.0 * 60 * 60 * 24 * 364); + contract.contact = user; + contract.distributorNum = 2; + contract.insert(); + + var p = new db.Product(); + p.name = t._("Big basket of vegetables"); + p.price = 15; + p.organic = true; + p.contract = contract; + p.insert(); + + var p = new db.Product(); + p.name = t._("Small basket of vegetables"); + p.price = 10; + p.organic = true; + p.contract = contract; + p.insert(); + + OrderService.make(user, 1, p, null, true); + + var d = new db.Distribution(); + d.contract = contract; + d.date = DateTools.delta(Date.now(), 1000.0 * 60 * 60 * 24 * 14); + d.end = DateTools.delta(d.date, 1000.0 * 60 * 90); + d.place = place; + d.insert(); + + } + + //contrat variable + var vendor = new db.Vendor(); + vendor.amap = g; + vendor.name = t._("Farm Galinette"); + vendor.zipCode = "000"; + vendor.city = t._("Bazas"); + vendor.email = "galinette@cagette.net"; + vendor.insert(); + + var contract = new db.Contract(); + contract.name = t._("Chicken Contract - Example"); + contract.description = t._("Example of contract with variable orders. It is allowed to order something else at every delivery."); + contract.amap = g; + contract.type = 1; + contract.vendor = vendor; + contract.startDate = Date.now(); + contract.endDate = DateTools.delta(Date.now(), 1000.0 * 60 * 60 * 24 * 364); + contract.contact = user; + contract.distributorNum = 2; + contract.flags.set(db.Contract.ContractFlags.UsersCanOrder); + contract.insert(); + + var egg = new db.Product(); + egg.name = t._("12 eggs"); + egg.price = 5; + //egg.type = 6; + egg.organic = true; + egg.contract = contract; + egg.insert(); + + var p = new db.Product(); + p.name = t._("Chicken"); + //p.type = 2; + p.price = 9.50; + p.organic = true; + p.contract = contract; + p.insert(); + + var d = new db.Distribution(); + d.contract = contract; + d.orderStartDate = Date.now(); + d.orderEndDate = DateTools.delta(Date.now(), 1000.0 * 60 * 60 * 24 * 19); + d.date = DateTools.delta(Date.now(), 1000.0 * 60 * 60 * 24 * 21); + d.end = DateTools.delta(d.date, 1000.0 * 60 * 90); + d.place = place; + d.insert(); + + OrderService.make(user, 2, egg, d.id); + OrderService.make(user, 1, p, d.id); + + App.current.session.data.amapId = g.id; + app.session.data.newGroup = true; + throw Redirect("/"); + } + + view.form= f; + + } + + /** + Displays a google map in a popup + **/ + @tpl('group/place.mtt') + public function doPlace(place:db.Place){ + view.place = place; + + //build adress for google maps + var addr = ""; + if (place.address1 != null) addr += place.address1; + if (place.address2 != null) addr += ", " + place.address2; + if (place.zipCode != null) addr += " " + place.zipCode; + if (place.city != null) addr += " " + place.city; + + view.addr = view.escapeJS(addr); + } + + /** + * Groups map + */ + @tpl("group/map.mtt") + public function doMap(?args:{?lat:Float,?lng:Float,?address:String}){ + + view.container = "container-fluid"; + + //if no param is sent, focus on Paris + if (args == null || (args.address == null && args.lat == null && args.lng == null)){ + args = {lat:48.855675, lng:2.3472365}; + } + + view.lat = args.lat; + view.lng = args.lng; + view.address = args.address; + } + + +} \ No newline at end of file diff --git a/src/controller/Install.hx b/src/controller/Install.hx new file mode 100644 index 0000000..9b63450 --- /dev/null +++ b/src/controller/Install.hx @@ -0,0 +1,273 @@ +package controller; +import sugoi.db.Variable; +import sugoi.form.elements.StringInput; +import thx.semver.Version; + +/** + * ... + * @author fbarbut + */ +class Install extends controller.Controller +{ + /** + * checks if its a first install or an update + */ + @tpl("install/default.mtt") + public function doDefault() { + if (db.User.manager.get(1) == null) { + + throw Redirect("/install/firstInstall"); + + }else { + //throw Error("/", "L'utilisateur admin a déjà été créé. Essayez de vous connecter avec admin@cagette.net, mot de passe : admin"); + + var status = new Array<{parameter:String,valid:Bool,message:String}>(); + + status.push(getVersionStatus()); + + view.status = status; + } + } + + public function doDiagnostics(){ + + var webroot = sugoi.Web.getCwd(); + + if(!sys.FileSystem.exists(webroot+"file")){ + Sys.println("no File directory : created"); + sys.FileSystem.createDirectory(webroot+"file"); + } + if(!sys.FileSystem.exists(webroot+"file/.htaccess")){ + Sys.println("no .htaccess file in 'File' directory"); + } + if(!sys.FileSystem.exists(webroot+"../tmp")) { + Sys.println("no tmp directory : created"); + sys.FileSystem.createDirectory(webroot+"../tmp"); + } + + } + + /** + * First install + */ + @tpl("form.mtt") + public function doFirstInstall(){ + view.title = "Installation de Cagette.net"; + + var f = new sugoi.form.Form("c"); + f.addElement(new StringInput("amapName", t._("Name of your group"),"",true)); + f.addElement(new StringInput("userFirstName", t._("Your firstname"),"",true)); + f.addElement(new StringInput("userLastName", t._("Your lastname"),"",true)); + + if (f.checkToken()) { + + var user = new db.User(); + user.firstName = f.getValueOf("userFirstName"); + user.lastName = f.getValueOf("userLastName"); + user.email = "admin@cagette.net"; + user.setPass("admin"); + user.insert(); + + var amap = new db.Amap(); + amap.name = f.getValueOf("amapName"); + amap.contact = user; + + amap.flags.set(db.Amap.AmapFlags.HasMembership); + //amap.flags.set(db.Amap.AmapFlags.IsAmap); + amap.insert(); + + var ua = new db.UserAmap(); + ua.user = user; + ua.amap = amap; + ua.rights = [db.UserAmap.Right.GroupAdmin,db.UserAmap.Right.Membership,db.UserAmap.Right.Messages,db.UserAmap.Right.ContractAdmin(null)]; + ua.insert(); + + //example datas + var place = new db.Place(); + place.name = t._("Marketplace"); + place.amap = amap; + place.address1 = t._("Place Jules Verne"); + place.zipCode = "00000"; + place.city = t._("St Martin de la Cagette"); + place.insert(); + + var vendor = new db.Vendor(); + vendor.amap = amap; + + vendor.name = t._("Jean Martin EURL"); + vendor.email = "jean.martin@cagette.net"; + vendor.zipCode = "00000"; + vendor.city = "Martignac"; + vendor.insert(); + + var contract = new db.Contract(); + contract.name = t._("Vegetables Contract Example"); + contract.amap = amap; + contract.type = 0; + contract.vendor = vendor; + contract.startDate = Date.now(); + contract.endDate = DateTools.delta(Date.now(), 1000.0 * 60 * 60 * 24 * 364); + contract.contact = user; + contract.distributorNum = 2; + contract.insert(); + + var p = new db.Product(); + p.name = t._("Big basket of vegetables"); + p.price = 15; + p.vat = 5; + p.contract = contract; + p.insert(); + + var p = new db.Product(); + p.name = t._("Small basket of vegetables"); + p.price = 10; + p.vat = 5; + p.contract = contract; + p.insert(); + + var uc = new db.UserContract(); + uc.user = user; + uc.product = p; + uc.paid = true; + uc.quantity = 1; + uc.productPrice = 10; + uc.insert(); + + var d = new db.Distribution(); + d.contract = contract; + d.date = DateTools.delta(Date.now(), 1000.0 * 60 * 60 * 24 * 14); + d.end = DateTools.delta(d.date, 1000.0 * 60 * 90); + d.place = place; + d.insert(); + + App.current.user = null; + App.current.session.setUser(user); + App.current.session.data.amapId = amap.id; + + throw Ok("/", t._("Group and user 'admin' created. Your email is 'admin@cagette.net' and your password is 'admin'")); + } + + view.form= f; + } + + /** + * get version status + */ + private function getVersionStatus(){ + + + var out = {parameter:"version", valid:false, message:""}; + + var v = Variable.get("version"); + if (v == null || v=="") { + Variable.set("version", App.VERSION.toString()); + v = App.VERSION.toString(); + } + + var v :thx.semver.Version = thx.semver.Version.stringToVersion(v); + + if (v.lessThan(App.VERSION)){ + + //need update ! + out.valid = false; + out.message = t._("You must update your database to version ") +App.VERSION.toString()+""; + + }else{ + out.valid = true; + out.message = t._("Current version") +v.toString()+""; + } + + return out; + + } + + /** + * perform migrations from a version to another + */ + @admin + public function doUpdateversion(){ + + var log = []; + + var currentVersion = thx.semver.Version.stringToVersion(Variable.get("version")); + + //Migrations to 0.9.2 + if (currentVersion.lessThan( thx.semver.Version.arrayToVersion([0,9,2]) )){ + + log.push(t._("Installation of the dictionnary of products (taxonomy)")); + _0_9_2_installTaxonomy(); + + log.push(t._("Improvement on saving orders")); + _0_9_2_dbMigration(); + + sugoi.db.Variable.set("version", "0.9.2"); + } + + //Migrations to 1.0.0 + //... + + throw Ok("/install", t._("Following update have been performed:
      ")+Lambda.map(log,function(x) return "
    • "+x+"
    • ").join("")+"
    "); + + } + + @admin + function _0_9_2_installTaxonomy(){ + + db.TxpCategory.manager.delete(true); + db.TxpSubCategory.manager.delete(true); + db.TxpProduct.manager.delete(true); + + var taxo = sys.io.File.getContent(sugoi.Web.getCwd() + "../data/productTaxonomy.json"); + var taxo = haxe.Json.parse(taxo); + + var categories : Array = taxo.categories; + var subcategories : Array = taxo.subCategories; + var products : Array = taxo.products; + + for ( c in categories){ + var cat = new db.TxpCategory(); + cat.id = c.id; + cat.name = c.name; + cat.insert(); + } + + for ( sc in subcategories){ + var scat = new db.TxpSubCategory(); + scat.id = sc.id; + scat.name = sc.name; + scat.category = db.TxpCategory.manager.get(Std.parseInt(sc.category)); + scat.insert(); + + } + + for ( p in products){ + var pro = new db.TxpProduct(); + pro.name = p.name; + pro.id = p.id; + pro.category = db.TxpCategory.manager.get(Std.parseInt(p.category)); + pro.subCategory = db.TxpSubCategory.manager.get(Std.parseInt(p.subCategory)); + pro.insert(); + } + + } + + @admin + function _0_9_2_dbMigration(){ + + //recompute prices on orders + for ( order in db.UserContract.manager.all(true)){ + order.productPrice = order.product.price; + order.feesRate = order.product.contract.percentageValue; + order.update(); + } + + + //activate payment orders + for ( a in db.Amap.manager.all(true)){ + a.allowedPaymentsType = ["cash", "transfer", "check"]; + a.update(); + } + + } + +} \ No newline at end of file diff --git a/src/controller/Main.hx b/src/controller/Main.hx new file mode 100755 index 0000000..677a4cf --- /dev/null +++ b/src/controller/Main.hx @@ -0,0 +1,261 @@ +package controller; +import db.Distribution; +import db.UserContract; +import haxe.Json; +import haxe.web.Dispatch; +import sugoi.form.elements.StringInput; +import sugoi.tools.ResultsBrowser; +import Common; +import tools.ArrayTool; + +class Main extends Controller { + + + /** + * public pages + */ + function doGroup(d:haxe.web.Dispatch){ + d.dispatch(new controller.Group()); + } + + /** + Group homepage + **/ + @tpl("home.mtt") + function doDefault() { + view.category = 'home'; + + var group = app.getCurrentGroup(); + if ( app.user!=null && group == null) { + throw Redirect("/user/choose"); + }else if (app.user == null && (group==null || group.regOption!=db.Amap.RegOption.Open) ) { + throw Redirect("/user/login"); + } + + view.amap = group; + + //contract with open orders + var openContracts = Lambda.filter(group.getActiveContracts(), function(c) return c.isUserOrderAvailable()); + view.openContracts = openContracts; + + //register to become "distributor" + view.contractsWithDistributors = app.user==null ? [] : Lambda.filter(app.user.amap.getActiveContracts(), function(c) return c.distributorNum > 0); + + //freshly created group + view.newGroup = app.session.data.newGroup == true; + + var n = Date.now(); + var now = new Date(n.getFullYear(), n.getMonth(), n.getDate(), 0, 0, 0); + var in3Month = DateTools.delta(now, 1000.0 * 60 * 60 * 24 * 30 * 3); + + var distribs = MultiDistrib.getFromTimeRange(group,now,in3Month); + view.distribs = distribs; + + //view functions + view.getWhosTurn = function(orderId:Int, distrib:Distribution) { + return db.UserContract.manager.get(orderId, false).getWhosTurn(distrib); + } + + //event for additionnal blocks on home page + var e = Blocks([], "home"); + app.event(e); + view.blocks = e.getParameters()[0]; + + //message if phone is required + if(app.user!=null && app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && app.user.phone==null){ + app.session.addMessage(t._("Members of this group should provide a phone number. Please click here to update your account."),true); + } + + } + + //login and stuff + function doUser(d:Dispatch) { + d.dispatch(new controller.User()); + } + + function doCron(d:Dispatch) { + d.dispatch(new controller.Cron()); + } + + /** + * JSON REST API Entry point + */ + function doApi(d:Dispatch) { + + try { + + d.dispatch(new controller.Api()); + + }catch (e:tink.core.Error){ + + //manage tink Errors (service errors) + sugoi.Web.setReturnCode(e.code); + Sys.print(Json.stringify( {error:{code:e.code,message:e.message,stack:e.exceptionStack}} )); + + }catch (e:Dynamic){ + + //manage other errors + sugoi.Web.setReturnCode(500); + var stack = if ( App.config.DEBUG ) haxe.CallStack.toString(haxe.CallStack.exceptionStack()) else ""; + App.current.logError(e, stack); + Sys.print(Json.stringify( {error:{code:500,message : Std.string(e), stack:stack }} )); + } + + } + + @tpl("cssDemo.mtt") + function doCssdemo() { + view.category = 'home'; + } + + @tpl("form.mtt") + function doInstall(d:Dispatch) { + d.dispatch(new controller.Install()); + } + + + function doP(d:Dispatch) { + + /* + * Invalid array access +Stack (ADMIN|DEBUG) + +Called from C:\HaxeToolkit\haxe\std/haxe/web/Dispatch.hx line 463 +Called from controller/Main.hx line 117 + * + var plugin = d.parts.shift(); + for ( p in App.plugins) { + var n = Type.getClassName(Type.getClass(p)).toLowerCase(); + n = n.split(".").pop(); + if (plugin == n) { + d.dispatch( p.getController() ); + return; + } + } + + throw Error("/","Plugin '"+plugin+"' introuvable."); + */ + + d.dispatch(new controller.Plugin()); + } + + + @logged + function doMember(d:Dispatch) { + view.category = 'members'; + d.dispatch(new controller.Member()); + } + + @logged + function doStats(d:Dispatch) { + view.category = 'stats'; + d.dispatch(new Stats()); + } + + @logged + function doAccount(d:Dispatch) { + view.category = 'account'; + d.dispatch(new controller.Account()); + } + + @logged + function doVendor(d:Dispatch) { + view.category = 'contractadmin'; + d.dispatch(new controller.Vendor()); + } + + @logged + function doPlace(d:Dispatch) { + view.category = 'contractadmin'; + d.dispatch(new controller.Place()); + } + + @logged + function doTransaction(d:Dispatch) { + view.category = 'members'; + d.dispatch(new controller.Transaction()); + } + + @logged + function doDistribution(d:Dispatch) { + view.category = 'contractadmin'; + d.dispatch(new controller.Distribution()); + } + + @logged + function doMembership(d:Dispatch) { + view.category = 'members'; + d.dispatch(new controller.Membership()); + } + + function doShop(d:Dispatch) { + view.category = 'shop'; + d.dispatch(new controller.Shop()); + } + + @tpl('shop/default2.mtt') + function doShop2(place:db.Place, date:String) { + view.category = 'shop'; + view.place = place; + view.date = date; + } + + @logged + function doProduct(d:Dispatch) { + view.category = 'contractadmin'; + d.dispatch(new controller.Product()); + } + + @logged + function doAmap(d:Dispatch) { + view.category = 'amap'; + d.dispatch(new controller.Amap()); + } + + @logged + function doContract(d:Dispatch) { + view.category = 'contract'; + d.dispatch(new Contract()); + } + + @logged + function doContractAdmin(d:Dispatch) { + view.category = 'contractadmin'; + d.dispatch(new ContractAdmin()); + } + + @logged + function doMessages(d:Dispatch) { + view.category = 'messages'; + d.dispatch(new Messages()); + } + + @logged + function doAmapadmin(d:Dispatch) { + view.category = 'amapadmin'; + d.dispatch(new AmapAdmin()); + } + + @logged + function doValidate(date:Date, place:db.Place, user:db.User, d:haxe.web.Dispatch){ + + var v = new controller.Validate(); + v.date = date; + v.place = place; + v.user = user; + d.dispatch(v); + } + + @admin + function doAdmin(d:Dispatch) { + d.dispatch(new controller.admin.Admin()); + } + + @admin + function doDb(d:Dispatch) { + d.parts = []; //disable haxe.web.Dispatch + sys.db.Admin.handler(); + } + + +} diff --git a/src/controller/Member.hx b/src/controller/Member.hx new file mode 100755 index 0000000..138441a --- /dev/null +++ b/src/controller/Member.hx @@ -0,0 +1,741 @@ +package controller; +import Common; +import haxe.Utf8; +import sugoi.form.Form; +import sugoi.form.elements.Selectbox; +import sugoi.form.validators.EmailValidator; +import sugoi.tools.Utils; + + +class Member extends Controller +{ + + public function new() + { + super(); + if (!app.user.canAccessMembership()) throw Redirect("/"); + } + + @logged + @tpl('member/default.mtt') + function doDefault(?args: { ?search:String, ?select:String } ) { + checkToken(); + + var browse:Int->Int->List; + var uids = db.UserAmap.manager.search($amap == app.user.getAmap(), false); + var uids = Lambda.map(uids, function(ua) return ua.user.id); + if (args != null && args.search != null) { + + //SEARCH + browse = function(index:Int, limit:Int) { + var search = "%"+StringTools.trim(args.search)+"%"; + return db.User.manager.search( + ($lastName.like(search) || + $lastName2.like(search) || + $address1.like(search) || + $address2.like(search) || + $firstName.like(search) || + $firstName2.like(search) + ) && $id in uids , { orderBy:-id }, false); + } + view.search = args.search; + + }else if(args!=null && args.select!=null){ + + //SELECTION + + switch(args.select) { + case "nocontract": + if (app.params.exists("csv")) { + sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_NoContracts()), ["firstName", "lastName", "email"], t._("Without contracts")); + return; + }else { + browse = function(index:Int, limit:Int) { return db.User.getUsers_NoContracts(index, limit); } + } + case "contract": + + if (app.params.exists("csv")) { + sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_Contracts()), ["firstName", "lastName", "email"], t._("With orders")); + return; + }else { + browse = function(index:Int, limit:Int) { return db.User.getUsers_Contracts(index, limit); } + } + + case "nomembership" : + if (app.params.exists("csv")) { + sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_NoMembership()), ["firstName", "lastName", "email"], t._("Memberships to be renewed")); + return; + }else { + browse = function(index:Int, limit:Int) { return db.User.getUsers_NoMembership(index, limit); } + } + case "newusers" : + if (app.params.exists("csv")) { + sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.getUsers_NewUsers()), ["firstName", "lastName", "email"], t._("Never connected")); + return; + }else { + browse = function(index:Int, limit:Int) { return db.User.getUsers_NewUsers(index, limit); } + } + default: + throw t._("Unknown selection"); + } + view.select = args.select; + + }else { + if (app.params.exists("csv")) { + var headers = ["firstName", "lastName", "email","phone", "firstName2", "lastName2","email2","phone2", "address1","address2","zipCode","city"]; + sugoi.tools.Csv.printCsvDataFromObjects(Lambda.array(db.User.manager.search( $id in uids, {orderBy:lastName}, false)), headers, t._("Members")); + return; + }else { + //default display + browse = function(index:Int, limit:Int) { + return db.User.manager.search( $id in uids, { limit:[index,limit], orderBy:lastName }, false); + } + } + } + + var count = uids.length; + var rb = new sugoi.tools.ResultsBrowser(count, (args.select!=null||args.search!=null)?1000:10, browse); + view.members = rb; + + if (args.select == null || args.select != "newusers") { + //count new users + view.newUsers = db.User.getUsers_NewUsers().length; + } + + view.waitingList = db.WaitingList.manager.count($group == app.user.amap); + + } + + /** + * Move to waiting list + */ + function doMovetowl(u:db.User){ + + var ua = db.UserAmap.get(u, app.user.amap, true); + ua.delete(); + + var wl = new db.WaitingList(); + wl.user = u; + wl.group = app.user.amap; + wl.insert(); + + throw Ok("/member", u.getName() +" "+ t._("is now on waiting list.") ); + + + } + + /** + * Display waiting list + */ + @tpl('member/waiting.mtt') + function doWaiting(?args:{?add:db.User,?remove:db.User}){ + + if (args != null){ + if (args.add != null){ + + service.WaitingListService.approveRequest(args.add,app.user.amap); + throw Ok("/member/waiting", t._("Membership request accepted") ); + + }else if (args.remove != null){ + + service.WaitingListService.cancelRequest(args.remove,app.user.amap); + throw Ok("/member/waiting", t._("Membership request refused") ); + + } + } + + view.waitingList = db.WaitingList.manager.search($group == app.user.amap,{orderBy:-date}); + } + + /** + * Send an invitation to a new member + */ + function doInviteMember(u:db.User){ + + if (checkToken() ) { + u.sendInvitation(app.user.amap); + throw Ok('/member/view/'+u.id, t._("Invitation sent.") ); + } + + } + + /** + * Invite 'never logged' users + */ + function doInvite() { + + if (checkToken()) { + + var users = db.User.getUsers_NewUsers(); + try{ + for ( u in users) { + u.sendInvitation(app.user.amap); + Sys.sleep(0.2); + } + }catch (e:String){ + if (e.indexOf("curl") >-1) { + App.current.logError(e, haxe.CallStack.toString(haxe.CallStack.exceptionStack())); + throw Error("/member", t._("An error occurred while sending emails, please retry")); + } + } + + throw Ok('/member', t._("Congratulations, you just sent ::userLength:: invitations", {userLength:users.length})); + } + + } + + + @tpl("member/view.mtt") + function doView(member:db.User) { + + view.member = member; + var userAmap = db.UserAmap.get(member, app.user.amap); + if (userAmap == null) throw Error("/member", t._("This person does not belong to your group")); + + view.userAmap = userAmap; + view.canLoginAs = (db.UserAmap.manager.count($userId == member.id) == 1 && app.user.isAmapManager()) || app.user.isAdmin(); + + //orders + var row = { constOrders:new Array(), varOrders:new Map>() }; + + //commandes fixes + var contracts = db.Contract.manager.search($type == db.Contract.TYPE_CONSTORDERS && $amap == app.user.amap && $endDate > DateTools.delta(Date.now(),-1000.0*60*60*24*30), false); + var orders = member.getOrdersFromContracts(contracts); + row.constOrders = service.OrderService.prepare(orders); + + //commandes variables groupées par date de distrib + var contracts = db.Contract.manager.search($type == db.Contract.TYPE_VARORDER && $amap == app.user.amap && $endDate > DateTools.delta(Date.now(),-1000.0*60*60*24*30), false); + var distribs = new Map>(); + for (c in contracts) { + var ds = c.getDistribs(); + for (d in ds) { + var k = d.date.toString().substr(0, 10); + var orders = member.getOrdersFromDistrib(d); + if (orders.length > 0) { + if (!distribs.exists(k)) { + distribs.set(k, orders); + }else { + + var v = distribs.get(k); + for ( o in orders ) v.add(o); + distribs.set(k, v); + } + } + } + } + for ( k in distribs.keys()){ + var d = distribs.get(k); + var d2 = service.OrderService.prepare(d); + row.varOrders.set(k,d2); + } + + + view.userContracts = row; + checkToken(); //to insert a token in tpl + + } + + /** + * Admin : Log in as this user for debugging purpose + * @param user + * @param amap + */ + function doLoginas(member:db.User, amap:db.Amap) { + + if (!app.user.isAdmin()){ + if (!app.user.isAmapManager()) return; + if (member.isAdmin()) return; + if ( db.UserAmap.manager.count($userId == member.id) > 1 ) return; + + } + + App.current.session.setUser(member); + App.current.session.data.amapId = amap.id; + throw Redirect("/member/view/" + member.id ); + } + + @tpl('member/lastMessages.mtt') + function doLastMessages(member:db.User){ + + var out = new Array<{date:Date,subject:String,success:String,failure:String}>(); + var threeMonth = DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 30.5 * 3); + + for ( m in sugoi.db.BufferedMail.manager.search($remoteId == app.user.amap.id && $cdate > threeMonth, {limit:10, orderBy:-cdate})){ + + var status : sugoi.mail.IMailer.MailerResult = m.status; + + if ( status!=null && status.get(member.email)!=null ){ + + var r = m.getMailerResultMessage(member.email); + out.push( {date:m.cdate,subject:m.title,success:r.success,failure:r.failure} ); + } + + } + view.emails = out; + } + + /** + * Edit a Member + */ + @tpl('form.mtt') + function doEdit(member:db.User) { + + if (member.isAdmin() && !app.user.isAdmin()) throw Error("/", t._("You cannot modify the account of an administrator")); + + var form = sugoi.form.Form.fromSpod(member); + + //cleaning + form.removeElement( form.getElement("rights") ); + form.removeElement( form.getElement("lang") ); + form.removeElement( form.getElement("ldate") ); + form.removeElement( form.getElement("apiKey") ); + + + var isReg = member.isFullyRegistred(); + var groupNum = db.UserAmap.manager.count($userId == member.id); + + //an administrator can modify a user's email only if he's not member elsewhere + if (groupNum > 1){ + form.removeElementByName("email"); + form.removeElementByName("email2"); + app.session.addMessage(t._("For security reasons, you cannot modify the e-mail of this person because this person is a member of more than 1 group.")); + } + + //an administrator can modify a user's pass only if he's a not registred user. + if (!isReg){ + app.session.addMessage(t._("This person did not define yet a password. You are exceptionaly authorized to do it. Please don't forget to tell this person.")); + form.getElement("pass").required = false; + }else{ + form.removeElement( form.getElement("pass") ); + } + + if (form.checkToken()) { + + if (app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && form.getValueOf("phone") == null ){ + throw Error("/member/edit/"+member.id, t._("Phone number is required in this group.")); + } + + form.toSpod(member); + + //check that the given emails are not already used elsewhere + var sim = db.User.getSameEmail(member.email,member.email2); + for ( s in sim) { + if (s.id == member.id) sim.remove(s); + } + if (sim.length > 0) { + + //Let's merge the 2 users if it has no orders. + var id = sim.first().id; + if (db.UserContract.manager.search( $userId == id || $userId2 == id , false).length == 0) { + //merge + member.merge( sim.first() ); + app.session.addMessage(t._("This e-mail was used by another user account. As this user account was not used, it has been merged into the current user account.")); + + } else { + var str = t._("Warning, this e-mail or this name already exists for another account : "); + str += Lambda.map(sim, function(u) return "" + u.getCoupleName() + "").join(","); + str += " "+t._("These accounts can't be merged because the second account has orders"); + throw Error("/member/edit/" + member.id, str); + } + } + + if (!isReg) member.setPass(form.getValueOf("pass")); + + member.update(); + + if (!App.config.DEBUG && groupNum == 1) { + + //warn the user that his email has been updated + if (form.getValueOf("email") != member.email) { + var m = new sugoi.mail.Mail(); + m.setSender(App.config.get("default_email"), t._("Cagette.net")); + m.addRecipient(member.email); + m.setSubject(t._("Change your e-mail in your account Cagette.net")); + m.setHtmlBody( app.processTemplate("mail/message.mtt", { text:app.user.getName() + t._(" just modified your e-mail in your account Cagette.net.
    Your e-mail is now:")+form.getValueOf("email") } ) ); + App.sendMail(m); + + } + if (form.getValueOf("email2") != member.email2 && member.email2!=null) { + var m = new sugoi.mail.Mail(); + m.setSender(App.config.get("default_email"),"Cagette.net"); + m.addRecipient(member.email2); + m.setSubject(t._("Change the e-mail of your account Cagette.net")); + m.setHtmlBody( app.processTemplate("mail/message.mtt", { text:app.user.getName() +t._(" just modified your e-mail in your account Cagette.net.
    Your e-mail is now:")+form.getValueOf("email2") } ) ); + App.sendMail(m); + } + } + + throw Ok('/member/view/'+member.id, t._("This member has beed updated")); + } + + view.form = form; + + } + + /** + * Remove a user from this group + */ + function doDelete(user:db.User,?args:{confirm:Bool,token:String}) { + + if (checkToken()) { + if (!app.user.canAccessMembership()) throw t._("You cannot do that."); + if (user.id == app.user.id) throw Error("/member/view/" + user.id, t._("You cannot delete yourself.")); + if ( Lambda.count(user.getOrders(app.user.amap),function(x) return x.quantity>0) > 0 && !args.confirm) { + throw Error("/member/view/"+user.id, t._("Warning, this account has orders. Remove anyway", {userid:user.id, argstoken:args.token})); + } + + var ua = db.UserAmap.get(user, app.user.amap, true); + if (ua != null) { + ua.delete(); + throw Ok("/member", t._("::user:: has been removed from your group",{user:user.getName()})); + }else { + throw Error("/member", t._("This person does not belong to \"::amapname::\"", {amapname:app.user.amap.name})); + } + }else { + throw Redirect("/member/view/"+user.id); + } + } + + @tpl('form.mtt') + function doMerge(user:db.User) { + + if (!app.user.canAccessMembership()) throw Error("/","Action interdite"); + + view.title = t._("Merge an account with another one"); + view.text = t._("This action allows you to merge two accounts (when you have duplicates in the database for example).
    Contracts of account 2 will be moved to account 1, and account 2 will be deleted. Warning, it is not possible to cancel this action."); + + var form = new Form("merge"); + + var members = app.user.amap.getMembers(); + var members = Lambda.array(Lambda.map(members, function(x) return { key:Std.string(x.id), value:x.getName() } )); + var mlist = new Selectbox("member1", t._("Account 1"), members, Std.string(user.id)); + form.addElement( mlist ); + var mlist = new Selectbox("member2", t._("Account 2"), members); + form.addElement( mlist ); + + if (form.checkToken()) { + + var m1 = Std.parseInt(form.getElement("member1").value); + var m2 = Std.parseInt(form.getElement("member2").value); + var m1 = db.User.manager.get(m1,true); + var m2 = db.User.manager.get(m2,true); + + //if (m1.amapId != m2.amapId) throw "ils ne sont pas de la même amap !"; + + //on prend tout à m2 pour donner à m1 + //change usercontracts + var contracts = db.UserContract.manager.search($user==m2 || $user2==m2,true); + for (c in contracts) { + if (c.user.id == m2.id) c.user = m1; + if (c.user2!=null && c.user2.id == m2.id) c.user2 = m1; + c.update(); + } + + //group memberships + var adh = db.UserAmap.manager.search($user == m2, true); + for ( a in adh) { + a.user = m1; + a.update(); + } + + //change contacts + var contacts = db.Contract.manager.search($contact==m2,true); + for (c in contacts) { + c.contact = m1; + c.update(); + } + //if (m2.amap.contact == m2) { + //m1.amap.lock(); + //m1.amap.contact = m1; + //m1.amap.update(); + //} + + m2.delete(); + + throw Ok("/member/view/" + m1.id, t._("Both accounts have been merged")); + + + } + + view.form = form; + + } + + + @tpl('member/import.mtt') + function doImport(?args: { confirm:Bool } ) { + + var step = 1; + var request = Utils.getMultipart(1024 * 1024 * 4); //4mb + + //on recupere le contenu de l'upload + var data = request.get("file"); + if ( data != null) { + + var csv = new sugoi.tools.Csv(); + csv.setHeaders([t._("Firstname"), t._("Lastname"), t._("E-mail"), t._("Mobile phone"), t._("Partner's firstname"), t._("Partner's lastname"), t._("Partner's e-mail"), t._("Partner's Mobile phone"), t._("Address 1"), t._("Address 2"), t._("Post code"), t._("City")]); + + //utf8 encode if needed + try{ + if (!haxe.Utf8.validate(data)){ + data = haxe.Utf8.encode(data); + } + }catch (e:Dynamic){ } + var unregistred = csv.importDatas(data); + + /*var checkEmail = function(email){ + if ( !sugoi.form.validators.EmailValidator.check(email) ) { + throw Error("/member", t._("The email ::email:: is invalid, please update your CSV file",{email:email}) ); + } + }*/ + + //cleaning + for ( user in unregistred.copy() ) { + + //check nom+prenom + if (user[0] == null || user[1] == null) { + throw Error("/member/import", t._("You must fill the name and the firstname of the person. This line is incomplete: ") + user); + } + if (user[2] == null) { + throw Error("/member/import", t._("Each person must have an e-mail to be able to log in. ::user0:: ::user1:: don't have one. ", {user0:user[0], user1:user[1]}) +user); + } + //uppercase du nom + if (user[1] != null) user[1] = user[1].toUpperCase(); + if (user[5] != null) user[5] = user[5].toUpperCase(); + //lowercase email + if (user[2] != null){ + user[2] = user[2].toLowerCase(); + //checkEmail(user[2]); + } + if (user[6] != null){ + user[6] = user[6].toLowerCase(); + //checkEmail(user[6]); + } + } + + //utf-8 check + for ( row in unregistred.copy()) { + + for ( i in 0...row.length) { + var t = row[i]; + if (t != "" && t != null) { + try{ + if (!Utf8.validate(t)) { + t = Utf8.encode(t); + } + }catch (e:Dynamic) {} + row[i] = t; + } + } + } + + //put already registered people in another list + var registred = []; + for (r in unregistred.copy()) { + //var firstName = r[0]; + //var lastName = r[1]; + var email = r[2]; + + //var firstName2 = r[4]; + //var lastName2 = r[5]; + var email2 = r[6]; + + var us = db.User.getSameEmail(email, email2); + + if (us.length > 0) { + unregistred.remove(r); + registred.push(r); + } + } + + + app.session.data.csvUnregistered = unregistred; + app.session.data.csvRegistered = registred; + + view.data = unregistred; + view.data2 = registred; + step = 2; + } + + + if (args != null && args.confirm) { + + //import unregistered members + var i : Iterable = cast app.session.data.csvUnregistered; + for (u in i) { + if (u[0] == null || u[0] == "null" || u[0] == "") continue; + + var user = new db.User(); + user.firstName = u[0]; + user.lastName = u[1]; + user.email = u[2]; + if (user.email != null && user.email != "null" &&!EmailValidator.check(user.email)) { + throw t._("The E-mail ::useremail:: is invalid, please modify your file", {useremail:user.email}); + } + user.phone = u[3]; + + user.firstName2 = u[4]; + user.lastName2 = u[5]; + user.email2 = u[6]; + if (user.email2 != null && user.email2 != "null" && !EmailValidator.check(user.email2)) { + App.log(u); + throw t._("The E-mail of the partner of ::userFirstName:: ::userLastName:: '::userEmail::' is invalid, please check your file", {userFirstName:user.firstName, userLastName:user.lastName, userEmail:user.email2}); + } + user.phone2 = u[7]; + user.address1 = u[8]; + user.address2 = u[9]; + user.zipCode = u[10]; + user.city = u[11]; + user.insert(); + + var ua = new db.UserAmap(); + ua.user = user; + ua.amap = app.user.amap; + ua.insert(); + } + + //import registered members + var i : Iterable> = cast app.session.data.csvRegistered; + for (u in i) { + var email = u[2]; + var email2 = u[6]; + + var us = db.User.getSameEmail(email, email2); + var userAmaps = db.UserAmap.manager.search($amap == app.user.amap && $userId in Lambda.map(us, function(u) return u.id), false); + + //member exists but is not member of this group. + if (userAmaps.length == 0) { + var ua = new db.UserAmap(); + ua.user = us.first(); + ua.amap = app.user.amap; + ua.insert(); + } + } + + view.numImported = app.session.data.csvUnregistered.length + app.session.data.csvRegistered.length; + app.session.data.csvUnregistered = null; + app.session.data.csvRegistered = null; + + step = 3; + } + + if (step == 1) { + //reset import when back to import page + app.session.data.csvUnregistered = null; + app.session.data.csvRegistered = null; + } + + view.step = step; + } + + @tpl("user/insert.mtt") + public function doInsert() { + + if (!app.user.canAccessMembership()) throw Error("/", t._("Forbidden action")); + + var m = new db.User(); + var form = sugoi.form.Form.fromSpod(m); + form.removeElement(form.getElement("lang")); + form.removeElement(form.getElement("rights")); + form.removeElement(form.getElement("pass")); + form.removeElement(form.getElement("ldate") ); + form.removeElement( form.getElement("apiKey") ); + form.addElement(new sugoi.form.elements.Checkbox("warnAmapManager", t._("Send an E-mail to the person in charge of the group"), true)); + form.getElement("email").addValidator(new EmailValidator()); + form.getElement("email2").addValidator(new EmailValidator()); + + if (form.isValid()) { + + //check doublon de User et de UserAmap + var userSims = db.User.getSameEmail(form.getValueOf("email"),form.getValueOf("email2")); + view.userSims = userSims; + var userAmaps = db.UserAmap.manager.search($amap == app.user.amap && $userId in Lambda.map(userSims, function(u) return u.id), false); + view.userAmaps = userAmaps; + + if (userAmaps.length > 0) { + //user deja enregistré dans cette amap + throw Error('/member/view/' + userAmaps.first().user.id, t._("This person is already member of this group")); + + }else if (userSims.length > 0) { + //des users existent avec ce nom , + //if (userSims.length == 1) { + // si yen a qu'un on l'inserte + var ua = new db.UserAmap(); + ua.user = userSims.first(); + ua.amap = app.user.amap; + ua.insert(); + throw Ok('/member/', t._("This person already had an account on Cagette.net, and is now member of your group.")); + /*}else { + //demander validation avant d'inserer le userAmap + //TODO + throw Error('/member', t._("Not possible to add this person because there are already some people in the database having the same firstname and name. Please contact the administrator.")+userSims); + }*/ + return; + }else { + + if (app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && form.getValueOf("phone") == null ){ + throw Error("/member/insert", t._("Phone number is required in this group.")); + } + + //insert user + var u = new db.User(); + form.toSpod(u); + u.lang = app.user.lang; + u.insert(); + + //insert userAmap + var ua = new db.UserAmap(); + ua.user = u; + ua.amap = app.user.getAmap(); + ua.insert(); + + if (form.getValueOf("warnAmapManager") == "1") { + var url = "http://" + App.config.HOST + "/member/view/" + u.id; + var text = t._("::admin:: just keyed-in contact details of a new member:
    ::newMember::
    See contact details",{admin:app.user.getName(),newMember:u.getCoupleName(),url:url}); + App.quickMail( + app.user.getAmap().contact.email, + app.user.amap.name +" - "+ t._("New member") + " : " + u.getCoupleName(), + app.processTemplate("mail/message.mtt", { text:text } ) + ); + } + + throw Ok('/member/', t._("This person is now member of the group")); + + } + } + + view.form = form; + } + + + /** + * user payments history + */ + @tpl('member/payments.mtt') + function doPayments(m:db.User){ + + service.PaymentService.updateUserBalance(m, app.user.amap); + var browse:Int->Int->List; + + //default display + browse = function(index:Int, limit:Int) { + return db.Operation.getOperationsWithIndex(m,app.user.amap,index,limit,true); + } + + var count = db.Operation.countOperations(m,app.user.amap); + var rb = new sugoi.tools.ResultsBrowser(count, 10, browse); + view.rb = rb; + view.member = m; + view.balance = db.UserAmap.get(m, app.user.amap).balance; + + checkToken(); + } + + @tpl('member/balance.mtt') + function doBalance(){ + view.balanced = db.UserAmap.manager.search($amap == app.user.amap && $balance == 0.0, false); + view.credit = db.UserAmap.manager.search($amap == app.user.amap && $balance > 0, false); + view.debt = db.UserAmap.manager.search($amap == app.user.amap && $balance < 0, false); + } + +} \ No newline at end of file diff --git a/src/controller/Membership.hx b/src/controller/Membership.hx new file mode 100755 index 0000000..c7bb938 --- /dev/null +++ b/src/controller/Membership.hx @@ -0,0 +1,73 @@ +package controller; +import sugoi.form.elements.IntSelect; +using Std; +/** + * Membership management + * + * @author fbarbut + */ +class Membership extends controller.Controller +{ + + @tpl("membership/default.mtt") + function doDefault(member:db.User) { + var userAmap = db.UserAmap.get(member, app.user.amap,true); + if (userAmap == null) throw Error("/member", t._("This person is not a member of your group")); + + //formulaire + var f = new sugoi.form.Form("membership"); + var year = Date.now().getFullYear(); + var data = []; + var now = Date.now(); + for ( x in 0...5) { + + var y = now.getFullYear() - x; + var yy = DateTools.delta(now, DateTools.days(365) * -x); + data.push({label:app.user.amap.getPeriodName(yy),value:app.user.amap.getMembershipYear(yy)}); + } + f.addElement(new IntSelect("year", t._("Period"), data,app.user.amap.getMembershipYear(),true)); + f.addElement(new sugoi.form.elements.DateDropdowns("date", t._("Date of payment of subscription"), null, true)); + if (f.isValid()) { + var y : Int = f.getValueOf("year"); + + if (db.Membership.get(member, app.user.amap, y) != null) throw Error("/membership/"+member.id, t._("This subscription has been already keyed-in")); + + var cotis = new db.Membership(); + cotis.amap = app.user.amap; + cotis.user = member; + cotis.year = y; + cotis.date = f.getElement("date").value; + cotis.insert(); + throw Ok("/membership/"+member.id, t._("Subscription saved")); + } + + //années de cotisation + var memberships = db.Membership.manager.search($user == member && $amap == app.user.amap,{orderBy:-year}, false); + //for ( m in memberships) { + //Reflect.setField(m, 'yearDate', new Date(m.year, 1, 1, 1, 1, 1)); + //} + view.memberships = memberships; + + //view + view.form = f; + view.member = member; + checkToken(); + } + + + public function doDelete(member:db.User, year:Int,?args:{token:String}) { + + if (checkToken()) { + var cotis = db.Membership.get(member, app.user.amap, year, true); + if (cotis == null) throw Error("/", t._("This subscription does not exist")); + + cotis.delete(); + throw Ok("/membership/" + member.id, t._("Subscription deleted")); + }else { + throw Error("/", "Bad Token"); + } + + } + + +} \ No newline at end of file diff --git a/src/controller/Messages.hx b/src/controller/Messages.hx new file mode 100755 index 0000000..cff5eae --- /dev/null +++ b/src/controller/Messages.hx @@ -0,0 +1,211 @@ +package controller; +import db.Message; +import db.UserContract; +import sugoi.form.ListData; +import sugoi.form.elements.*; +import sugoi.form.Form; + +class Messages extends Controller +{ + + public function new() + { + super(); + if (!app.user.canAccessMessages()) throw Redirect("/"); + } + + @tpl("messages/default.mtt") + function doDefault() { + + var form = new Form("msg"); + + var senderName = ""; + var senderMail = ""; + + if (App.current.session.data.whichUser == 1 && app.user.email2 != null) { + senderMail = app.user.email2; + senderName = app.user.firstName2 + " " + app.user.lastName2; + + }else { + senderMail = app.user.email; + senderName = app.user.firstName + " " + app.user.lastName; + } + + var lists = getLists(); + form.addElement( new StringInput("senderName", t._("Sender name"),senderName,true)); + form.addElement( new StringInput("senderMail", t._("Sender E-Mail"),senderMail,true)); + form.addElement( new StringSelect("list", t._("Recipients"),lists,"1", true,null,"style='width:500px;'")); + form.addElement( new StringInput("subject", t._("Subject:"),"",false,null,"style='width:500px;'") ); + form.addElement( new TextArea("text", t._("Message:"), "", false, null, "style='width:500px;height:350px;'") ); + + if (form.checkToken()) { + + var listId = form.getElement("list").value; + var dest = getSelection(listId); + var mails = []; + for ( d in dest) { + if (d.email != null) mails.push(d.email); + if (d.email2 != null) mails.push(d.email2); + } + + //send mail confirmation link + var e = new sugoi.mail.Mail(); + e.setSubject(form.getValueOf("subject")); + for ( x in mails) e.addRecipient(x); + + e.setSender(App.config.get("default_email"),form.getValueOf("senderName")); + e.setReplyTo(form.getValueOf("senderMail"),form.getValueOf("senderName")); + //sender : default email ( explicitly tells that the server send an email on behalf of the user ) + //e.setHeader("Sender", App.config.get("default_email")); + var text :String = form.getValueOf("text"); + var html = app.processTemplate("mail/message.mtt", { text:text,group:app.user.amap,list:getListName(listId) }); + e.setHtmlBody(html); + + App.sendMail(e,app.user.getAmap(),listId,app.user); + + //store message + var lm = new db.Message(); + lm.amap = app.user.amap; + lm.recipients = Lambda.array(Lambda.map(e.getRecipients(), function(x) return x.email)); + lm.title = e.getSubject(); + lm.date = Date.now(); + lm.body = e.getHtmlBody(); + if (listId != null) lm.recipientListId = listId; + lm.sender = app.user; + lm.insert(); + + + throw Ok("/messages", t._("The message has been sent")); + } + + view.form = form; + + if (app.user.isAmapManager()) { + view.sentMessages = Message.manager.search($amap == app.user.amap && $recipientListId!=null, {orderBy:-date,limit:20}, false); + }else { + view.sentMessages = Message.manager.search($sender == app.user && $recipientListId!=null && $amap == app.user.amap , {orderBy:-date,limit:20}, false); + } + + } + + @tpl("messages/message.mtt") + public function doMessage(msg:Message) { + + if (!app.user.isAmapManager() && msg.sender.id != app.user.id) throw Error("/", t._("Non authorized access")); + + view.list = getListName(msg.recipientListId); + view.msg = msg; + + //make status easier to display + var s = new Array<{email:String,success:String,failure:String}>(); + /*if (msg.status != null){ + for ( k in msg.status.keys()) { + var r = msg.getMailerResultMessage(k); + s.push({email:k,success:r.success,failure:r.failure}); + } + }*/ + + view.status = s; + + } + + function getLists() :FormData{ + var out = [ + {value:'1', label: t._("Everyone")}, + {value:'2', label: t._("The board: persons in charge + contracts + memberships")}, + ]; + + out.push( { value:'3', label: t._("TEST: me + spouse") } ); + out.push( { value:'4', label: t._("Members without contract/order") } ); + if(app.user.amap.hasMembership()) out.push( { value:'5', label:t._("Memberships to be renewed")} ); + + + var contracts = db.Contract.getActiveContracts(app.user.amap,true); + for ( c in contracts) { + var label = t._("Subscribers") + " " + c.toString(); + out.push({value:'c'+c.id,label:label}); + } + return out ; + + } + + /** + * get list name from id + * @param listId + */ + function getListName(listId:String) { + var l = getLists(); + + for (ll in l) { + if (ll.value == listId) return ll.label; + } + + return null; + + } + + function getSelection(listId:String) { + if (listId.substr(0, 1) == "c") { + //contrats + var contract = Std.parseInt(listId.substr(1)); + + var pids = db.Product.manager.search($contractId == contract, false); + var pids = Lambda.map(pids, function(x) return x.id); + var up = db.UserContract.manager.search($productId in pids, false); + + + var users = []; + for ( order in up) { + if (!Lambda.has(users, order.user)) { + users.push(order.user); + } + if (order.user2 != null && !Lambda.has(users, order.user2)) { + users.push(order.user2); + } + } + return users; + + }else { + var out = []; + switch(listId) { + case "1": + //tout le monde + out = Lambda.array(app.user.amap.getMembers()); + + case "2": + var users = []; + users.push(app.user.amap.contact); + for ( c in db.Contract.manager.search($amap == app.user.amap)) { + if (!Lambda.has(users, c.contact)) { + users.push(c.contact); + } + } + + //ajouter les autres personnes ayant les droits Admin ou Gestion Adhérents ou Gestion Contrats + for (ua in Lambda.array(db.UserAmap.manager.search($rights != null && $amap == app.user.amap, false))) { + if (ua.hasRight(GroupAdmin) || ua.hasRight(Membership) || ua.hasRight(ContractAdmin())) { + if (!Lambda.has(users, ua.user)) users.push(ua.user); + } + } + + out = users; + + case "3": + //moi + return [app.user]; + case "4": + return Lambda.array(db.User.getUsers_NoContracts()); + case "5": + return Lambda.array(db.User.getUsers_NoMembership()); + } + + return out; + + } + } + + + + + +} \ No newline at end of file diff --git a/src/controller/Place.hx b/src/controller/Place.hx new file mode 100755 index 0000000..91b61ff --- /dev/null +++ b/src/controller/Place.hx @@ -0,0 +1,87 @@ +package controller; +import sugoi.form.Form; + +/** + * Place controller + */ +class Place extends Controller +{ + + public function new() + { + super(); + } + + @tpl('place/view.mtt') + function doView(place:db.Place) { + view.place = place; + + //build adress for google maps + var addr = ""; + if (place.address1 != null) addr += place.address1; + if (place.address2 != null) addr += ", " + place.address2; + if (place.zipCode != null) addr += " " + place.zipCode; + if (place.city != null) addr += " " + place.city; + + view.addr = view.escapeJS(addr); + } + + @tpl('form.mtt') + function doEdit(p:db.Place) { + + var currentAddress = p.getAddress(); + + var f = sugoi.form.Form.fromSpod(p); + + if (f.isValid()) { + + f.toSpod(p); + + if(currentAddress!=p.getAddress() || p.lat==null){ + try{ + service.PlaceService.geocode(p); + }catch(e:Dynamic){ + App.current.session.addMessage(t._("Oops, we're unable to find where is located this address. This place will not be shown on the map.")+'
    $e',true); + } + } + + p.amap = app.user.amap; + p.update(); + throw Ok('/contractAdmin',t._("this place has been updated")); + } + + view.form = f; + view.title = t._("Edit a place"); + } + + @tpl("form.mtt") + public function doInsert() { + + var d = new db.Place(); + var f = sugoi.form.Form.fromSpod(d); + + if (f.isValid()) { + f.toSpod(d); + d.amap = app.user.amap; + d.insert(); + throw Ok('/contractAdmin',t._("The place has been registred") ); + } + + view.form = f; + view.title = t._("Register a new delivery place"); + } + + public function doDelete(p:db.Place) { + if (!app.user.isAmapManager()) throw "forbidden"; + if (checkToken()) { + + if (db.Distribution.manager.search($placeId == p.id).length > 0) + throw Error('/contractAdmin', t._("You can't delete this place because one or more distributions are linked to this place.") ); + + p.lock(); + p.delete(); + throw Ok("/contractAdmin", t._("Place deleted") ); + } + + } +} \ No newline at end of file diff --git a/src/controller/Plugin.hx b/src/controller/Plugin.hx new file mode 100755 index 0000000..1cfc2f1 --- /dev/null +++ b/src/controller/Plugin.hx @@ -0,0 +1,39 @@ +package controller; + +/** + * ... + * @author fbarbut + */ +class Plugin extends sugoi.BaseController +{ + + public function new() + { + super(); + } + + #if plugins + + //cagette-hosted + public function doHosted(d:haxe.web.Dispatch) { + d.dispatch(new hosted.controller.Main()); + } + + //cagette-pro + public function doPro(d:haxe.web.Dispatch) { + d.dispatch(new pro.controller.Main()); + } + + //cagette-connector + public function doConnector(d:haxe.web.Dispatch) { + d.dispatch(new connector.controller.Main()); + } + + //cagette-wholesale-order + public function doWho(d:haxe.web.Dispatch) { + d.dispatch(new who.controller.Main()); + } + + #end + +} \ No newline at end of file diff --git a/src/controller/Product.hx b/src/controller/Product.hx new file mode 100755 index 0000000..911e4d8 --- /dev/null +++ b/src/controller/Product.hx @@ -0,0 +1,339 @@ +package controller; +import sugoi.form.Form; +import Common; +import sugoi.form.ListData.FormData; +import sugoi.form.elements.FloatInput; +import sugoi.form.elements.FloatSelect; +import sugoi.form.elements.IntSelect; +using Std; + +class Product extends Controller +{ + public function new() + { + super(); + view.nav = ["contractadmin","products"]; + } + + @tpl('form.mtt') + function doEdit(d:db.Product) { + + if (!app.user.canManageContract(d.contract)) throw t._("Forbidden access"); + + var f = sugoi.form.Form.fromSpod(d); + + //stock mgmt ? + if (!d.contract.hasStockManagement()) f.removeElementByName('stock'); + + //VAT selector + f.removeElement( f.getElement('vat') ); + var data :FormData = []; + for (k in app.user.amap.vatRates.keys()) { + data.push( { label:k, value:app.user.amap.vatRates[k] } ); + } + f.addElement( new FloatSelect("vat", "TVA", data, d.vat ) ); + + f.removeElementByName("contractId"); + + //Product Taxonomy: + //view.taxo = db.TxpProduct.manager.all(); + //f.addElement(new form.TxpProduct("txpProduct", "taxo",null,false) ); + var txId = d.txpProduct == null ? "" : Std.string(d.txpProduct.id); + var html = '
    '; + f.addElement(new sugoi.form.elements.Html("html",html, 'Nom'),1); + + if (f.isValid()) { + + f.toSpod(d); + app.event(EditProduct(d)); + d.update(); + throw Ok('/contractAdmin/products/'+d.contract.id, t._("The product has been updated")); + }else{ + app.event(PreEditProduct(d)); + } + + view.form = f; + view.title = t._("Modify a product"); + } + + @tpl("form.mtt") + public function doInsert(contract:db.Contract ) { + + if (!app.user.isContractManager(contract)) throw Error("/", t._("Forbidden action")); + + var d = new db.Product(); + var f = sugoi.form.Form.fromSpod(d); + + f.removeElementByName("contractId"); + + //stock mgmt ? + if (!contract.hasStockManagement()) f.removeElementByName('stock'); + + //vat selector + f.removeElement( f.getElement('vat') ); + var data = []; + for (k in app.user.amap.vatRates.keys()) { + data.push( { value:app.user.amap.vatRates[k], label:k } ); + } + f.addElement( new FloatSelect("vat", "TVA", data, d.vat ) ); + + var formName = f.name; + var html = '
    '; + f.addElement(new sugoi.form.elements.Html("html",html, 'Nom'),1); + + if (f.isValid()) { + f.toSpod(d); + d.contract = contract; + app.event(NewProduct(d)); + d.insert(); + throw Ok('/contractAdmin/products/'+d.contract.id, t._("The product has been saved")); + }else{ + app.event(PreNewProduct(contract)); + } + + view.form = f; + view.title = t._("Key-in a new product"); + } + + public function doDelete(p:db.Product) { + + if (!app.user.canManageContract(p.contract)) throw t._("Forbidden access"); + + if (checkToken()) { + + app.event(DeleteProduct(p)); + + var orders = db.UserContract.manager.search($productId == p.id, false); + if (orders.length > 0) { + throw Error("/contractAdmin", t._("Not possible to delete this product because some orders are referencing it")); + } + var cid = p.contract.id; + p.lock(); + p.delete(); + + throw Ok("/contractAdmin/products/"+cid, t._("Product deleted")); + } + throw Error("/contractAdmin", t._("Token error")); + } + + + @tpl('product/import.mtt') + function doImport(c:db.Contract, ?args: { confirm:Bool } ) { + + if (!app.user.canManageContract(c)) throw t._("Forbidden access"); + + var csv = new sugoi.tools.Csv(); + csv.step = 1; + var request = sugoi.tools.Utils.getMultipart(1024 * 1024 * 4); + csv.setHeaders( ["productName","price","ref","desc","qt","unit","organic","floatQt","vat","stock"] ); + view.contract = c; + + // get the uploaded file content + if (request.get("file") != null) { + //convert to utf-8 if needed + var csvData = request.get("file"); + try{ + if (!haxe.Utf8.validate(csvData)){ + csvData = haxe.Utf8.encode(csvData); + } + }catch (e:Dynamic){ } + var datas = csv.importDatasAsMap(csvData); + + app.session.data.csvImportedData = datas; + + csv.step = 2; + view.csv = csv; + } + + if (args != null && args.confirm) { + var i : Iterable> = cast app.session.data.csvImportedData; + var fv = new sugoi.form.filters.FloatFilter(); + + for (p in i) { + + if (p["productName"] != null){ + + var product = new db.Product(); + product.name = p["productName"]; + product.price = fv.filterString(p["price"]); + product.ref = p["ref"]; + product.desc = p["desc"]; + product.vat = fv.filterString(p["vat"]); + product.qt = fv.filterString(p["qt"]); + if(p["unit"]!=null){ + product.unitType = switch(p["unit"].toLowerCase()){ + case "kg" : Kilogram; + case "g" : Gram; + case "l" : Litre; + case "cl" : Centilitre; + case "litre" : Litre; + default : Piece; + } + } + if (p["stock"] != null) product.stock = fv.filterString(p["stock"]); + product.organic = p["organic"] != null; + product.hasFloatQt = p["floatQt"] != null; + + product.contract = c; + product.insert(); + } + + } + + view.numImported = app.session.data.csvImportedData.length; + app.session.data.csvImportedData = null; + + csv.step = 3; + } + + if (csv.step == 1) { + //reset import when back to import page + app.session.data.csvImportedData = null; + } + + view.step = csv.step; + } + + @tpl("product/categorize.mtt") + public function doCategorize(contract:db.Contract) { + + + if (!app.user.canManageContract(contract)) throw t._("Forbidden access"); + + if (db.CategoryGroup.get(app.user.amap).length == 0) throw Error("/contractAdmin", t._("You must first define categories before you can assign a category to a product")); + + //var form = new sugoi.form.Form("cat"); + // + //for ( g in db.CategoryGroup.get(app.user.amap)) { + //var data = []; + //for ( c in g.getCategories()) { + //data.push({key:Std.string(c.id),value:c.name}); + //} + //form.addElement(new sugoi.form.elements.Selectbox("cats"+g.id,g.name,data)); + //} + // + //view.form = form; + view.c = contract; + + } + + /** + * init du Tagger + * @param contract + */ + public function doCategorizeInit(contract:db.Contract) { + + if (!app.user.canManageContract(contract)) throw t._("Forbidden access"); + + var data : TaggerInfos = { + products:[], + categories:[] + } + + for (p in contract.getProducts()) { + + data.products.push({product:p.infos(),categories:Lambda.array(Lambda.map(p.getCategories(),function(x) return x.id))}); + } + + for (cg in db.CategoryGroup.get(app.user.amap)) { + + var x = { id:cg.id, categoryGroupName:cg.name, color:App.current.view.intToHex(db.CategoryGroup.COLORS[cg.color]),tags:[] }; + + for (t in cg.getCategories()) { + x.tags.push({id:t.id,name:t.name}); + } + data.categories.push(x); + + } + + Sys.print(haxe.Json.stringify(data)); + } + + public function doCategorizeSubmit(contract:db.Contract) { + + if (!app.user.canManageContract(contract)) throw t._("Forbidden access"); + + var data : TaggerInfos = haxe.Json.parse(app.params.get("data")); + + db.ProductCategory.manager.unsafeDelete("delete from ProductCategory where productId in (" + Lambda.map(contract.getProducts(), function(t) return t.id).join(",")+")"); + + for (p in data.products) { + for (t in p.categories) { + var x = new db.ProductCategory(); + x.category = db.Category.manager.get(t, false); + x.product = db.Product.manager.get(p.product.id,false); + x.insert(); + } + } + + Sys.print(t._("Modifications saved")); + } + + + @tpl('product/addimage.mtt') + function doAddImage(product:db.Product) { + + if (!app.user.canManageContract(product.contract)) throw t._("Forbidden access"); + + view.c = product.contract; + view.image = product.image; + + var request = sugoi.tools.Utils.getMultipart(1024 * 1024 * 12); //12Mb + + if (request.exists("image")) { + + //Image + var image = request.get("image"); + if (image != null && image.length > 0) { + var img : sugoi.db.File = null; + if ( Sys.systemName() == "Windows") { + img = sugoi.db.File.create(request.get("image"), request.get("image_filename")); + }else { + img = sugoi.tools.UploadedImage.resizeAndStore(request.get("image"), request.get("image_filename"), 400, 400); + } + + product.lock(); + + if (product.image != null) { + //efface ancienne + product.image.lock(); + product.image.delete(); + } + + product.image = img; + product.update(); + throw Ok('/product/addImage/'+product.id,'Image mise à jour'); + } + } + } + + + @tpl('product/compose.mtt') + function doCompose(){ + + } + + + function doGetTaxo(){ + + var out : TxpDictionnary = {products:new Map(), categories:new Map(), subCategories:new Map()}; + + for ( p in db.TxpProduct.manager.all()){ + out.products.set(p.id, {id:p.id, name:p.name, category:p.category.id, subCategory:p.subCategory.id}); + } + + for ( c in db.TxpCategory.manager.all()){ + out.categories.set(c.id, {id:c.id,name:c.name }); + } + + for ( c in db.TxpSubCategory.manager.all()){ + out.subCategories.set(c.id, {id:c.id,name:c.name }); + } + + Sys.print(haxe.Serializer.run(out)); + + } + + + +} \ No newline at end of file diff --git a/src/controller/Shop.hx b/src/controller/Shop.hx new file mode 100755 index 0000000..783f8fd --- /dev/null +++ b/src/controller/Shop.hx @@ -0,0 +1,264 @@ +package controller; +import Common; +import tools.ArrayTool; +import service.OrderService; + +class Shop extends Controller +{ + + var distribs : List; + var contracts : List; + + @tpl('shop/default.mtt') + public function doDefault(place:db.Place,date:Date) { + var products = getProducts(place,date); + view.products = products; + view.place = place; + view.date = date; + view.group = place.amap; + view.infos = ArrayTool.groupByDate(Lambda.array(distribs), "orderEndDate"); + + //message if phone is required + if(app.user!=null && app.user.amap.flags.has(db.Amap.AmapFlags.PhoneRequired) && app.user.phone==null){ + app.session.addMessage(t._("Members of this group should provide a phone number. Please click here to update your account."),true); + } + + //event for additionnal blocks on home page + var e = Blocks([], "shop"); + app.event(e); + view.blocks = e.getParameters()[0]; + } + + /** + * prints the full product list and current cart in JSON + */ + public function doInit(place:db.Place,date:Date) { + + //init order serverside if needed + var order :OrderInSession = app.session.data.order; + if ( order == null) { + app.session.data.order = order = cast {products:[]}; + } + + var products = []; + var categs = new Array<{name:String,pinned:Bool,categs:Array}>(); + + if (place.amap.flags.has(db.Amap.AmapFlags.ShopCategoriesFromTaxonomy)){ + + //TAXO CATEGORIES + products = getProducts(place, date, true); + }else{ + + //CUSTOM CATEGORIES + products = getProducts(place, date, false); + } + + categs = place.amap.getCategoryGroups(); + + //clean + for ( p in order.products){ + p.product = null; + } + Sys.print( haxe.Serializer.run( {products:products,categories:categs,order:order} ) ); + } + + + + /** + * Get the available products list + */ + private function getProducts(place,date,?categsFromTaxo=false):Array { + + contracts = db.Contract.getActiveContracts(app.getCurrentGroup()); + + for (c in Lambda.array(contracts)) { + //only varying orders + if (c.type != db.Contract.TYPE_VARORDER) { + contracts.remove(c); + } + + if (!c.isVisibleInShop()) { + contracts.remove(c); + } + + } + + view.contracts = contracts; + + var now = Date.now(); + var cids = Lambda.map(contracts, function(c) return c.id); + var d1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); + var d2 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59); + + //distribs open to orders, and where distribDate is in the date given as parameter + distribs = db.Distribution.manager.search(($contractId in cids) && $orderStartDate <= now && $orderEndDate >= now && $date > d1 && $end < d2 && $place == place, false); + var products = []; + for ( d in distribs){ + for (p in d.contract.getProducts(true)){ + products.push( p.infos(categsFromTaxo,null,d) ); + } + } + return products; + + /*var cids = Lambda.map(distribs, function(d) return d.contract.id); + var products = db.Product.manager.search(($contractId in cids) && $active==true, { orderBy:name }, false); + + return Lambda.array(Lambda.map(products, function(p) return p.infos(categsFromTaxo)));*/ + } + + /** + * Overlay window loaded by Ajax for product Infos + */ + @tpl('shop/productInfo.mtt') + public function doProductInfo(p:db.Product,?args:{distribution:db.Distribution}) { + var d = args!=null && args.distribution!=null ? args.distribution : null; + view.p = p.infos(null,null,d); + view.product = p; + view.vendor = p.contract.vendor; + } + + /** + * receive cart + */ + public function doSubmit() { + + var order : OrderInSession = haxe.Json.parse(app.params.get("data")); + app.session.data.order = order; + + } + + /** + * add a product to the cart + */ + public function doAdd(productId:Int, quantity:Int) { + + var order : OrderInSession = app.session.data.order; + if ( order == null) order = cast { products:[] }; + order.products.push( { productId:productId, quantity:quantity } ); + Sys.print( haxe.Json.stringify( {success:true} ) ); + + } + + /** + * remove a product from cart + */ + public function doRemove(pid:Int) { + + var order:OrderInSession = app.session.data.order; + if ( order == null) return; + + for ( p in order.products.copy()) { + if (p.productId == pid) { + order.products.remove(p); + } + } + + Sys.print( haxe.Json.stringify( { success:true } ) ); + } + + + /** + * validate the order + */ + @tpl('shop/needLogin.mtt') + public function doValidate(place:db.Place, date:Date){ + + //login is needed : display a loginbox + if (app.user == null) { + view.redirect = "/shop/validate/" + place.id + "/" + date.toString().substr(0, 10); + view.group = place.amap; + view.register = true; + view.message = t._("In order to confirm your order, You need to authenticate."); + return; + } + + //add the user to this group if needed + if (place.amap.regOption == db.Amap.RegOption.Open && db.UserAmap.get(app.user, place.amap) == null){ + app.user.makeMemberOf(place.amap); + } + + var order : OrderInSession = app.session.data.order; + if (order == null || order.products == null || order.products.length == 0) { + throw Error("/", t._("Your order is empty") ); + } + + if (place == null) throw "place cannot be empty"; + if (date == null) throw "date cannot be empty"; + + var products = getProducts(place, date); + + var errors = []; + order.total = 0.0; + + //cleaning + for (o in order.products.copy()) { + + var p = db.Product.manager.get(o.productId, false); + + //check that the products are from this group (we never know...) + if (p.contract.amap.id != app.user.amap.id){ + app.session.data.order = null; + throw Error("/", t._("This cart is invalid") ); + } + + //check if the product is available + if (Lambda.find(products, function(x) return x.id == o.productId) == null) { + errors.push( t._("This distribution does not supply the product ::pname::",{pname:p.name}) ); + order.products.remove(o); + continue; + }else{ + o.product = p; + } + + //find distrib + var d = Lambda.find(distribs, function(d) return d.contract.id == p.contract.id); + if ( d == null ){ + errors.push( t._("This distribution does not supply the product ::pname::",{pname:p.name}) ); + order.products.remove(o); + continue; + }else{ + o.distributionId = d.id; + } + + //moderate order according available stocks + if (p.stock != null && p.contract.hasStockManagement() ) { + if (p.stock - o.quantity < 0) { + var canceled = o.quantity - p.stock; + o.quantity -= canceled; + errors.push(t._("Order of ::pname:: reduced to ::oquantity:: to match remaining stock", {pname:p.name, oquantity:o.quantity})); + } + } + + order.total += p.getPrice() * o.quantity; + } + + order.userId = app.user.id; + + if (errors.length > 0) { + app.session.addMessage(errors.join("
    "), true); + } + + //store cart in session + app.session.data.order = order; + + //debug + for ( p in order.products){ + if (p.distributionId == null){ + App.current.logError(place.amap.name + " : panier sans distrib Id : " + Std.string(order) ); + break; + } + } + + if (app.user.amap.hasPayments()){ + //Go to payments page + throw Redirect("/transaction/pay/"); + }else{ + //no payments, confirm direclty + OrderService.confirmSessionOrder(order); + throw Ok("/contract", t._("Your order has been confirmed") ); + } + + } + + +} diff --git a/src/controller/Stats.hx b/src/controller/Stats.hx new file mode 100755 index 0000000..ff96538 --- /dev/null +++ b/src/controller/Stats.hx @@ -0,0 +1,18 @@ +package controller; + +class Stats extends Controller +{ + + public function new() + { + super(); + } + + @tpl("stats/default.mtt") + function doDefault() { + + + } + + +} \ No newline at end of file diff --git a/src/controller/Transaction.hx b/src/controller/Transaction.hx new file mode 100644 index 0000000..95995e2 --- /dev/null +++ b/src/controller/Transaction.hx @@ -0,0 +1,294 @@ +package controller; +import db.Operation.OperationType; +import Common; +import service.OrderService; +using Lambda; + +/** + * Transction controller + * @author fbarbut + */ +class Transaction extends controller.Controller +{ + + /** + * A manager inserts manually a payment + */ + @tpl('form.mtt') + public function doInsertPayment(user:db.User){ + + if (!app.user.isContractManager()) throw Error("/", t._("Action forbidden")); + var t = sugoi.i18n.Locale.texts; + + var op = new db.Operation(); + op.user = user; + op.date = Date.now(); + + var f = new sugoi.form.Form("payement"); + f.addElement(new sugoi.form.elements.StringInput("name", t._("Label||label or name for a payment"), null, true)); + f.addElement(new sugoi.form.elements.FloatInput("amount", t._("Amount"), null, true)); + f.addElement(new sugoi.form.elements.DatePicker("date", t._("Date"), Date.now(), true)); + var paymentTypes = service.PaymentService.getPaymentTypesForManualEntry(app.user.amap); + f.addElement(new sugoi.form.elements.StringSelect("Mtype", t._("Payment type"), paymentTypes, null, true)); + + //related operation + var unpaid = db.Operation.manager.search($user == user && $group == app.user.amap && $type != Payment ,{limit:20,orderBy:-date}); + var data = unpaid.map(function(x) return {label:x.name, value:x.id}).array(); + f.addElement(new sugoi.form.elements.IntSelect("unpaid", t._("As a payment for :"), data, null, false)); + + if (f.isValid()){ + f.toSpod(op); + op.type = db.Operation.OperationType.Payment; + var data : db.Operation.PaymentInfos = {type:f.getValueOf("Mtype")}; + op.data = data; + op.group = app.user.amap; + op.user = user; + + if (f.getValueOf("unpaid") != null){ + var t2 = db.Operation.manager.get(f.getValueOf("unpaid")); + op.relation = t2; + if (t2.amount + op.amount == 0) { + op.pending = false; + t2.lock(); + t2.pending = false; + t2.update(); + } + } + + op.insert(); + + service.PaymentService.updateUserBalance(user, app.user.amap); + + throw Ok("/member/payments/" + user.id, t._("Payment recorded") ); + + } + + view.title = t._("Record a payment for ::user::",{user:user.getCoupleName()}) ; + view.form = f; + } + + + @tpl('form.mtt') + public function doEdit(op:db.Operation){ + + if (!app.user.canAccessMembership() || op.group.id != app.user.amap.id ) { + throw Error("/member/payments/" + op.user.id, t._("Action forbidden")); + } + + if (op.getPaymentType() == "lemonway-ec") throw Error("/member/payments/"+op.user.id, t._("Editing a credit card payment is not allowed")); + + op.lock(); + + var f = new sugoi.form.Form("payement"); + f.addElement(new sugoi.form.elements.StringInput("name", t._("Label||label or name for a payment"), op.name, true)); + f.addElement(new sugoi.form.elements.FloatInput("amount", t._("Amount"), op.amount, true)); + f.addElement(new sugoi.form.elements.DatePicker("date", t._("Date"), op.date, true)); + //f.addElement(new sugoi.form.elements.DatePicker("pending", t._("Confirmed"), !op.pending, true)); + //related operation + var unpaid = db.Operation.manager.search( $user == op.user && $group == op.group && $type != Payment ,{limit:20,orderBy:-date}); + var data = unpaid.map(function(x) return {label:x.name, value:x.id}).array(); + if (op.relation != null) data.push({label:op.relation.name,value:op.relation.id}); + f.addElement(new sugoi.form.elements.IntSelect("unpaid", t._("As a payment for :"), data, op.relation!=null ? op.relation.id : null, false)); + + + if (f.isValid()){ + f.toSpod(op); + op.pending = false; + + if (f.getValueOf("unpaid") != null){ + var t2 = db.Operation.manager.get(f.getValueOf("unpaid")); + op.relation = t2; + if (t2.amount + op.amount == 0) { + op.pending = false; + t2.lock(); + t2.pending = false; + t2.update(); + } + } + + op.update(); + throw Ok("/member/payments/" + op.user.id, t._("Operation updated")); + } + + view.form = f; + } + + /** + * Delete an operation + */ + public function doDelete(op:db.Operation){ + if (!app.user.canAccessMembership() || op.group.id != app.user.amap.id ) throw Error("/member/payments/" + op.user.id, t._("Action forbidden")); + //cannot delete a bank card payment op + if (op.getPaymentType() == "lemonway-ec"){ + throw Error("/member/payments/" + op.user.id, t._("Deleting a credit card payment is not allowed")); + } + //only an admin can delete an order op + if((op.type == db.Operation.OperationType.VOrder || op.type == db.Operation.OperationType.COrder) && !app.user.isAdmin()){ + throw Error("/member/payments/" + op.user.id, t._("Action forbidden")); + } + if (checkToken()){ + op.delete(); + throw Ok("/member/payments/" + op.user.id, t._("Operation deleted")); + } + } + + + /** + * payement entry page + * @param distribKey + */ + @tpl("transaction/pay.mtt") + public function doPay() { + + view.category = 'home'; + + var order : OrderInSession = app.session.data.order; + if (order == null) throw Redirect("/"); + if (order.products.length == 0) throw Error("/", t._("Your cart is empty")); + + view.amount = order.total; + view.paymentTypes = service.PaymentService.getAllowedPaymentTypes(app.user.amap); + view.allowMoneyPotWithNegativeBalance = app.user.amap.allowMoneyPotWithNegativeBalance; + view.futurebalance = db.UserAmap.get(app.user, app.user.amap).balance - order.total; + } + + /** + * pay by check + */ + @tpl("transaction/check.mtt") + public function doCheck(){ + + //order in session + var tmpOrder : OrderInSession = app.session.data.order; + if (tmpOrder == null) throw Redirect("/contract"); + if (tmpOrder.products.length == 0) throw Error("/", t._("Your cart is empty")); + + //get a code + var d = db.Distribution.manager.get(tmpOrder.products[0].distributionId, false); + var code = payment.Check.getCode(d.date, d.place, app.user); + + view.code = code; + view.amount = tmpOrder.total; + + //if (checkToken()){ + + //record order + var orders = OrderService.confirmSessionOrder(tmpOrder); + var ops = db.Operation.onOrderConfirm(orders); + var ordersGrouped = tools.ObjectListTool.groupOrdersByKey(orders); + + if (Lambda.array(ordersGrouped).length == 1){ + //all orders are for the same multidistrib + var name = t._("Check for the order of ::date::", {date:view.hDate(d.date)}) + " ("+code+")"; + db.Operation.makePaymentOperation(app.user,app.user.amap, payment.Check.TYPE, tmpOrder.total, name, ops[0] ); + }else{ + //orders are for multiple distribs : create one payment + db.Operation.makePaymentOperation(app.user,app.user.amap,payment.Check.TYPE, tmpOrder.total, t._("Check") + " ("+code+")" ); + } + + + //throw Ok("/contract", t._("Your payment by check has been saved. It will be validated by a coordinator at the delivery.")); + //} + + } + + /** + * pay by transfer + */ + @tpl("transaction/transfer.mtt") + public function doTransfer(){ + + //order in session + var tmpOrder : OrderInSession = app.session.data.order; + if (tmpOrder == null) throw Redirect("/contract"); + if (tmpOrder.products.length == 0) throw Error("/", t._("Your cart is empty")); + + //get a code + var d = db.Distribution.manager.get(tmpOrder.products[0].distributionId, false); + var code = payment.Check.getCode(d.date, d.place, app.user); + + view.code = code; + view.amount = tmpOrder.total; + + //if (checkToken()){ + + //record order + var orders = OrderService.confirmSessionOrder(tmpOrder); + var ops = db.Operation.onOrderConfirm(orders); + var ordersGrouped = tools.ObjectListTool.groupOrdersByKey(orders); + + if (Lambda.array(ordersGrouped).length == 1){ + //one multidistrib + var name = t._("Transfer for the order of ::date::", {date:view.hDate(d.date)}) + " ("+code+")"; + db.Operation.makePaymentOperation(app.user,app.user.amap,payment.Transfer.TYPE, tmpOrder.total, name, ops[0] ); + }else{ + //many distribs + db.Operation.makePaymentOperation(app.user,app.user.amap,payment.Transfer.TYPE, tmpOrder.total, t._("Bank transfer")+" ("+code+")" ); + } + + + //throw Ok("/contract", t._("Your payment by transfer has been saved. It will be validated by a coordinator.")); + //} + + } + + /** + * pay by cash + */ + @tpl("transaction/cash.mtt") + public function doCash(){ + + //order in session + var tmpOrder : OrderInSession = app.session.data.order; + if (tmpOrder == null) throw Redirect("/contract"); + if (tmpOrder.products.length == 0) throw Error("/", t._("Your cart is empty")); + + view.amount = tmpOrder.total; + var d = db.Distribution.manager.get(tmpOrder.products[0].distributionId, false); + + //if (checkToken()){ + + //record order + var orders = OrderService.confirmSessionOrder(tmpOrder); + var ops = db.Operation.onOrderConfirm(orders); + var ordersGrouped = tools.ObjectListTool.groupOrdersByKey(orders); + + if (Lambda.array(ordersGrouped).length == 1){ + //same multidistrib + var name = t._("Cash for the order of ::date::", {date:view.hDate(d.date)}); + db.Operation.makePaymentOperation(app.user,app.user.amap,payment.Cash.TYPE, tmpOrder.total, name , ops[0] ); + }else{ + //various distribs + db.Operation.makePaymentOperation(app.user, app.user.amap, payment.Cash.TYPE, tmpOrder.total, t._("Cash payment")); + } + + + //throw Ok("/contract", t._("Your order is validated, you commited to pay in cash at the delivery.")); + //} + + } + + /** + * Use the money pot + */ + @tpl("transaction/moneypot.mtt") + public function doMoneypot(){ + + //order in session + var tmpOrder : OrderInSession = app.session.data.order; + if (tmpOrder == null) throw Redirect("/contract"); + if (tmpOrder.products.length == 0) throw Error("/", t._("Your cart is empty")); + var futureBalance = db.UserAmap.get(app.user, app.user.amap).balance - tmpOrder.total; + if (!app.user.amap.allowMoneyPotWithNegativeBalance && futureBalance < 0) { + throw Error("/transaction/pay", t._("You do not have sufficient funds to pay this order with your money pot.")); + } + + //record order + var orders = OrderService.confirmSessionOrder(tmpOrder); + var ops = db.Operation.onOrderConfirm(orders); + + view.amount = tmpOrder.total; + view.balance = db.UserAmap.get(app.user, app.user.amap).balance; + + } +} \ No newline at end of file diff --git a/src/controller/User.hx b/src/controller/User.hx new file mode 100755 index 0000000..e472b93 --- /dev/null +++ b/src/controller/User.hx @@ -0,0 +1,255 @@ +package controller; +import haxe.crypto.Md5; +import sugoi.form.elements.Input; +import sugoi.form.Form; +import sugoi.form.elements.IntInput; +import sugoi.form.elements.StringInput; +import sugoi.form.validators.EmailValidator; +import ufront.mail.*; + +class User extends Controller +{ + public function new() + { + super(); + } + + @tpl("user/default.mtt") + function doDefault() { + + } + + + @tpl("user/login.mtt") + function doLogin() { + + if (App.current.user != null) { + throw Redirect('/'); + } + + //if its needed to redirect after login + if (app.params.exists("redirect")){ + view.redirect = app.params.exists("redirect"); + }else{ + view.redirect = "/"; + } + } + + + /** + * Choose which group to connect to. + */ + @logged + @tpl("user/choose.mtt") + function doChoose(?args: { amap:db.Amap } ) { + + if (app.user == null) throw t._("You are not connected"); + + var amaps = db.UserAmap.manager.search($user == app.user, false); + + if (amaps.length == 1 && !app.params.exists("show")) { + //qu'une amap + app.session.data.amapId = amaps.first().amap.id; + throw Redirect('/'); + }else{ + view.noGroup = true; //force template to not display current group + } + + if (args!=null && args.amap!=null) { + //select a group + var which = app.session.data==null ? 0 : app.session.data.whichUser ; + app.session.data.order = null; + app.session.data.newGroup = null; + app.session.data.amapId = args.amap.id; + app.session.data.whichUser = which; + throw Redirect('/'); + } + + view.amaps = amaps; + view.wl = db.WaitingList.manager.search($user == app.user, false); + + + #if plugins + view.pros = pro.db.PUserCompany.getCompanies(app.user); + #end + } + + function doLogout() { + App.current.session.delete(); + throw Redirect('/'); + } + + /** + * Ask for password renewal by mail + * when password is forgotten + */ + @tpl("user/forgottenPassword.mtt") + function doForgottenPassword(?key:String, ?u:db.User){ + + //STEP 1 + var step = 1; + var error : String = null; + var url = "/user/forgottenPassword"; + + //ask for mail + var askmailform = new Form("askemail"); + askmailform.addElement(new StringInput("email", t._("Please key-in your E-Mail address"),null,true)); + + //change pass form + var chpassform = new Form("chpass"); + + var pass1 = new StringInput("pass1", t._("Your new password"),null,true); + pass1.password = true; + chpassform.addElement(pass1); + + var pass2 = new StringInput("pass2", t._("Again your new password"),null,true); + pass2.password = true; + chpassform.addElement(pass2); + + var uid = new IntInput("uid","uid", u == null?null:u.id); + uid.inputType = ITHidden; + chpassform.addElement(uid); + + if (askmailform.isValid()) { + //STEP 2 + //send password renewal email + step = 2; + + var email :String = askmailform.getValueOf("email"); + var user = db.User.manager.select(email == $email, false); + //could be user 2 + if(user==null) user = db.User.manager.select(email == $email2, false); + + //user not found + if (user == null) throw Error(url, t._("This E-mail is not linked to a known account")); + + //create token + var token = haxe.crypto.Md5.encode("chp"+Std.random(1000000000)); + sugoi.db.Cache.set(token, user.id, 60 * 60 * 24 * 30); + + var m = new sugoi.mail.Mail(); + m.setSender(App.config.get("default_email"), t._("Cagette.net")); + m.setRecipient(email, user.name); + m.setSubject( "["+App.config.NAME+"] : "+t._("Password change")); + m.setHtmlBody( app.processTemplate('mail/forgottenPassword.mtt', { user:user, link:'http://' + App.config.HOST + '/user/forgottenPassword/'+token+"/"+user.id }) ); + App.sendMail(m); + } + + if (key != null && u!=null) { + //check key and propose to change pass + step = 3; + + if ( u.id == sugoi.db.Cache.get(key) ) { + view.form = chpassform; + }else { + error = t._("Invalid request"); + } + } + + if (chpassform.isValid()) { + //change pass + step = 4; + + if ( chpassform.getValueOf("pass1") == chpassform.getValueOf("pass2")) { + + var uid = Std.parseInt( chpassform.getValueOf("uid") ); + var user = db.User.manager.get(uid, true); + var pass = chpassform.getValueOf("pass1"); + user.setPass(pass); + user.update(); + + var m = new sugoi.mail.Mail(); + m.setSender(App.config.get("default_email"), t._("Cagette.net")); + m.setRecipient(user.email, user.name); + if(user.email2!=null) m.setRecipient(user.email2, user.name); + m.setSubject( "["+App.config.NAME+"] : "+t._("New password confirmed")); + var emails = [user.email]; + if(user.email2!=null) emails.push(user.email2); + var params = { + user:user, + emails:emails.join(", "), + password:pass, + NAME:App.config.NAME + } + m.setHtmlBody( app.processTemplate('mail/newPasswordConfirmed.mtt', params) ); + App.sendMail(m); + + }else { + error = t._("You must key-in two times the same password"); + } + } + + if (step == 1) { + view.form = askmailform; + } + + view.step = step; + view.error = error; + } + + + /** + * generate a custom key for transactionnal emails, valid during the current day + */ + //function getKey(m:db.User) { + //return haxe.crypto.Md5.encode(App.config.get("key")+m.email+(Date.now().getDate())).substr(0,12); + //} + + + @logged + @tpl("form.mtt") + function doDefinePassword(?key:String, ?u:db.User){ + + if (app.user.isFullyRegistred()) throw Error("/", t._("You already have a password")); + + var form = new Form("definepass"); + var pass1 = new StringInput("pass1", t._("Your new password")); + var pass2 = new StringInput("pass2", t._("Again your new password")); + pass1.password = true; + pass2.password = true; + form.addElement(pass1); + form.addElement(pass2); + + if (form.isValid()) { + + if ( form.getValueOf("pass1") == form.getValueOf("pass2")) { + + app.user.lock(); + app.user.setPass(form.getValueOf("pass1")); + app.user.update(); + throw Ok('/', t._("Congratulations, your account is now protected by a password.")); + + }else { + form.addError( t._("You must key-in two times the same password")); + } + } + view.form = form; + view.title = t._("Create a password for your account"); + } + + /** + * landing page when coming from an invitation + * @param k + */ + public function doValidate(k:String ) { + + var uid = Std.parseInt(sugoi.db.Cache.get("validation" + k)); + if (uid == null || uid==0) throw Error('/user/login', t._("Your invitation is invalid or expired ($k)")); + var user = db.User.manager.get(uid, true); + + db.User.login(user, user.email); + + var groups = user.getAmaps(); + if(groups.length>0) app.session.data.amapId = groups.first().id; + + sugoi.db.Cache.destroy("validation" + k); + + throw Ok("/user/definePassword", t._("Congratulations ::userName::, your account is validated!", {userName:user.getName()})); + + + + } + + + +} \ No newline at end of file diff --git a/src/controller/Validate.hx b/src/controller/Validate.hx new file mode 100644 index 0000000..6c4ecb9 --- /dev/null +++ b/src/controller/Validate.hx @@ -0,0 +1,154 @@ +package controller; +import db.Operation.OperationType; +using Lambda; + +/** + * Distribution validation + * @author fbarbut + */ +class Validate extends controller.Controller +{ + public var date : Date; + public var user : db.User; + public var place : db.Place; + + @tpl('validate/user.mtt') + public function doDefault(){ + view.member = user; + + if (!app.user.amap.hasShopMode()){ + //get last operations and check balance + view.operations = db.Operation.getLastOperations(this.user,place.amap,10); + view.balance = db.UserAmap.get(this.user, place.amap).balance; + } + + var b = db.Basket.get(user, place, date); + view.orders = service.OrderService.prepare(b.getOrders()); + view.place = place; + view.date = date; + view.basket = b; + + checkToken(); + } + + public function doDeleteOp(op:db.Operation){ + if (checkToken()){ + + op.lock(); + op.delete(); + + service.PaymentService.updateUserBalance(user, app.user.amap); + + throw Ok("/validate/"+date+"/"+place.id+"/"+user.id, t._("Operation deleted")); + } + } + + public function doValidateOp(op:db.Operation){ + if (checkToken()){ + + op.lock(); + op.pending = false; + op.update(); + + service.PaymentService.updateUserBalance(user, app.user.amap); + + throw Ok("/validate/"+date+"/"+place.id+"/"+user.id, t._("Operation validated")); + } + } + + @tpl('form.mtt') + public function doAddRefund(){ + + if (!app.user.isContractManager()) throw t._("Forbidden access"); + + var o = new db.Operation(); + o.user = user; + o.date = Date.now(); + + var b = db.Basket.get(user, place, date); + var op = b.getOrderOperation(false); + if(op==null) throw "unable to find related order operation"; + + var f = new sugoi.form.Form(t._("payment")); + f.addElement(new sugoi.form.elements.StringInput("name", t._("Label"), t._("Refund"), true)); + f.addElement(new sugoi.form.elements.FloatInput("amount", t._("Amount"), null, true)); + f.addElement(new sugoi.form.elements.DatePicker("date", "Date", Date.now(), true)); + var paymentTypes = service.PaymentService.getPaymentTypesForManualEntry(app.user.amap); + f.addElement(new sugoi.form.elements.StringSelect("Mtype", t._("Payment type"), paymentTypes, null, true)); + + + if (f.isValid()){ + f.toSpod(o); + o.type = db.Operation.OperationType.Payment; + var data : db.Operation.PaymentInfos = {type:f.getValueOf("Mtype")}; + o.data = data; + o.group = app.user.amap; + o.user = user; + o.relation = op; + o.amount = 0-Math.abs(o.amount); + o.insert(); + + App.current.event(NewOperation(o)); + + service.PaymentService.updateUserBalance(user, app.user.amap); + + throw Ok("/validate/"+date+"/"+place.id+"/"+user.id, t._("Refund saved")); + } + + view.title = t._("Key-in a refund for ::user::",{user:user.getCoupleName()}); + view.form = f; + } + + @tpl('form.mtt') + public function doAddPayment(){ + + if (!app.user.isContractManager()) throw Error("/",t._("Forbidden access")); + + var o = new db.Operation(); + o.user = user; + o.date = Date.now(); + + var f = new sugoi.form.Form("payment"); + f.addElement(new sugoi.form.elements.StringInput("name", t._("Label"), t._("Additional payment"), true)); + f.addElement(new sugoi.form.elements.FloatInput("amount", t._("Amount"), null, true)); + f.addElement(new sugoi.form.elements.DatePicker("date", t._("Date"), Date.now(), true)); + var paymentTypes = service.PaymentService.getPaymentTypesForManualEntry(app.user.amap); + f.addElement(new sugoi.form.elements.StringSelect("Mtype", t._("Payment type"), paymentTypes, null, true)); + + var b = db.Basket.get(user, place, date); + var op = b.getOrderOperation(false); + if(op==null) throw "unable to find related order operation"; + + if (f.isValid()){ + f.toSpod(o); + o.type = db.Operation.OperationType.Payment; + var data : db.Operation.PaymentInfos = {type:f.getValueOf("Mtype")}; + o.data = data; + o.group = app.user.amap; + o.user = user; + o.relation = op; + o.insert(); + + service.PaymentService.updateUserBalance(user, app.user.amap); + + throw Ok("/validate/"+date+"/"+place.id+"/"+user.id, t._("Payment saved")); + } + + view.title = t._("Key-in a payment for ::user::",{user:user.getCoupleName()}); + view.form = f; + } + + public function doValidate(){ + + if (checkToken()){ + + var basket = db.Basket.get(user, place, date); + service.PaymentService.validateBasket(basket); + + throw Ok("/distribution/validate/"+date+"/"+place.id, t._("Order validated")); + + } + + } + +} \ No newline at end of file diff --git a/src/controller/Vendor.hx b/src/controller/Vendor.hx new file mode 100755 index 0000000..299b016 --- /dev/null +++ b/src/controller/Vendor.hx @@ -0,0 +1,124 @@ +package controller; +import db.UserContract; +import sugoi.form.elements.Selectbox; +import sugoi.form.Form; +import neko.Web; +import sugoi.tools.Utils; + + +class Vendor extends Controller +{ + + public function new() + { + super(); + + if (!app.user.isContractManager()) throw t._("Forbidden access"); + + } + + @logged + @tpl('vendor/default.mtt') + function doDefault() { + var browse:Int->Int->List; + + //default display + browse = function(index:Int, limit:Int) { + return db.Vendor.manager.search($id > index && $amap==app.user.amap, { limit:limit, orderBy:-id }, false); + } + + var count = db.Vendor.manager.count($amap==app.user.amap); + var rb = new sugoi.tools.ResultsBrowser(count, 10, browse); + view.vendors = rb; + } + + + @tpl("vendor/view.mtt") + function doView(vendor:db.Vendor) { + view.vendor = vendor; + } + + @tpl('form.mtt') + function doEdit(vendor:db.Vendor) { + + var form = sugoi.form.Form.fromSpod(vendor); + form.removeElement( form.getElement("amapId") ); + + if (form.isValid()) { + form.toSpod(vendor); //update model + vendor.amap = app.user.amap; + vendor.update(); + throw Ok('/contractAdmin', t._("This supplier has been updated")); + } + + view.form = form; + } + + @tpl("form.mtt") + public function doInsert() { + + + var m = new db.Vendor(); + var form = sugoi.form.Form.fromSpod(m); + form.removeElement(form.getElement("amapId")); + + if (form.isValid()) { + form.toSpod(m); //update model + m.amap = app.user.amap; + m.insert(); + + throw Ok('/contractAdmin/', t._("This supplier has been saved")); + } + + view.form = form; + } + + public function doDelete(v:db.Vendor) { + if (!app.user.isAmapManager()) throw t._("Forbidden action"); + if (checkToken()) { + + if (db.Contract.manager.search($vendorId == v.id).length > 0) throw Error('/contractAdmin', t._("You cannot delete this supplier because some contracts (current or old) are referencing this supplier.")); + + v.lock(); + v.delete(); + throw Ok("/contractAdmin", t._("Supplier deleted")); + } + + } + + @tpl('vendor/addimage.mtt') + function doAddImage(v:db.Vendor) { + + view.vendor = v; + view.image = v.image; + + var request = sugoi.tools.Utils.getMultipart(1024 * 1024 * 12); //12Mb + + if (request.exists("image")) { + + //Image + var image = request.get("image"); + if (image != null && image.length > 0) { + var img : sugoi.db.File = null; + if ( Sys.systemName() == "Windows") { + img = sugoi.db.File.create(request.get("image"), request.get("image_filename")); + }else { + img = sugoi.tools.UploadedImage.resizeAndStore(request.get("image"), request.get("image_filename"), 400, 400); + } + + v.lock(); + + if (v.image != null) { + //efface ancienne + v.image.lock(); + v.image.delete(); + } + + v.image = img; + v.update(); + throw Ok('/contractAdmin/', t._("Image updated")); + } + } + } + +} \ No newline at end of file diff --git a/src/controller/admin/Admin.hx b/src/controller/admin/Admin.hx new file mode 100755 index 0000000..b1b6d85 --- /dev/null +++ b/src/controller/admin/Admin.hx @@ -0,0 +1,223 @@ +package controller.admin; +import haxe.web.Dispatch; +import Common; + +class Admin extends Controller { + + public function new() { + super(); + view.category = 'admin'; + + //trigger a "Nav" event + var nav = new Array(); + var e = Nav(nav,"admin"); + app.event(e); + view.nav = e.getParameters()[0]; + + } + + @tpl("admin/default.mtt") + function doDefault() { + view.now = Date.now(); + } + + @tpl("admin/emails.mtt") + function doEmails() { + var browse = function(index:Int, limit:Int) { + return sugoi.db.BufferedMail.manager.search($sdate==null,{limit:[index,limit],orderBy:-cdate},false); + } + + var count = sugoi.db.BufferedMail.manager.count($sdate==null); + view.browser = new sugoi.tools.ResultsBrowser(count,10,browse); + view.num = count; + + } + + @tpl("form.mtt") + function doSmtp() { + + var f = new sugoi.form.Form("emails"); + var data = [ + {label:"SMTP",value:"smtp"}, + {label:"Mandrill API",value:"mandrill"}, + ]; + + var mailer = sugoi.db.Variable.get("mailer")==null ? "smtp" : sugoi.db.Variable.get("mailer"); + var host = sugoi.db.Variable.get("smtp_host")==null ? App.config.get("smtp_host") : sugoi.db.Variable.get("smtp_host"); + var port = sugoi.db.Variable.get("smtp_port")==null ? App.config.get("smtp_port") : sugoi.db.Variable.get("smtp_port"); + var user = sugoi.db.Variable.get("smtp_user")==null ? App.config.get("smtp_user") : sugoi.db.Variable.get("smtp_user"); + var pass = sugoi.db.Variable.get("smtp_pass")==null ? App.config.get("smtp_pass") : sugoi.db.Variable.get("smtp_pass"); + + + f.addElement(new sugoi.form.elements.StringSelect("mailer", "Mailer", data, mailer )); + f.addElement(new sugoi.form.elements.StringInput("smtp_host", "host", host)); + f.addElement(new sugoi.form.elements.StringInput("smtp_port", "port", port)); + f.addElement(new sugoi.form.elements.StringInput("smtp_user", "user", user)); + f.addElement(new sugoi.form.elements.StringInput("smtp_pass", "pass", pass)); + + + if (f.isValid()){ + for ( k in ["mailer","smtp_host","smtp_port","smtp_user","smtp_pass"]){ + sugoi.db.Variable.set(k, f.getValueOf(k)); + } + throw Ok("/admin/emails", t._("Configuration updated") ); + + } + + view.title = t._("Email service configuration"); + view.form = f; + } + + function doPlugins(d:Dispatch) { + d.dispatch(new controller.admin.Plugins()); + } + + + @tpl("admin/taxo.mtt") + function doTaxo(){ + + view.categ = db.TxpCategory.manager.all(); + + } + + /** + * Display errors logged in DB + */ + @tpl("admin/errors.mtt") + function doErrors( args:{?user: Int, ?like: String, ?empty:Bool} ) { + view.now = Date.now(); + + view.u = args.user!=null ? db.User.manager.get(args.user,false) : null; + view.like = args.like!=null ? args.like : ""; + + var sql = ""; + if( args.user!=null ) sql += " AND uid="+args.user; + //if( args.like!=null && args.like != "" ) sql += " AND error like "+sys.db.Manager.cnx.quote("%"+args.like+"%"); + if (args.empty) { + sys.db.Manager.cnx.request("truncate table Error"); + } + + var errorsStats = sys.db.Manager.cnx.request("select count(id) as c, DATE_FORMAT(date,'%y-%m-%d') as day from Error where date > NOW()- INTERVAL 1 MONTH "+sql+" group by day order by day").results(); + view.errorsStats = errorsStats; + + view.browser = new sugoi.tools.ResultsBrowser( + sugoi.db.Error.manager.unsafeCount("SELECT count(*) FROM Error WHERE 1 "+sql), + 20, + function(start, limit) { return sugoi.db.Error.manager.unsafeObjects("SELECT * FROM Error WHERE 1 "+sql+" ORDER BY date DESC LIMIT "+start+","+limit,false); } + ); + } + + @tpl("admin/graph.mtt") + function doGraph(?key:String,?year:Int,?month:Int){ + + + var from = new Date(year,month,1,0,0,0); + var to = new Date(year,month+1,0,23,59,59); + + if(app.params.exists("recompute")){ + + switch(key){ + case "basket": + for( d in 1...to.getDate()){ + var _from = new Date(year,month,d,0,0,0); + var _to = new Date(year,month,d,23,59,59); + var value = db.Basket.manager.count($cdate>=_from && $cdate<=_to); + var g = db.Graph.record(key,value, _from ); + // trace(value,_from,g); + + } + + + } + + } + + var data = db.Graph.getRange(key,from,to); + view.data = data; + view.from = from; + view.to = to; + view.key = key; + + var averageValue = 0.0; + var total = 0.0; + for( d in data) total += d.value; + averageValue = total/data.length; + view.total = total; + view.averageValue = averageValue; + + + + + } + + function doFixDistribValidation() { + + Sys.println("===== Liste des distributions ayant été re-validées ====
    "); + + //Get the current group + var group = app.user.amap; + //Get all the contracts for this given group + var contractIds = Lambda.map(group.getContracts(),function(x) return x.id); + //Get all the validated distributions for this given group + var validatedDistribs = db.Distribution.manager.search( ($contractId in contractIds) && $validated == true, {orderBy:date}, false); + for (distrib in validatedDistribs){ + + service.PaymentService.validateDistribution(distrib); + Sys.println(distrib.toString() + "
    "); + + } + + Sys.println("===== Fin de la liste ===="); + + } + + function doCheckDistribValidation() { + + Sys.println("===== Liste des distributions validées ayant des opérations/paiements non validés ====
    "); + + //Get the current group + var group = app.user.amap; + //Get all the contracts for this given group + var contractIds = Lambda.map(group.getContracts(),function(x) return x.id); + //Get all the validated distributions for this given group + var validatedDistribs = db.Distribution.manager.search( ($contractId in contractIds) && $validated == true, {orderBy:date}, false); + for (distrib in validatedDistribs){ + + for (user in distrib.getUsers()){ + + var basket = db.Basket.get(user, distrib.place, distrib.date); + if (basket == null || basket.isValidated()) continue; + + for (order in basket.getOrders()){ + if (!order.paid) { + Sys.println(order.distribution.toString() + "
    "); + Sys.println(order.toString() + "
    "); + } + } + + var operation = basket.getOrderOperation(false); + if (operation != null){ + + if (operation.pending) { + Sys.println(distrib.toString() + "
    "); + Sys.println(operation.toString() + "
    "); + } + + for ( payment in basket.getPayments()){ + + if (payment.pending){ + Sys.println(distrib.toString() + "
    "); + Sys.println(payment.toString() + "
    "); + } + } + } + + } + } + + Sys.println("===== Fin de la liste ===="); + + } + +} + diff --git a/src/controller/admin/Plugins.hx b/src/controller/admin/Plugins.hx new file mode 100755 index 0000000..1170c23 --- /dev/null +++ b/src/controller/admin/Plugins.hx @@ -0,0 +1,32 @@ +package controller.admin; + +/** + * ... + * @author fbarbut + */ +class Plugins extends controller.Controller +{ + + public function new() + { + super(); + } + + @tpl("admin/plugins/default.mtt") + public function doDefault() { + view.plugins = App.current.plugins; + } + + + public function doInstall(plugin:String) { + + /*var p = App.current.getPlugin(plugin); + if (p == null) throw Error("/admin/plugins","Le plugin '"+plugin+"' introuvable"); + + if(p.isInstalled()) throw Ok("/admin/plugins","Le plugin '"+plugin+"' est déjà installé"); + p.install(); + throw Ok("/admin/plugins","Le plugin '"+plugin+"' est correctement installé");*/ + + } + +} \ No newline at end of file diff --git a/src/controller/api/Group.hx b/src/controller/api/Group.hx new file mode 100644 index 0000000..eeec87f --- /dev/null +++ b/src/controller/api/Group.hx @@ -0,0 +1,80 @@ +package controller.api; +import haxe.Json; +import Common; + +/** + * Groups API + * @author fbarbut + */ +class Group extends Controller +{ + /** + * JSON map datas + * + * Request by zone : http://localhost/api/group/map?minLat=42.8115217450979&maxLat=51.04139389812637=&minLng=-18.369140624999996&maxLng=23.13720703125 + * Request by location : http://localhost/api/group/map?lat=48.85&lng=2.32 + * Request by address : http://localhost/api/group/map?address=105%20avenue%20d%27ivry%20Paris + */ + public function doMap(args:{?minLat:Float, ?maxLat:Float, ?minLng:Float, ?maxLng:Float, ?lat:Float, ?lng:Float, ?address:String}) { + + var out = new Array(); + var places = new List(); + if (args.minLat != null && args.maxLat != null && args.minLng != null && args.maxLng != null){ + + //Request by zone + #if plugins + var sql = "select p.* from Place p, Hosting h where h.id=p.amapId and h.visible=1 and "; + sql += 'p.lat > ${args.minLat} and p.lat < ${args.maxLat} and p.lng > ${args.minLng} and p.lng < ${args.maxLng}'; + #else + var sql = "select p.* from Place p where "; + sql += 'p.lat > ${args.minLat} and p.lat < ${args.maxLat} and p.lng > ${args.minLng} and p.lng < ${args.maxLng}'; + #end + places = db.Place.manager.unsafeObjects(sql, false); + + }else if (args.lat!=null && args.lng!=null){ + + //Request by location + places = findGroupByDist(args.lat, args.lng); + + }else{ + //Request by address + if (args.address == null) throw "Please provide parameters"; + + var geocode = new sugoi.apis.google.GeoCode(App.config.get("google_geocoding_key")); + var loc = geocode.geocode(args.address)[0].geometry.location; + + args.lat = loc.lat; + args.lng = loc.lng; + + places = findGroupByDist(args.lat, args.lng); + } + + for ( p in places){ + out.push({ + id : p.amap.id, + name : p.amap.name, + image : p.amap.image==null ? null : view.file(p.amap.image), + place : p.getInfos() + }); + } + + Sys.print(haxe.Json.stringify({success:true,groups:out})); + } + + /** + * ~~ Pythagore rulez ~~ + */ + function findGroupByDist(lat:Float, lng:Float,?limit=5){ + #if plugins + var sql = 'select p.*,SQRT( POW(p.lat-$lat,2) + POW(p.lng-$lng,2) ) as dist from Place p, Hosting h '; + sql += "where h.id=p.amapId and h.visible=1 and p.lat is not null "; + sql += 'order by dist asc LIMIT $limit'; + #else + var sql = 'select p.*,SQRT( POW(p.lat-$lat,2) + POW(p.lng-$lng,2) ) as dist from Place p '; + sql += "where p.lat is not null "; + sql += 'order by dist asc LIMIT $limit'; + #end + return db.Place.manager.unsafeObjects(sql, false); + } + +} \ No newline at end of file diff --git a/src/controller/api/Order.hx b/src/controller/api/Order.hx new file mode 100644 index 0000000..cbafc04 --- /dev/null +++ b/src/controller/api/Order.hx @@ -0,0 +1,142 @@ +package controller.api; +import haxe.Json; +import tink.core.Error; +import service.OrderService; + +/** + * Public order API + */ +class Order extends Controller +{ + /** + * get orders of a user from a contractId (constant contract) or a distributionId (varying contract) + */ + public function doGet(userId:Int){ + + checkIsLogged(); + + //params + var p = app.params; + var distributionId = Std.parseInt(p.get("distributionId")); + var contractId = Std.parseInt(p.get("contractId")); + if (distributionId == null && contractId == null) throw "You should provide a contractId or a distributionId"; + var user = db.User.manager.get(userId, false); + if (user == null) throw 'user #$userId doesn\'t exists'; + var c : db.Contract = null; + var d : db.Distribution = null; + if (distributionId == null) { + c = db.Contract.manager.get(contractId, false); + }else{ + d = db.Distribution.manager.get(distributionId, false); + c = d.contract; + } + + //rights + if (!app.user.canManageContract(c)) throw new Error(t._("You do not have the authorization to manage this contract")); + if (d != null && d.validated) throw new Error(t._("This delivery has been already validated")); + if (c.type == db.Contract.TYPE_VARORDER && d == null ) throw "this contract is a 'varying order contract', please provide a distributionId"; + + //get datas + var pids = tools.ObjectListTool.getIds(c.getProducts(false)); + var orders; + if (c.type == db.Contract.TYPE_VARORDER) { + orders = db.UserContract.manager.search($user == user && $distributionId==d.id && ($productId in pids), true); + }else { + orders = db.UserContract.manager.search($user == user && ($productId in pids), true); + } + var orders = OrderService.prepare(orders); + + Sys.print(tink.Json.stringify({success:true,orders:orders})); + } + + /** + * Update orders of a user ( from react OrderBox component ) + * @param userId + */ + public function doUpdate(userId:Int){ + + checkIsLogged(); + + //GET params + var p = app.params; + var distributionId = Std.parseInt(p.get("distributionId")); + var contractId = Std.parseInt(p.get("contractId")); + + //POST payload + var data = new Array<{id:Int,productId:Int,qt:Float,paid:Bool,invertSharedOrder:Bool,userId2:Int}>(); + data = haxe.Json.parse( StringTools.urlDecode(sugoi.Web.getPostData()) ).orders; + + if (distributionId == null && contractId == null) throw "You should provide a contractId or a distributionId"; + var user = db.User.manager.get(userId, false); + if (user == null) throw 'user #$userId doesn\'t exists'; + + var c : db.Contract = null; + var d : db.Distribution = null; + if (distributionId == null) { + c = db.Contract.manager.get(contractId, false); + }else{ + d = db.Distribution.manager.get(distributionId, false); + c = d.contract; + } + var pids = tools.ObjectListTool.getIds(c.getProducts(false)); + + //rights & checks + //fbarbut 2018-11-13 : too many problems when people try to edit the order of someone who left the group... + //if (!user.isMemberOf(c.amap)) throw new Error(t._("::user:: is not member of this group", {user:user.name})); + if (!app.user.canManageContract(c)) throw new Error(t._("You do not have the authorization to manage this contract")); + if (d != null && d.validated) throw new Error(t._("This delivery has been already validated")); + if (c.type == db.Contract.TYPE_VARORDER && d == null ) throw "this contract is a 'varying order contract', please provide a distributionId"; + + /* + * record orders + **/ + + //find existing orders + var exOrders = null; + if (c.type == db.Contract.TYPE_VARORDER) { + exOrders = db.UserContract.manager.search($user == user && $distributionId==d.id && ($productId in pids), true); + }else { + exOrders = db.UserContract.manager.search($user == user && ($productId in pids), true); + } + + var orders = []; + for (o in data) { + + //get product + var product = db.Product.manager.get(o.productId, false); + if (product.contract.id != c.id) throw "product " + o.productId + " is not in contract " + c.id; + + //find existing order + var uo = Lambda.find(exOrders, function(uo) return uo.id == o.id); + + //user2 + invert + var user2 : db.User = null; + var invert = false; + if ( o.userId2 != null ) { + user2 = db.User.manager.get(o.userId2,false); + if (user2 == null) throw t._("Unable to find user #::num::",{num:o.userId2}); + if (!user2.isMemberOf(product.contract.amap)) throw t._("::user:: is not part of this group",{user:user2}); + if (user.id == user2.id) throw t._("Both selected accounts must be different ones"); + + invert = o.invertSharedOrder; + } + + //record order + if (uo != null) { + //existing record + var o = OrderService.edit(uo, o.qt, o.paid , user2, invert); + if (o != null) orders.push(o); + }else { + //new record + var o = OrderService.make(user, o.qt , product, d == null ? null : d.id, o.paid , user2, invert); + if (o != null) orders.push(o); + } + } + + app.event(MakeOrder(orders)); + db.Operation.onOrderConfirm(orders); + + Sys.print(Json.stringify({success:true, orders:data})); + } + +} \ No newline at end of file diff --git a/src/controller/api/Product.hx b/src/controller/api/Product.hx new file mode 100644 index 0000000..6215de5 --- /dev/null +++ b/src/controller/api/Product.hx @@ -0,0 +1,20 @@ +package controller.api; +import Common; + +/** + * Product API + * @author fbarbut + */ +class Product extends Controller +{ + + public function doGet(args:{?contractId:db.Contract}) { + + if(args==null || args.contractId==null) throw "invalid params"; + + var out = {products:new Array()}; + for( p in args.contractId.getProducts(false)) out.products.push(p.infos(false,false)); + Sys.print(tink.Json.stringify(out)); + } + +} \ No newline at end of file diff --git a/src/controller/api/Shop.hx b/src/controller/api/Shop.hx new file mode 100644 index 0000000..10f3132 --- /dev/null +++ b/src/controller/api/Shop.hx @@ -0,0 +1,209 @@ +package controller.api; +import haxe.Json; +import tink.core.Error; +import Common; +import db.Amap; +import tools.ArrayTool; +using tools.ObjectListTool; +using Lambda; + +class Shop extends Controller +{ + /** + * @doc https://app.swaggerhub.com/apis/Cagette.net/Cagette.net/0.9.2#/shop/get_shop_categories + */ + public function doCategories(args:{date:String, place:db.Place}){ + + var out = new Array(); + var group = args.place.amap; + + if (group.flags.has(ShopCategoriesFromTaxonomy)){ + + //TAXO CATEGORIES + var taxoCategs = db.TxpCategory.manager.all(false); + for (txp in taxoCategs){ + + var c : CategoryInfo = {id:txp.id, name:txp.name, subcategories:[]}; + for (sc in txp.getSubCategories()){ + c.subcategories.push({id:sc.id,name:sc.name}); + } + out.push(c); + } + + }else{ + + //CUSTOM CATEGORIES + var catGroups = db.CategoryGroup.get(group); + for (cat in catGroups){ + + var c : CategoryInfo = {id:cat.id, name:cat.name, subcategories:[]}; + for ( sc in cat.getCategories() ){ + c.subcategories.push({id:sc.id,name:sc.name}); + } + out.push(c); + } + } + + Sys.print(Json.stringify({success:true,categories:out})); + } + + /** + * @doc https://app.swaggerhub.com/apis/Cagette.net/Cagette.net/0.9.2#/shop/get_shop_products + */ + public function doProducts(args:{date:String, place:db.Place, ?category:Int, ?subcategory:Int}){ + + if ( args == null || (args.category == null && args.subcategory == null)) throw "You should provide a category Id or a subcategory Id"; + //need some optimization : populating all thses objects eats memory, and we need only the ids ! + var products = getProducts(args.place, Date.fromString(args.date), args.place.amap.flags.has(ShopCategoriesFromTaxonomy)); + var pids = products.getIds(); + var categsFromTaxo = args.place.amap.flags.has(ShopCategoriesFromTaxonomy); + var catName = "undefined category"; + + if( categsFromTaxo ){ + + /** + * Use Taxonomy : + * - Category is TxpCatgory + * - Subcategory us TxpSubCategory + * - Products are linked to TxpProduct which belongs to a TxpCatgory and a TxpSubCategory + */ + var sql = ""; + + if (args.subcategory != null){ + + sql = 'SELECT p.* FROM Product p, TxpProduct tp, TxpSubCategory sc + WHERE p.txpProductId = tp.id + AND tp.subCategoryId = sc.id + AND sc.id = ${args.subcategory} + AND p.id IN ( ${pids.join(",")} )'; + + var cat = db.TxpSubCategory.manager.get(args.subcategory, false); + if (cat == null) throw 'unknown subcategory #' + args.subcategory; + catName = cat.name; + + }else if (args.category != null){ + + sql = 'SELECT p.* FROM Product p, TxpProduct tp, TxpCategory c + WHERE p.txpProductId = tp.id + AND tp.categoryId = c.id + AND c.id = ${args.category} + AND p.id IN ( ${pids.join(",")} )'; + + var cat = db.TxpCategory.manager.get(args.subcategory, false); + if (cat == null) throw 'unknown category #' + args.category; + catName = cat.name; + } + + products = db.Product.manager.unsafeObjects(sql,false).array(); + + }else{ + + /** + * Use custom categories : + * - Category is CategoryGroup + * - Subcategory is Category + * - Products are tagged with ProductCategory + */ + var sql = ""; + + if (args.subcategory != null){ + + sql = 'SELECT p.* FROM Product p, ProductCategory pc, Category c + WHERE pc.productId = p.id + AND pc.categoryId = c.id + AND c.id = ${args.subcategory} + AND p.id IN ( ${pids.join(",")} )'; + + var cat = db.Category.manager.get(args.subcategory, false); + if (cat == null) throw 'unknown subcategory #' + args.subcategory; + catName = cat.name; + + }else if (args.category != null){ + + sql = 'SELECT p.* FROM Product p, ProductCategory pc, Category c, CategoryGroup cg + WHERE pc.productId = p.id + AND pc.categoryId = c.id + AND c.categoryGroupId = cg.id + AND cg.id = ${args.category} + AND p.id IN ( ${pids.join(",")} )'; + + var cat = db.CategoryGroup.manager.get(args.category, false); + if (cat == null) throw 'unknown category #' + args.category; + catName = cat.name; + } + + products = db.Product.manager.unsafeObjects(sql,false).array(); + } + + //to productInfos + var products : Array = products.map( function(p) return p.infos(categsFromTaxo,true) ).array(); + + if (args.category != null){ + Sys.print(Json.stringify( {success:true, products:products, category:catName} )); + }else{ + Sys.print(Json.stringify( {success:true, products:products, subcategory:catName} )); + } + } + + + public function doInit(args:{place:db.Place, date:String}){ + + var out = {place:args.place.getInfos(), orderEndDates: new Array<{date:String,contracts:Array}>() }; + + //order end dates + var contracts = db.Contract.getActiveContracts(args.place.amap); + + for (c in Lambda.array(contracts)) { + if (c.type != db.Contract.TYPE_VARORDER) contracts.remove(c);//only varying orders + if (!c.isVisibleInShop()) contracts.remove(c); + } + + var date = Date.fromString(args.date); + var now = Date.now(); + var cids = Lambda.map(contracts, function(c) return c.id); + var d1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); + var d2 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59); + + var distribs = db.Distribution.manager.search(($contractId in cids) && $orderStartDate <= now && $orderEndDate >= now && $date > d1 && $end < d2 && $place == args.place, false); + var distribByDate = ArrayTool.groupByDate(Lambda.array(distribs), "orderEndDate"); + out.orderEndDates = []; + for ( k in distribByDate.keys() ) { + out.orderEndDates.push( {date:k , contracts: distribByDate.get(k).map( function(x) return x.contract.name)} ); + } + + + Sys.print(Json.stringify( out )); + } + + private function getProductInfos(place:db.Place, date, ?categsFromTaxo = false):Array{ + var products = getProducts(place, date, categsFromTaxo); + return Lambda.array(Lambda.map(products, function(p) return p.infos(categsFromTaxo))); + } + + /** + * Get the available products list + */ + private function getProducts(place:db.Place,date,?categsFromTaxo=false):Array { + + var contracts = db.Contract.getActiveContracts(place.amap); + + for (c in Lambda.array(contracts)) { + if (c.type != db.Contract.TYPE_VARORDER) contracts.remove(c);//only varying orders + if (!c.isVisibleInShop()) contracts.remove(c); + } + + var now = Date.now(); + var cids = Lambda.map(contracts, function(c) return c.id); + var d1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); + var d2 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59); + + var distribs = db.Distribution.manager.search(($contractId in cids) && $orderStartDate <= now && $orderEndDate >= now && $date > d1 && $end < d2 && $place == place, false); + + var cids = Lambda.map(distribs, function(d) return d.contract.id); + return Lambda.array(db.Product.manager.search(($contractId in cids) && $active==true, { orderBy:name }, false)); + + } + + + +} \ No newline at end of file diff --git a/src/controller/api/User.hx b/src/controller/api/User.hx new file mode 100644 index 0000000..336560a --- /dev/null +++ b/src/controller/api/User.hx @@ -0,0 +1,57 @@ +package controller.api; +import haxe.Json; +import tink.core.Error; +import Common; + +/** + * Public user API + */ +class User extends Controller +{ + + /** + * Login + */ + public function doLogin(){ + + //cleaning + var email = StringTools.trim(App.current.params.get("email")).toLowerCase(); + var pass = StringTools.trim(App.current.params.get("password")); + + service.UserService.login(email, pass); + + Sys.print(Json.stringify({success:true})); + } + + /** + * Register + */ + public function doRegister(){ + + //cleaning + var p = app.params; + var email = StringTools.trim(p.get("email")).toLowerCase(); + var pass = StringTools.trim(p.get("password")); + var firstName = StringTools.trim(p.get("firstName")); + var lastName = StringTools.trim(p.get("lastName")).toUpperCase(); + var phone = p.exists("phone") ? StringTools.trim(p.get("phone")) : null; + + service.UserService.register(firstName, lastName, email, phone, pass); + + Sys.print(Json.stringify({success:true})); + } + + + /** + * get users of current group + */ + @logged + function doGetFromGroup(){ + + if(!app.user.canAccessMembership()) throw new tink.core.Error(403,"Access forbidden"); + + var members:Array = service.UserService.getFromGroup(app.user.amap).map(function(m) return m.infos() ); + Sys.print(tink.Json.stringify({users:members})); + } + +} \ No newline at end of file diff --git a/src/db/Amap.hx b/src/db/Amap.hx new file mode 100755 index 0000000..ede3da6 --- /dev/null +++ b/src/db/Amap.hx @@ -0,0 +1,373 @@ +package db; +import sugoi.form.ListData.FormData; +import sys.db.Object; +import sys.db.Types; +import Common; + +enum AmapFlags { + HasMembership; //membership management + ShopMode; //shop mode / standard mode + HasPayments; //manage payments and user balance + ComputeMargin; //compute margin instead of percentage + CagetteNetwork; //register in cagette.net groups directory + ShopCategoriesFromTaxonomy; //the custom categories are not used anymore, use product taxonomy instead + HidePhone; //Hide manager phone on group public page + PhoneRequired; //phone number of members is required for this group + +} + +//user registration options +enum RegOption{ + Closed; + WaitingList; + Open; + Full; +} + +enum GroupType{ + Amap; //CSA / GASAP / AMAP + GroupedOrders; //groupements d'achat + ProducerDrive; //drive de producteurs + FarmShop; //vente à la ferme +} + + +/** + * AMAP + */ +class Amap extends Object +{ + public var id : SId; + public var name : SString<64>; + + @formPopulate("getMembersFormElementData") + @:relation(userId) + public var contact : SNull; + + public var txtIntro:SNull; //introduction de l'amap + public var txtHome:SNull; //texte accueil adhérents + public var txtDistrib:SNull; //sur liste d'emargement + + public var extUrl : SNull>; //lien sur logo du groupe + + public var membershipRenewalDate : SNull; + @hideInForms public var membershipPrice : SNull; + + @hideInForms + public var vatRates : SData>; + + public var flags:SFlags; + public var groupType:SNull>; + + @hideInForms @:relation(imageId) + public var image : SNull; + + @hideInForms public var cdate : SDateTime; + @hideInForms @:relation(placeId) public var mainPlace : SNull; + + public var regOption : SEnum; + + @hideInForms public var currency:SString<12>; //name or symbol. + @hideInForms public var currencyCode:SString<3>; //https://fr.wikipedia.org/wiki/ISO_4217 + + //payments + @hideInForms public var allowedPaymentsType:SNull>>; + @hideInForms public var checkOrder:SNull>; + @hideInForms public var IBAN:SNull>; + @hideInForms public var allowMoneyPotWithNegativeBalance:SNull; + + public function new() + { + super(); + flags = cast 0; + flags.set(CagetteNetwork); + flags.set(ShopMode); + vatRates = ["5,5%" => 5.5, "20%" => 20]; + cdate = Date.now(); + regOption = Open; + currency = "€"; + currencyCode = "EUR"; + checkOrder = ""; + + } + + /** + * find the most common delivery place + */ + public function getMainPlace() { + + if (mainPlace != null && Std.random(100) != 0) { + return mainPlace; + }else { + this.lock(); + + var places = getPlaces(); + + //just 1 place + if (places.length == 1) { + this.mainPlace = places.first(); + this.update(); + return this.mainPlace; + } + + //no places ! + if (places.length == 0) return null; + + var pids = Lambda.map(places, function(x) return x.id); + + var res = sys.db.Manager.cnx.request("select placeId,count(placeId) as top from Distribution where placeId IN ("+pids.join(",")+") group by placeId order by top desc").results(); + var res = res.first(); + var pid :Int = null; + + if (res == null){ + pid = this.getPlaces().first().id; + }else{ + pid = Std.parseInt(res.placeId); + } + + if (pid != 0 && pid != null) { + var p = db.Place.manager.get(pid, false); + this.mainPlace = p; + this.update(); + return p; + }else { + return null; + } + } + } + + /** + * Methods to get flags in templates + */ + + public function hasMembership():Bool { + return flags != null && flags.has(HasMembership); + } + + public function hasShopMode() { + return flags.has(ShopMode); + } + + public function canExposePhone() { + return !flags.has(HidePhone); + } + + public function hasPayments(){ + return flags != null && flags.has(HasPayments); + } + + public function hasTaxonomy(){ + return flags != null && flags.has(ShopCategoriesFromTaxonomy); + } + + public function hasPhoneRequired(){ + return flags != null && flags.has(PhoneRequired); + } + + public function getCategoryGroups() { + + //if (flags.has(ShopCategoriesFromTaxonomy)){ + //return Lambda.array( cast db.TxpCategory.manager.all(false) ); + //}else{ + //return Lambda.array( db.CategoryGroup.get(this) ); + //} + var t = sugoi.i18n.Locale.texts; + var categs = new Array<{id:Int,name:String,color:String,pinned:Bool,categs:Array}>(); + + if (this.flags.has(db.Amap.AmapFlags.ShopCategoriesFromTaxonomy)){ + + //TAXO CATEGORIES + var taxoCategs = db.TxpCategory.manager.all(false); + var c : Array = Lambda.array(Lambda.map( taxoCategs, function(c){return {id:c.id, name:c.name, subcategories:null}; })); + + categs.push({ + id:0, + name: t._("Product type"), + pinned:false, + color:"#583816", + categs: c + }); + + }else{ + + //CUSTOM CATEGORIES + var catGroups = db.CategoryGroup.get(this); + for ( cg in catGroups){ + var color = App.current.view.intToHex(db.CategoryGroup.COLORS[cg.color]); + categs.push({ + id:cg.id, + name:cg.name, + pinned:cg.pinned, + color:color, + categs: Lambda.array(Lambda.map( cg.getCategories(), function(c) return c.infos())) + }); + } + } + + return categs; + + } + + + //public function canAddMember():Bool { + // return isAboOk(true); + //} + + /** + * Renvoie la liste des contrats actifs + * @param large=false + */ + public function getActiveContracts(?large=false) { + return Contract.getActiveContracts(this, large, false); + } + + public function getContracts() { + return Contract.manager.search($amap == this, false); + } + + /** + * récupere les produits des contracts actifs + */ + public function getProducts() { + var contracts = db.Contract.getActiveContracts(App.current.user.amap,false,false); + return Product.manager.search( $contractId in Lambda.map(contracts, function(c) return c.id),{orderBy:name}, false); + } + + /** + * get next multi-deliveries + */ + public function getDeliveries(?limit=3){ + var out = new Map(); + for ( c in getActiveContracts()){ + for ( d in c.getDistribs(true,3)){ + out.set(d.getKey(), d); + } + } + + var out = Lambda.array(out); + out.sort(function(a, b){ + return Math.round(a.date.getTime() / 1000) - Math.round(b.date.getTime() / 1000); + }); + return out.slice(0,limit); + } + + public function getPlaces() { + return Place.manager.search($amap == this, false); + } + + public function getVendors() { + return Vendor.manager.search($amap == this, false); + } + + public function getMembers() { + return User.manager.unsafeObjects("Select u.* from User u,UserAmap ua where u.id=ua.userId and ua.amapId="+this.id+" order by u.lastName", false); + } + + public function getMembersNum():Int{ + return UserAmap.manager.count($amapId == this.id); + } + + public function getMembersFormElementData():FormData { + var m = getMembers(); + var out = []; + for (mm in m) { + + out.push({label:mm.getCoupleName() , value:mm.id}); + + } + return out; + } + + override public function toString() { + if (name != '' && name != null) { + return name; + }else { + return 'group#' + id; + } + } + + /** + * pour avoir le nom de la periode de cotisation pour une date donnée + */ + public function getPeriodName(?d:Date):String { + if (d == null) d = Date.now(); + var year = getMembershipYear(d); + return getPeriodNameFromYear(year); + } + + /** + * Si la date de renouvellement est en janvier ou février, on note la cotisation avec l'année en cours, + * sinon c'est "à cheval" donc on note la cotis avec l'année la plus ancienne (ex:2014 pour une cotis 2014-2015) + */ + public function getMembershipYear(?d:Date):Int { + if (d == null) d = Date.now(); + var year = d.getFullYear(); + var n = membershipRenewalDate; + if (n == null) n = Date.now(); + var renewalDate = new Date(year, n.getMonth(), n.getDate(), 0, 0, 0); + + //if (membershipRenewalDate.getMonth() <= 1) { + + if (d.getTime() < renewalDate.getTime()) { + return year-1; + }else { + return year; + } + + //}else { + //return year - 1; + //} + } + + /** + * à partir d'une année de cotis enregistrée, afficher le nom de la periode + * @param y + */ + public function getPeriodNameFromYear(y:Int):String { + if (membershipRenewalDate!=null && membershipRenewalDate.getMonth() <= 1) { + return Std.string(y); + }else { + return Std.string(y) + "-" + Std.string(y+1); + } + } + + override public function insert(){ + + if (txtHome == null){ + var t = sugoi.i18n.Locale.texts; + txtHome = t._("Welcome in the group of ::name::!\n You can look at the delivery planning or make a new order.",{name:this.name}); + } + + App.current.event(NewGroup(this,App.current.user)); + + super.insert(); + } + + public function getCurrency():String{ + + if (currency == ""){ + lock(); + currency = "€"; + currencyCode = "EUR"; + update(); + } + + return currency; + } + + public static function getLabels(){ + var t = sugoi.i18n.Locale.texts; + return [ + "name" => t._("Group name"), + "txtIntro" => t._("Short description"), + "txtHome" => t._("Homepage text"), + "txtDistrib" => t._("Text for distribution lists"), + "extUrl" => t._("Group website URL"), + "membershipRenewalDate" => t._("Membership renewal date"), + "flags" => t._("Options"), + "groupType" => t._("Group type"), + "regOption" => t._("Registration setting"), + "contact" => t._("Main contact"), + ]; + } +} diff --git a/src/db/Basket.hx b/src/db/Basket.hx new file mode 100644 index 0000000..1efcd89 --- /dev/null +++ b/src/db/Basket.hx @@ -0,0 +1,130 @@ +package db; +import sys.db.Object; +import sys.db.Types; + +/** + * Basket : represents the orders of a user for specific date + place + */ +//@:index(userId,placeId,ddate,unique) +class Basket extends Object +{ + public var id : SId; + public var cdate : SDateTime; //date when the order has been placed + public var num : SInt; //order number + + //TODO : link baskets to a multidistrib ID. + + //2018-07-21 fbarbut : we cannot use keys like this, because some distribution's place or date may change after orders are made. + //@:relation(userId) public var user : db.User; + //@:relation(placeId) public var place : db.Place; + //public var ddate : SDate; //date of the delivery + + public static var CACHE = new Map(); + + public function new(){ + super(); + cdate = Date.now(); + } + + public static function emptyCache(){ + CACHE = new Map(); + } + + public static function get(user:db.User, place:db.Place, date:Date, ?lock = false):db.Basket{ + + date = tools.DateTool.setHourMinute(date, 0, 0); + + //caching + // var k = user.id + "-" + place.id + "-" + date.toString().substr(0, 10); + // var b = CACHE.get(k); + var b = null; + // if (b == null){ + var md = MultiDistrib.get(date, place,db.Contract.TYPE_VARORDER); + var orders = md.getUserOrders(user); + + for( o in orders){ + if(o.basket!=null) { + b = o.basket; + break; + } + } + // CACHE.set(k, b); + // } + + return b; + } + + /** + * Get a Basket or create it if it doesn't exists. + * Also link existing orders to this basket + * @param user + * @param place + * @param date + */ + public static function getOrCreate(user, place, date){ + var b = get(user, place, date, true); + + date = tools.DateTool.setHourMinute(date, 0, 0); + + if (b == null){ + + //compute basket number + var md = MultiDistrib.get(date, place,db.Contract.TYPE_VARORDER); + + b = new Basket(); + b.num = md.getUsers().length + 1; + //TODO : should be more safe to do something like "b.num = MAX(num)+1 FROM Basket" + b.insert(); + + //try to find orders and link them to the basket + var dids = tools.ObjectListTool.getIds(md.distributions); + for ( o in db.UserContract.manager.search( ($distributionId in dids) && ($user == user), true)){ + o.basket = b; + o.update(); + } + } + return b; + } + + /** + * Get basket's orders + */ + public function getOrders(){ + return db.UserContract.manager.search($basket == this, false); + } + + /** + * Returns the list of operations which paid this basket + * @return + */ + public function getPayments():Iterable{ + + var op = getOrderOperation(false); + if (op == null){ + return []; + }else{ + return op.getRelatedPayments(); + } + } + + public function getOrderOperation(?onlyPending=true):db.Operation{ + + var order = getOrders().first(); + if(order==null) return null; + + var key = db.Distribution.makeKey(order.distribution.date, order.distribution.place); + return db.Operation.findVOrderTransactionFor(key, order.user, order.distribution.place.amap, onlyPending); + + } + + public function isValidated(){ + + var ordersPaid = Lambda.count(getOrders(), function(o) return !o.paid) == 0; + var op = getOrderOperation(false); + var orderOperationNotPending = op!=null ? op.pending == false : true; + var paymentOperationsNotPending = Lambda.count(getPayments(), function(p) return p.pending) == 0; + + return ordersPaid && orderOperationNotPending && paymentOperationsNotPending; + } + +} \ No newline at end of file diff --git a/src/db/Category.hx b/src/db/Category.hx new file mode 100755 index 0000000..6b94290 --- /dev/null +++ b/src/db/Category.hx @@ -0,0 +1,22 @@ +package db; +import sys.db.Types; +import Common; + +class Category extends sys.db.Object +{ + public var id : SId; + public var name :SString<128>; + + @:relation(categoryGroupId) public var categoryGroup:db.CategoryGroup; + + /** + * get category color in hexa + */ + public function getColor():String { + return App.current.view.intToHex(db.CategoryGroup.COLORS[categoryGroup.color]); + } + + public function infos():CategoryInfo{ + return {id:id, name:name, /*parent:categoryGroup.id, /*pinned:categoryGroup.pinned*/}; + } +} \ No newline at end of file diff --git a/src/db/CategoryGroup.hx b/src/db/CategoryGroup.hx new file mode 100755 index 0000000..8f7d4c2 --- /dev/null +++ b/src/db/CategoryGroup.hx @@ -0,0 +1,47 @@ +package db; +import sys.db.Types; + + +class CategoryGroup extends sys.db.Object +{ + public var id : SId; + public var name : SString<128>; + public var color : STinyInt; //color id + public var pinned : SBool; //if true, the products tagged with these categories will be pinned on top of the shop. + + @:relation(amapId) public var amap:db.Amap; + + @:skip public static var COLORS = [ + 0x7BAD1C, //vert clair + 0x007700, //vert foncé + 0x583816, //marron + 0xD97801, //orange carotte + 0xB1933D, //sable + 0xC91F25, //rouge + 0x6F0D2E, //vin + 0x616161, //gris + ]; + + public function new(){ + super(); + pinned = false; + color = 7; + } + + public function getCategories() { + return db.Category.manager.search($categoryGroup == this, false); + } + + public static function get(amap:db.Amap):List { + return manager.search($amap == amap, false); + } + + public static function getLabels(){ + var t = sugoi.i18n.Locale.texts; + return [ + "name" => t._("Category group name"), + "pinned" => t._("Pinned on top"), + "color" => t._("Color"), + ]; + } +} \ No newline at end of file diff --git a/src/db/Contract.hx b/src/db/Contract.hx new file mode 100755 index 0000000..e4d6421 --- /dev/null +++ b/src/db/Contract.hx @@ -0,0 +1,285 @@ +package db; +import sugoi.form.ListData.FormData; +import sys.db.Object; +import sys.db.Types; + +enum ContractFlags { + UsersCanOrder; //adhérents peuvent saisir eux meme la commande en ligne + StockManagement; //gestion des commandes + PercentageOnOrders; //calcul d'une commission supplémentaire + + //LogisticMgmt; //gestion logistique + //SubGroups; //sous groupes pour commandes groupées + //InviteFriends; //peut inviter des amis à participer à la commande + +} + +/** + * Contract + * + * un contrat réunissant pluseiurs produits d'un meme fournisseur + * qui sont livrés au meme endroit et meme moment; + * + */ +class Contract extends Object +{ + + public var id : SId; + public var name : SString<64>; + + //responsable + @formPopulate("populate") @:relation(userId) public var contact : SNull; + @formPopulate("populateVendor") @:relation(vendorId) public var vendor : Vendor; + + public var startDate:SDate; + public var endDate :SDate; + + public var description:SNull; + + @:relation(amapId) public var amap:Amap; + public var distributorNum:STinyInt; + public var flags : SFlags; + + public var percentageValue : SNull; //fees percentage + public var percentageName : SNull>; //fee name + + public var type : SInt; + @:skip public static var TYPE_CONSTORDERS = 0; //CSA contract + @:skip public static var TYPE_VARORDER = 1; //varying orders contract + + @:skip var cache_hasActiveDistribs : Bool; + + public function new() + { + super(); + flags = cast 0; + distributorNum = 0; + + } + + /** + * The products can be ordered currently ? + * + * @deprecated it depends on distributions + */ + public function isUserOrderAvailable():Bool { + + if (type == TYPE_CONSTORDERS ) { + return isVisibleInShop(); + }else { + + //if ( cache_hasActiveDistribs != null ) return cache_hasActiveDistribs; + + //for varying orders, we need to know if there are some available deliveries + var n = Date.now(); + var d = db.Distribution.manager.count( $orderStartDate <= n && $orderEndDate >= n && $contractId==this.id); + + //tmp : add the "old" deliveries which have a null orderStartDate + //d += db.Distribution.manager.count( $orderStartDate == null && $date > n && $contractId == this.id ); + + //cache_hasActiveDistribs = d > 0; + //return cache_hasActiveDistribs && isVisibleInShop(); + return d>0 && isVisibleInShop(); + } + + } + + /** + * The products can be displayed in a shop ? + */ + public function isVisibleInShop():Bool { + + //yes if the contract is active and the 'UsersCanOrder' flag is checked + var n = Date.now().getTime(); + return flags.has(UsersCanOrder) && n < this.endDate.getTime() && n > this.startDate.getTime(); + } + + /** + * is currently open to orders + */ + public function hasRunningOrders(){ + var now = Date.now(); + var n = now.getTime(); + + var contractOpen = flags.has(UsersCanOrder) && n < this.endDate.getTime() && n > this.startDate.getTime(); + var d = db.Distribution.manager.count( $orderStartDate <= now && $orderEndDate > now && $contractId==this.id); + + return contractOpen && d > 0; + } + + + public function hasPercentageOnOrders():Bool { + return flags.has(PercentageOnOrders) && percentageValue!=null && percentageValue!=0; + } + + public function hasStockManagement():Bool { + return flags.has(StockManagement); + } + + /** + * computes a 'percentage' fee or a 'margin' fee + * depending on the group settings + * + * @param basePrice + */ + public function computeFees(basePrice:Float) { + if (!hasPercentageOnOrders()) return 0.0; + + if (amap.flags.has(ComputeMargin)) { + //commercial margin + return (basePrice / ((100 - percentageValue) / 100)) - basePrice; + + }else { + //add a percentage + return percentageValue / 100 * basePrice; + } + } + + /** + * + * @param amap + * @param large = false Si true, montre les contrats terminés depuis moins d'un mois + * @param lock = false + */ + public static function getActiveContracts(amap:Amap,?large = false, ?lock = false) { + var now = Date.now(); + var end = Date.now(); + + if (large) { + end = DateTools.delta(end , -1000.0 * 60 * 60 * 24 * 30); + return db.Contract.manager.search($amap == amap && $endDate > end,{orderBy:-vendorId}, lock); + }else { + return db.Contract.manager.search($amap == amap && $endDate > now && $startDate < now,{orderBy:-vendorId}, lock); + } + } + + /** + * get products in this contract + * @param onlyActive = true + * @return + */ + public function getProducts(?onlyActive = true):List { + if (onlyActive) { + return Product.manager.search($contract==this && $active==true,{orderBy:name},false); + }else { + return Product.manager.search($contract==this,{orderBy:name},false); + } + } + + /** + * get a few products to display + * @param limit = 6 + */ + public function getProductsPreview(?limit = 6){ + return Product.manager.search($contract==this && $active==true,{limit:limit,orderBy:-id},false); + } + + + /** + * get users who have orders in this contract ( including user2 ) + * @return Array + */ + public function getUsers():Array { + var pids = getProducts().map(function(x) return x.id); + var ucs = UserContract.manager.search($productId in pids, false); + var ucs2 = []; + for( uc in ucs) { + ucs2.push(uc.user); + if(uc.user2!=null) ucs2.push(uc.user2); + } + + //comme un user peut avoir plusieurs produits au sein d'un contrat, il faut dédupliquer cette liste + var out = new Map(); + for (u in ucs2) { + out.set(u.id, u); + } + + return Lambda.array(out); + } + + /** + * Get all orders of this contract + * @param d A delivery is needed for varying orders contract + * @return + */ + public function getOrders(?d:db.Distribution):Array { + if (type == TYPE_VARORDER && d == null) throw "This type of contract must have a delivery"; + + //get product ids, some of the products may have been disabled but we keep the order + var pids = getProducts(false).map(function(x) return x.id); + var ucs = new List(); + if (type == TYPE_VARORDER) { + ucs = UserContract.manager.search( ($productId in pids) && $distribution==d,{orderBy:userId}, false); + }else { + ucs = UserContract.manager.search( ($productId in pids) ,{orderBy:userId}, false); + } + return Lambda.array(ucs); + } + + /** + * Get orders for a user. + * + * @param d + * @return + */ + public function getUserOrders(u:db.User,?d:db.Distribution):Array { + if (type == TYPE_VARORDER && d == null) throw "This type of contract must have a delivery"; + + var pids = getProducts(false).map(function(x) return x.id); + var ucs = new List(); + if (d != null && d.contract.type==db.Contract.TYPE_VARORDER) { + ucs = UserContract.manager.search( ($productId in pids) && $distribution==d && ($user==u || $user2==u ), false); + }else { + ucs = UserContract.manager.search( ($productId in pids) && ($user==u || $user2==u ),false); + } + return Lambda.array(ucs); + } + + public function getDistribs(excludeOld = true,?limit=999):List { + if (excludeOld) { + //still include deliveries which just expired in last 24h + return Distribution.manager.search($end > DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24) && $contract == this, { orderBy:date,limit:limit } ); + }else{ + return Distribution.manager.search( $contract == this, { orderBy:date,limit:limit } ); + } + } + + override function toString() { + return name+" du "+this.startDate.toString().substr(0,10)+" au "+this.endDate.toString().substr(0,10); + } + + public function populate() { + return App.current.user.amap.getMembersFormElementData(); + } + + /** + * get a vendor list as form data + * @return + */ + public function populateVendor():FormData{ + var vendors = Vendor.manager.search($amap == App.current.user.amap, false); + var out = []; + for (v in vendors) { + out.push({label:v.name, value:v.id }); + } + return out; + } + + public static function getLabels(){ + var t = sugoi.i18n.Locale.texts; + return [ + "name" => t._("Contract name"), + "startDate" => t._("Start date"), + "endDate" => t._("End date"), + "description" => t._("Description"), + "distributorNum" => t._("Number of required distributors (0 to 4)"), + "flags" => t._("Options"), + "percentageValue" => t._("Fees percentage"), + "percentageName" => t._("Fees label"), + "contact" => t._("Contact"), + "vendor" => t._("Farmer"), + ]; + } + + +} \ No newline at end of file diff --git a/src/db/Distribution.hx b/src/db/Distribution.hx new file mode 100755 index 0000000..7bf13eb --- /dev/null +++ b/src/db/Distribution.hx @@ -0,0 +1,271 @@ +package db; +import sugoi.form.ListData; +import sys.db.Object; +import sys.db.Types; + +/** + * Distrib + */ +class Distribution extends Object +{ + public var id : SId; + + @:relation(contractId) + public var contract : Contract; + + @formPopulate("placePopulate") + @:relation(placeId) + public var place : Place; + + //when orders are open + @hideInForms public var orderStartDate : SNull; + @hideInForms public var orderEndDate : SNull; + + //start and end date for delivery + public var date : SDateTime; + public var end : SDateTime; + + @:relation(distributionCycleId) public var distributionCycle : SNull; + + @formPopulate("populate") @:relation(distributor1Id) public var distributor1 : SNull; + @formPopulate("populate") @:relation(distributor2Id) public var distributor2 : SNull; + @formPopulate("populate") @:relation(distributor3Id) public var distributor3 : SNull; + @formPopulate("populate") @:relation(distributor4Id) public var distributor4 : SNull; + + @hideInForms public var validated :SBool; + + public static var DISTRIBUTION_VALIDATION_LIMIT = 10; + + public function new() + { + super(); + date = Date.now(); + end = DateTools.delta(date, 1000 * 60 * 90); + validated = false; + } + + /** + * get group members list as form data + */ + public function populate():FormData { + if(App.current.user!=null && App.current.user.getAmap()!=null){ + return App.current.user.getAmap().getMembersFormElementData(); + }else{ + return []; + } + + } + + /** + * get groups places as form data + * @return + */ + public function placePopulate():FormData { + var out = []; + var places = new List(); + if(this.contract!=null){ + //edit form + places = db.Place.manager.search($amapId == this.contract.amap.id, false); + }else{ + //insert form + places = db.Place.manager.search($amapId == App.current.user.amap.id, false); + } + + for (p in places) out.push( { label:p.name,value:p.id} ); + return out; + } + + public function hasEnoughDistributors() { + var n = contract.distributorNum; + + var d = 0; + if (distributor1 != null) d++; + if (distributor2 != null) d++; + if (distributor3 != null) d++; + if (distributor4 != null) d++; + + return (d >= n) ; + } + + public function isDistributor(u:User) { + if (u == null) return false; + return (distributor1!=null && u.id == distributor1.id) || + (distributor2!=null && u.id == distributor2.id) || + (distributor3!=null && u.id == distributor3.id) || + (distributor4!=null && u.id == distributor4.id); + } + + /** + * String to identify this distribution (debug use only) + */ + override public function toString() { + return "#" + id + " Delivery " + date.toString() + " of " + contract.name; + } + + public function getOrders() { + + if ( this.contract.type == Contract.TYPE_CONSTORDERS){ + var pids = db.Product.manager.search($contract == this.contract, false); + var pids = Lambda.map(pids, function(x) return x.id); + return UserContract.manager.search( ($productId in pids), false); + }else{ + return UserContract.manager.search($distribution == this, false); + } + } + + public function getUserOrders(user:db.User){ + if ( this.contract.type == Contract.TYPE_CONSTORDERS){ + var pids = db.Product.manager.search($contract == this.contract, false); + var pids = Lambda.map(pids, function(x) return x.id); + return UserContract.manager.search( (($productId in pids) && ($user==user || $user2==user) ), false); + }else{ + return UserContract.manager.search($distribution == this && $user==user, false); + } + + } + + public function getUsers():Iterable{ + + return tools.ObjectListTool.deduplicate( Lambda.map(getOrders(), function(x) return x.user ) ); + + } + + + /** + * Get TTC turnover for this distribution + */ + public function getTurnOver(){ + + var sql = "select SUM(quantity * productPrice) from UserContract where productId IN (" + tools.ObjectListTool.getIds(contract.getProducts()).join(",") +") "; + if (contract.type == db.Contract.TYPE_VARORDER) { + sql += " and distributionId=" + this.id; + } + + return sys.db.Manager.cnx.request(sql).getFloatResult(0); + } + + /** + * Get HT turnover for this distribution + */ + public function getHTTurnOver(){ + + var pids = tools.ObjectListTool.getIds(contract.getProducts(false)); + + var sql = "select SUM(uc.quantity * (p.price/(1+p.vat/100)) ) from UserContract uc, Product p "; + sql += "where uc.productId IN (" + pids.join(",") +") "; + sql += "and p.id=uc.productId "; + + if (contract.type == db.Contract.TYPE_VARORDER) { + sql += " and uc.distributionId=" + this.id; + } + + return sys.db.Manager.cnx.request(sql).getFloatResult(0); + } + + /** + * + */ + public function canOrderNow() { + + if (orderEndDate == null) { + return this.contract.isUserOrderAvailable(); + }else { + var n = Date.now().getTime(); + var f = this.contract.flags.has(UsersCanOrder); + + return f && n < orderEndDate.getTime() && n > orderStartDate.getTime(); + + } + } + + /** + * Get next multi-devliveries + * ( deliveries including more than one vendors ) + */ + /*public static function getNextMultiDeliveries(){ + + var out = new Map}>(); + return Lambda.array(manager.search($orderStartDate <= Date.now() && $orderEndDate >= Date.now() && $contract==contract,false)); + + var now = Date.now(); + + var contracts = Contract.getActiveContracts(App.current.user.amap); + var cids = Lambda.map(contracts, function(p) return p.id); + + //available deliveries + some of the next deliveries + + var distribs = db.Distribution.manager.search(($contractId in cids) && $orderEndDate >= now, { orderBy:date }, false); + var inOneMonth = DateTools.delta(now, 1000.0 * 60 * 60 * 24 * 30); + for (d in distribs) { + + var o = out.get(d.getKey()); + if (o == null) o = {place:d.place, startDate:d.date,active:null, endDate:d.end, products:[]}; + for ( p in d.contract.getProductsPreview(8)){ + if (o.products.length >= 8) break; + o.products.push( p.infos() ); + } + + if (d.orderStartDate.getTime() <= now.getTime() ){ + //order currently open + o.active = true; + }else if (d.orderStartDate.getTime() <= inOneMonth.getTime() ){ + //open soon + o.active = false; + }else{ + continue; + + } + + out.set(d.getKey(), o); + } + return Lambda.array(out); + }*/ + + override public function update(){ + this.end = new Date(this.date.getFullYear(), this.date.getMonth(), this.date.getDate(), this.end.getHours(), this.end.getMinutes(), 0); + super.update(); + } + + + + /** + * Get open to orders deliveries + * @param contract + */ + public static function getOpenToOrdersDeliveries(contract:db.Contract){ + + return Lambda.array(manager.search($orderStartDate <= Date.now() && $orderEndDate >= Date.now() && $contract==contract,{orderBy:date},false)); + + } + + + + /** + * Return a string like $placeId-$date. + * + * It's an ID representing all the distributions happening on that day at that place. + */ + public function getKey():String{ + return db.Distribution.makeKey(this.date, this.place); + } + + public static function makeKey(date, place){ + return date.toString().substr(0, 10) +"|"+Std.string(place.id); + } + + + public static function getLabels(){ + var t = sugoi.i18n.Locale.texts; + return [ + "date" => t._("Date"), + "endDate" => t._("End hour"), + "place" => t._("Place"), + "distributor1" => t._("Distributor #1"), + "distributor2" => t._("Distributor #2"), + "distributor3" => t._("Distributor #3"), + "distributor4" => t._("Distributor #4"), + ]; + } + + +} \ No newline at end of file diff --git a/src/db/DistributionCycle.hx b/src/db/DistributionCycle.hx new file mode 100755 index 0000000..c3fa9ee --- /dev/null +++ b/src/db/DistributionCycle.hx @@ -0,0 +1,54 @@ +package db; +import sys.db.Object; +import sys.db.Types; +using tools.DateTool; + +enum CycleType { + Weekly; + Monthly; + BiWeekly; + TriWeekly; +} + +/** + * Distribution cycle + */ +class DistributionCycle extends Object +{ + public var id : SId; + @:relation(contractId) public var contract : Contract; + public var cycleType:SEnum; + public var startDate : SDate; //debut + public var endDate : SDate; //fin de la recurrence + public var startHour : SDateTime; + public var endHour : SDateTime; + public var daysBeforeOrderStart:SNull; + public var daysBeforeOrderEnd:SNull; + public var openingHour:SNull; + public var closingHour:SNull; + @formPopulate("placePopulate") @:relation(placeId) public var place : Place; + + public function new() { + super(); + } + + public static function getLabels(){ + var t = sugoi.i18n.Locale.texts; + return [ + "cycleType" => t._("Fréquence"), + "startDate" => t._("Date de début"), + "endDate" => t._("Date de fin"), + "daysBeforeOrderStart" => t._("Ouverture de commande (nbre de jours avant distribution)"), + "daysBeforeOrderEnd" => t._("Fermeture de commande (nbre de jours avant distribution)"), + "place" => t._("Place"), + ]; + } + + public function placePopulate():Array<{label:String,value:Int}> { + var out = []; + if ( App.current.user == null || App.current.user.amap == null ) return out; + var places = db.Place.manager.search($amapId == App.current.user.amap.id, false); + for (p in places) out.push( { label:p.name,value :p.id } ); + return out; + } +} \ No newline at end of file diff --git a/src/db/Graph.hx b/src/db/Graph.hx new file mode 100644 index 0000000..9c236ae --- /dev/null +++ b/src/db/Graph.hx @@ -0,0 +1,45 @@ +package db; +import sys.db.Object; +import sys.db.Types; + + +/** + * Tool to Graph daily values + */ +@:id(key,date) +class Graph extends Object{ + + public var key:SString<128>; + public var date:SDate; + public var value:SFloat; + + /** + * record a value + */ + public static function record(key:String,value:Float,?date:Date){ + if(date==null) date = Date.now(); + date = new Date(date.getFullYear(),date.getMonth(),date.getDate(),0,0,0); + var o = manager.select($key==key && $date==date,true); + if(o == null){ + o = new Graph(); + o.date = date; + o.key = key; + o.value = value; + o.insert(); + }else{ + o.value = value; + o.update(); + } + return o; + } + + /** + * Get a list of records in a time frame + */ + public static function getRange(key:String,from:Date,to:Date):Array{ + return Lambda.array(manager.search($key==key && $date>=from && $date<=to, false)); + } + + + +} \ No newline at end of file diff --git a/src/db/Membership.hx b/src/db/Membership.hx new file mode 100755 index 0000000..4f8574a --- /dev/null +++ b/src/db/Membership.hx @@ -0,0 +1,24 @@ +package db; +import sys.db.Object; +import sys.db.Types; + +@:id(userId,amapId,year) +class Membership extends Object +{ + @:relation(amapId) + public var amap : Amap; + + @:relation(userId) + public var user : db.User; + + //année de cotisation (année la plus ancienne si a cheval sur deux années : 2014-2015 -> 2014) + public var year : Int; + public var date : SNull; + + public static function get(user:User, amap:Amap,year:Int, ?lock = false) { + return manager.select($user == user && $amap == amap && $year == year, lock); + } + + + +} \ No newline at end of file diff --git a/src/db/Message.hx b/src/db/Message.hx new file mode 100755 index 0000000..06eda73 --- /dev/null +++ b/src/db/Message.hx @@ -0,0 +1,28 @@ +package db; +import sys.db.Object; +import sys.db.Types; +import tink.core.Noise; +import tink.core.Outcome; +import sugoi.mail.IMailer; + + +/** + * Message sent from the message Section + */ +class Message extends Object +{ + + public var id : SId; + @:relation(amapId) public var amap : SNull; + @:relation(senderId) public var sender : SNull; + + public var recipientListId : SNull>; + public var recipients : SNull>>; + + public var title : SString<128>; + public var body : SText; + public var date : SDateTime; + + + +} \ No newline at end of file diff --git a/src/db/Operation.hx b/src/db/Operation.hx new file mode 100644 index 0000000..dfae03c --- /dev/null +++ b/src/db/Operation.hx @@ -0,0 +1,431 @@ +package db; +import sys.db.Types; +import tink.core.Error; + +enum OperationType{ + VOrder; //order on a varying order contract + COrder;//order on a constant order contract + Payment; + Membership; +} + +typedef PaymentInfos = {type:String, ?remoteOpId:Int, ?netAmount:Float}; +typedef VOrderInfos = {basketId:Int}; +typedef COrderInfos = {contractId:Int}; + + +/** + * Payment operation + * + * @author fbarbut + */ +class Operation extends sys.db.Object +{ + public var id : SId; + public var name : SString<128>; + public var amount : SFloat; + public var date : SDateTime; + public var type : SEnum; + public var data : SData; + @hideInForms @:relation(relationId) public var relation : SNull; //linked to another operation : ie a payment pays an order + + @formPopulate("populate") @:relation(userId) public var user : db.User; + @hideInForms @:relation(groupId) public var group : db.Amap; + + public var pending : SBool; //a pending payment means the payment has not been confirmed, a pending order means the ordre can still change before closing. + + public function getTypeIndex(){ + var e : OperationType = type; + return e.getIndex(); + } + + public function new(){ + super(); + pending = false; + } + + /** + * if operation is a payment, give the payment type + */ + public function getPaymentType():String{ + switch(type){ + case Payment: + var x : PaymentInfos = this.data; + if (data == null){ + return null; + }else{ + return x.type; + } + default : return null; + } + } + + /** + * get translated payment type name + */ + public function getPaymentTypeName(){ + var t = getPaymentType(); + if (t == null) return null; + for ( pt in service.PaymentService.getAllPaymentTypes()){ + if (pt.type == t) return pt.name; + } + return null; + } + + /** + * get payments linked to this order transaction + */ + public function getRelatedPayments(){ + return db.Operation.manager.search($relation == this, false); + } + + public function getOrderInfos(){ + return switch(type){ + case COrder, VOrder : this.data; + default : null; + } + } + + public function getPaymentInfos():PaymentInfos{ + return switch(type){ + case Payment : this.data; + default : null; + } + } + + public static function countOperations(user:db.User, group:db.Amap):Int{ + return manager.count($user == user && $group == group); + } + + /** + * get all user operations + * @param user - + * @param group - + * @param reverse=false - + */ + public static function getOperations(user:db.User, group:db.Amap,?reverse=false ){ + if(reverse) { + return manager.search($user == user && $group == group,{orderBy:-date},false); + } + return manager.search($user == user && $group == group,{orderBy:date},false); + } + + public static function getOperationsWithIndex(user:db.User, group:db.Amap,index:Int,limit:Int,?reverse=false ){ + if(reverse) { + return manager.search($user == user && $group == group, { limit:[index,limit], orderBy:-date }, false); + } + return manager.search($user == user && $group == group, { limit:[index,limit], orderBy:date },false); + } + + /*public static function getOrder_Operations(user:db.User, group:db.Amap,?limit=50 ){ + //return manager.search($user == user && $group == group && $type!=Payment,{orderBy:date},false); + //return manager.search($user == user && $group == group && $relation==null,{orderBy:date},false); + return manager.search($user == user && $group == group,{orderBy:date,limit:limit},false); + }*/ + + public static function getPaymentOperations(user:db.User, group:db.Amap,?limit=50){ + return manager.search($user == user && $group == group && $type == Payment, {orderBy:date,limit:limit},false); + } + + public static function getLastOperations(user:db.User, group:db.Amap, ?limit = 50){ + + var c = manager.count($user == user && $group == group); + c -= limit; + if (c < 0) c = 0; + return manager.search($user == user && $group == group,{orderBy:date,limit:[c,limit]},false); + } + + /** + * Create a new transaction + * @param orders + */ + public static function makeOrderOperation(orders: Array, ?basket:db.Basket){ + + if (orders == null) throw "orders are null"; + if (orders.length == 0) throw "no orders"; + if (orders[0].user == null ) throw "no user in order"; + var t = sugoi.i18n.Locale.texts; + + var _amount = 0.0; + for ( o in orders ){ + var t = o.quantity * o.productPrice; + _amount += t + t * (o.feesRate / 100); + } + + var contract = orders[0].product.contract; + + var op = new db.Operation(); + var user = orders[0].user; + var group = orders[0].product.contract.amap; + + if (contract.type == db.Contract.TYPE_CONSTORDERS){ + //Constant orders + var dNum = contract.getDistribs(false).length; + op.name = "" + contract.name + " (" + contract.vendor.name+") " + dNum + " " + t._("deliveries"); + op.amount = dNum * (0 - _amount); + op.date = Date.now(); + op.type = COrder; + var data : COrderInfos = {contractId:contract.id}; + op.data = data; + op.user = user; + op.group = group; + op.pending = true; + + }else{ + + if (basket == null) throw "varying contract orders should have a basket"; + + //varying orders + var date = App.current.view.dDate(orders[0].distribution.date); + op.name = t._("Order for ::date::",{date:date}); + op.amount = 0 - _amount; + op.date = Date.now(); + op.type = VOrder; + var data : VOrderInfos = {basketId:basket.id}; + op.data = data; + op.user = user; + op.group = group; + op.pending = true; + } + + op.insert(); + + service.PaymentService.updateUserBalance(op.user, op.group); + + return op; + } + + /** + * update an order operation + */ + public static function updateOrderOperation(op:db.Operation, orders: Array, ?basket:db.Basket){ + + op.lock(); + var t = sugoi.i18n.Locale.texts; + + var _amount = 0.0; + for ( o in orders ){ + var a = o.quantity * o.productPrice; + _amount += a + a * (o.feesRate / 100); + } + + var contract = orders[0].product.contract; + if (contract.type == db.Contract.TYPE_CONSTORDERS){ + //Constant orders + var dNum = contract.getDistribs(false).length; + op.name = "" + contract.name + " (" + contract.vendor.name+") "+ dNum + " " + t._("deliveries"); + op.amount = dNum * (0 - _amount); + }else{ + + if (basket == null) throw "varying contract orders should have a basket"; + op.amount = 0 - _amount; + } + + //op.date = Date.now(); //leave original date + op.update(); + service.PaymentService.updateUserBalance(op.user, op.group); + return op; + } + + /** + * Record a new payment operation + * @param type + * @param amount + * @param name + * @param relation + */ + public static function makePaymentOperation(user:db.User,group:db.Amap,type:String, amount:Float, name:String, ?relation:db.Operation ){ + + var t = new db.Operation(); + t.amount = Math.abs(amount); + t.date = Date.now(); + t.name = name; + t.group = group; + t.pending = true; + t.user = user; + t.type = Payment; + var data : PaymentInfos = {type:type}; + t.data = data; + if(relation!=null) t.relation = relation; + t.insert(); + + service.PaymentService.updateUserBalance(user, group); + + return t; + } + + /** + * when updating a (varying) order , we need to update the existing pending transaction + */ + public static function findVOrderTransactionFor(dkey:String, user:db.User, group:db.Amap,?onlyPending=true):db.Operation{ + + //throw 'find $dkey for user ${user.id} in group ${group.id} , onlyPending:$onlyPending'; + + var date = dkey.split("|")[0]; + var placeId = Std.parseInt(dkey.split("|")[1]); + var transactions = new List(); + if (onlyPending){ + transactions = manager.search($user == user && $group == group && $pending == true && $type==VOrder , {orderBy:-date}, true); + }else{ + transactions = manager.search($user == user && $group == group && $type==VOrder , {orderBy:-date}, true); + } + + //throw transactions; + + var place = db.Place.manager.get(placeId,false); + var date = Date.fromString(date); + var basket = db.Basket.get(user, place, date); + if(basket==null) throw new Error('No basket found for user #'+user.id+', place #'+place.id+', date '+date); + + for ( t in transactions){ + switch(t.type){ + case VOrder : + var data : VOrderInfos = t.data; + if ( data == null) continue; + if (data.basketId == basket.id) return t; + default : + continue; + } + } + + return null; + } + + /** + * when updating a constant order, we need to update the existing operation. + */ + public static function findCOrderTransactionFor(contract:db.Contract, user:db.User):db.Operation{ + + if (contract.type != db.Contract.TYPE_CONSTORDERS) throw "contract type should be TYPE_CONSTORDERS"; + + var transactions = manager.search($user == user && $group == contract.amap && $amount<=0 && $type==COrder, {orderBy:date,limit:100}, true); + + for ( t in transactions){ + + switch(t.type){ + + case COrder : + + //var id = Lambda.find(orders, function(x) return db.UserContract.manager.get(x, false) != null); + //if (id == null) { + ////all orders in this transaction dont exists anymore + //t.delete(); + //continue; + //}else{ + //for ( i in orders){ + //var order = db.UserContract.manager.get(i); + //if (order == null) continue; + //if (order.product.contract.id == contract.id) return t; + //} + //} + var data : COrderInfos = t.data; + if (data == null) continue; + if (data.contractId == contract.id) return t; + + + default : + continue; + } + } + + return null; + + } + + /** + * Create/update the needed order operations and returns the related operations + * @param orders + */ + public static function onOrderConfirm(orders:Array):Array{ + + if (orders.length == 0) return null; + if (orders[0] == null) return null; + + var out = []; + var user = orders[0].user; + var group = orders[0].product.contract.amap; + + //should not go further if group has not activated payements + if (user==null || !group.hasPayments()) return null; + + //we consider that ALL orders are from the same contract type : varying or constant + if (orders[0].product.contract.type == db.Contract.TYPE_VARORDER ){ + + // varying contract : + //manage separatly orders which occur at different dates + var ordersGroup = tools.ObjectListTool.groupOrdersByKey(orders); + + for ( orders in ordersGroup){ + + //find basket + var basket = null; + for ( o in orders) { + if (o.basket != null) { + basket = o.basket; + break; + } + } + + //get all orders for the same multidistrib, in order to update related operation. + var k = orders[0].distribution.getKey(); + var allOrders = db.UserContract.getUserOrdersByMultiDistrib(k, user, group); + + //existing transaction + var existing = db.Operation.findVOrderTransactionFor( k , user, group, false); + + var op; + if (existing != null){ + op = db.Operation.updateOrderOperation(existing,allOrders,basket); + }else{ + op = db.Operation.makeOrderOperation(allOrders,basket); + } + out.push(op); + + //delete order and payment operations if sum of orders qt is 0 + /*var sum = 0.0; + for ( o in allOrders) sum += o.quantity; + if ( sum == 0 ) { + existing.delete(); + op.delete(); + }*/ + } + + }else{ + + // constant contract + // create/update a transaction computed like $distribNumber * $price. + var contract = orders[0].product.contract; + + var existing = db.Operation.findCOrderTransactionFor( contract , user); + if (existing != null){ + out.push( db.Operation.updateOrderOperation(existing, contract.getUserOrders(user) ) ); + }else{ + out.push( db.Operation.makeOrderOperation( contract.getUserOrders(user) ) ); + } + } + + + + return out; + + } + + public function populate(){ + return App.current.user.getAmap().getMembersFormElementData(); + } + + /*public static function getLabels(){ + var t = sugoi.i18n.Locale.texts; + return [ + "name" => t._("Text"), + "date" => t._("Date"), + "endDate" => t._("End date"), + "place" => t._("Place"), + "distributor1" => t._("Distributor #1"), + "distributor2" => t._("Distributor #2"), + "distributor3" => t._("Distributor #3"), + "distributor4" => t._("Distributor #4"), + ]; + }*/ + +} \ No newline at end of file diff --git a/src/db/Place.hx b/src/db/Place.hx new file mode 100755 index 0000000..7e4c481 --- /dev/null +++ b/src/db/Place.hx @@ -0,0 +1,87 @@ +package db; +import sys.db.Object; +import sys.db.Types; +import Common; + +class Place extends Object +{ + + public var id : SId; + public var name : SString<64>; + public var address1:SNull>; + public var address2:SNull>; + public var zipCode:SString<32>; + public var city:SString<64>; + public var country:SNull>; + + //latitude/longitude + public var lat:SNull; + public var lng:SNull; + + @hideInForms @:relation(amapId) public var amap : Amap; + + public function new() + { + super(); + country = "France"; + } + + override function toString() { + if (name == null) { + return "place"; + }else { + return name; + } + } + + public function getFullAddress(){ + var str = new StringBuf(); + str.add(name+", \n"); + if (address1 != null) str.add(address1 + ", \n"); + if (address2 != null) str.add(address2 + ", \n"); + if (zipCode != null) str.add(zipCode); + if (city != null) str.add(" - "+city); + return str.toString(); + } + + /** + * get adress without 'name' field. + */ + public function getAddress(){ + var str = new StringBuf(); + if (address1 != null) str.add(address1 + ", \n"); + if (address2 != null) str.add(address2 + ", \n"); + if (zipCode != null) str.add(zipCode); + if (city != null) str.add(" - "+city); + if(country != null) str.add(", "+country); + return str.toString(); + } + + public static function getLabels():Map{ + var t = sugoi.i18n.Locale.texts; + return [ + "name" => t._("Name"), + "address1" => t._("Address 1"), + "address2" => t._("Address 2"), + "zipCode" => t._("Zip code"), + "city" => t._("City"), + "country" => t._("Country"), + "lat" => t._("Latitude"), + "lng" => t._("Longitude"), + ]; + } + + public function getInfos():PlaceInfos{ + return { + id:id, + name:name, + address1:address1, + address2:address2, + zipCode:zipCode, + city:city, + latitude : lat, + longitude: lng + } + } + +} \ No newline at end of file diff --git a/src/db/Product.hx b/src/db/Product.hx new file mode 100755 index 0000000..811ee5e --- /dev/null +++ b/src/db/Product.hx @@ -0,0 +1,206 @@ +package db; +import sys.db.Object; +import sys.db.Types; +import Common; + +/** + * Product + */ +class Product extends Object +{ + public var id : SId; + public var name : SString<128>; + public var ref : SNull>; //référence produit + + @:relation(contractId) + public var contract : Contract; + + //prix TTC + public var price : SFloat; + public var vat : SFloat; + + public var desc : SNull; + public var stock : SNull; //if qantity can be float, stock should be float + + public var unitType : SNull>; // Kg / L / g / units + public var qt : SNull; + + public var organic : SBool; + public var variablePrice : Bool; //price can vary depending on weighting of the product + public var multiWeight : Bool; //product cannot be cumulated in one order record + + //https://docs.google.com/document/d/1IqHN8THT6zbKrLdHDClKZLWgKWeL0xw6cYOiFofw04I/edit + @hideInForms public var wholesale : Bool; //this product is a wholesale product (crate,bag,pallet) + @hideInForms public var retail : Bool; //this products is a fraction of a wholesale product + @hideInForms public var bulk : Bool; //(vrac) warn the customer this product is not packaged + public var hasFloatQt:SBool; //this product can be ordered in "float" quantity + + @hideInForms @:relation(imageId) public var image : SNull; + @:relation(txpProductId) public var txpProduct : SNull; //taxonomy + + + public var active : SBool; //if false, product disabled, not visible on front office + + + public function new() + { + super(); + //type = 0; + organic = false; + hasFloatQt = false; + active = true; + variablePrice = false; + multiWeight = false; + wholesale = false; + retail = false; + bulk = false; + vat = 5.5; + unitType = Unit.Piece; + qt = 1; + + } + + /** + * Returns product image URL + */ + public function getImage() { + if (image == null) { + if (txpProduct != null){ + return "/img/taxo/cat" + txpProduct.category.id + ".png"; + }else{ + return "/img/unknown.png"; + } + }else { + return App.current.view.file(image); + } + } + + public function getName(){ + + if (unitType != null && qt != null && qt != 0){ + return name +" " + qt + " " + App.current.view.unit(unitType); + }else{ + return name; + } + } + + override function toString() { + return getName(); + } + + /** + * get price including margins + */ + public function getPrice():Float{ + return price + contract.computeFees(price); + } + + /** + get product infos as an anonymous object + @param CategFromTaxo=false + @param populateCategories=tru + @return + **/ + public function infos(?CategFromTaxo=false,?populateCategories=true,?distribution:db.Distribution):ProductInfo { + var o :ProductInfo = { + id : id, + ref : ref, + name : name, + image : getImage(), + contractId : contract.id, + price : getPrice(), + vat : vat, + vatValue: (vat != 0 && vat != null) ? ( this.price - (this.price / (vat/100+1)) ) : null, + contractTax : contract.percentageValue, + contractTaxName : contract.percentageName, + desc : App.current.view.nl2br(desc), + categories : null, + subcategories:null, + orderable : this.contract.isUserOrderAvailable(), + stock : contract.hasStockManagement() ? this.stock : null, + hasFloatQt : hasFloatQt, + qt:qt, + unitType:unitType, + organic:organic, + variablePrice:variablePrice, + wholesale:wholesale, + active: active, + distributionId : distribution==null ? null : distribution.id, + } + + if(populateCategories){ + if (CategFromTaxo){ + o.categories = [txpProduct == null?null:txpProduct.category.id]; + o.subcategories = [txpProduct == null?null:txpProduct.subCategory.id]; + }else{ + o.categories = Lambda.array(Lambda.map(getCategories(), function(c) return c.id)); + o.subcategories = o.categories; + } + } + + App.current.event(ProductInfosEvent(o,distribution)); + + return o; + } + + /** + * customs categs + */ + public function getCategories() { + //"Types de produits" categGroup first + //var pc = db.ProductCategory.manager.search($productId == id, {orderBy:categoryId}, false); + return Lambda.map(db.ProductCategory.manager.search($productId == id,{orderBy:categoryId},false), function(x) return x.category); + } + + /** + * general categs + */ + public function getFullCategorization(){ + if (txpProduct == null) return []; + return txpProduct.getFullCategorization(); + } + + public static function getByRef(c:db.Contract, ref:String){ + var pids = tools.ObjectListTool.getIds(c.getProducts(false)); + return db.Product.manager.select($ref == ref && $id in pids, false); + } + + /** + * Fix values that will make mysql 5.7 scream + */ + function check(){ + if(this.vat==null) this.vat=0; + if(this.name.length>128) this.name = this.name.substr(0,128); + } + + override public function update(){ + check(); + super.update(); + } + + override public function insert(){ + check(); + super.insert(); + } + + public static function getLabels(){ + var t = sugoi.i18n.Locale.texts; + return [ + "name" => t._("Product name"), + "ref" => t._("Product ID"), + "price" => t._("Price"), + "desc" => t._("Description"), + "stock" => t._("Stock"), + "unitType" => t._("Base unit"), + "qt" => t._("Quantity"), + "hasFloatQt" => t._("Allow fractional quantities"), + "active" => t._("Available"), + "organic" => t._("Organic agriculture"), + "vat" => t._("VAT Rate"), + "variablePrice" => t._("Variable price based on weight"), + "multiWeight" => t._("Multi-weighing"), + ]; + } + +} + diff --git a/src/db/ProductCategory.hx b/src/db/ProductCategory.hx new file mode 100755 index 0000000..388f168 --- /dev/null +++ b/src/db/ProductCategory.hx @@ -0,0 +1,27 @@ +package db; +import sys.db.Types; + +@:id(productId,categoryId) +class ProductCategory extends sys.db.Object +{ + + @:relation(productId) + public var product : db.Product; + + @:relation(categoryId) + public var category : db.Category; + + public static function getOrCreate(product, category){ + + var x = db.ProductCategory.manager.select($product==product && $category==category,true); + if(x==null){ + x = new db.ProductCategory(); + x.product = product; + x.category = category; + x.insert(); + } + return x; + + } + +} \ No newline at end of file diff --git a/src/db/TxpCategory.hx b/src/db/TxpCategory.hx new file mode 100644 index 0000000..be3ecac --- /dev/null +++ b/src/db/TxpCategory.hx @@ -0,0 +1,23 @@ +package db; +import sys.db.Object; +import sys.db.Types; +/** + * ... + * @author fbarbut + */ +class TxpCategory extends Object +{ + + public var id : SId; + public var name : SString<128>; + + public function getSubCategories(){ + + return db.TxpSubCategory.manager.search($category == this, false); + + } + + override public function toString(){ + return '#$id-$name'; + } +} \ No newline at end of file diff --git a/src/db/TxpProduct.hx b/src/db/TxpProduct.hx new file mode 100644 index 0000000..40c540a --- /dev/null +++ b/src/db/TxpProduct.hx @@ -0,0 +1,27 @@ +package db; +import sys.db.Object; +import sys.db.Types; + +/** + * ... + * @author fbarbut + */ +class TxpProduct extends Object +{ + + public var id : SId; + public var name : SString<128>; + @:relation(categoryId) public var category : db.TxpCategory; + @:relation(subCategoryId) public var subCategory : db.TxpSubCategory; + + override public function toString(){ + return '#$id-$name'; + } + + public function getFullCategorization():Array{ + return [category.name, subCategory.name, name]; + + } + +} + diff --git a/src/db/TxpSubCategory.hx b/src/db/TxpSubCategory.hx new file mode 100644 index 0000000..10f6437 --- /dev/null +++ b/src/db/TxpSubCategory.hx @@ -0,0 +1,26 @@ +package db; +import sys.db.Object; +import sys.db.Types; +/** + * ... + * @author fbarbut + */ +class TxpSubCategory extends Object +{ + + public var id : SId; + public var name : SString<128>; + @:relation(categoryId) public var category:db.TxpCategory; + + + public function getProducts(){ + + return db.TxpProduct.manager.search($subCategory == this, false); + + + } + + override public function toString(){ + return '#$id-$name'; + } +} \ No newline at end of file diff --git a/src/db/User.hx b/src/db/User.hx new file mode 100755 index 0000000..74cff8f --- /dev/null +++ b/src/db/User.hx @@ -0,0 +1,574 @@ +package db; +import sys.db.Object; +import sys.db.Types; +import db.UserAmap; +import Common; + +enum UserFlags { + HasEmailNotif4h; //send notifications by mail 4h before + HasEmailNotif24h; //send notifications by mail 24h before + HasEmailNotifOuverture; //send notifications by mail on command open + //Tuto; //enable tutorials +} + +/** + * Site-wide right + */ +enum RightSite { + Admin; +} + +@:index(email,unique) +class User extends Object { + + public var id : SId; + public var lang : SString<2>; + @:skip public var name(get, set) : String; + public var pass : STinyText; + public var rights : SFlags; + + public var firstName:SString<32>; + public var lastName:SString<32>; + public var email : SString<64>; + public var phone:SNull>; + + public var firstName2:SNull>; + public var lastName2:SNull>; + public var email2 : SNull>; + public var phone2:SNull>; + + public var address1:SNull>; + public var address2:SNull>; + public var zipCode:SNull>; + public var city:SNull>; + + @:skip public var amap(get_amap, null) : Amap; + + public var cdate : SDate; //creation + public var ldate : SNull; //derniere connexion + + public var flags : SFlags; + + @hideInForms public var tutoState : SNull>; //tutorial state + + public var apiKey : SNull>; //private API key + + public function new() { + super(); + + //default values + cdate = Date.now(); + rights = sys.db.Types.SFlags.ofInt(0); + flags = sys.db.Types.SFlags.ofInt(0); + flags.set(HasEmailNotif24h); + flags.set(HasEmailNotifOuverture); + lang = "fr"; + pass = ""; + + } + + public override function toString() { + return getName()+" ["+id+"]"; + } + + public function isAdmin() { + return rights.has(Admin) || id==1; + } + + public static function login(user:db.User, email:String) { + + user.lock(); + user.ldate = Date.now(); + user.update(); + App.current.session.setUser(user); + if (App.current.session.data == null) App.current.session.data = {}; + //Who's connected, user1 or user2 ? + App.current.session.data.whichUser = (email == user.email) ? 0 : 1; + + } + + /** + * is this user the manager of the current group + */ + public function isAmapManager() { + var a = getAmap(); + if (a == null) return false; + var ua = getUserAmap(a); + if (ua == null) return false; + return ua.hasRight(Right.GroupAdmin); + } + + function getUserAmap(amap:db.Amap):db.UserAmap { + return db.UserAmap.get(this, amap); + } + + public function isFullyRegistred(){ + return pass != null && pass != ""; + } + + public function makeMemberOf(group:db.Amap){ + var ua = db.UserAmap.get(this, group); + if (ua == null) { + ua = new db.UserAmap(); + ua.user = this; + ua.amap = group; + ua.insert(); + } + return ua; + } + + + + /** + * Est ce que ce membre a la gestion de ce contrat + * si null, est ce qu'il a la gestion d'un des contrat, n'importe lequel (utilse pour afficher 'gestion contrat' dans la nav ) + * @param contract + */ + public function isContractManager(?contract:db.Contract ) { + if (isAdmin()) return true; + if (contract != null) { + return canManageContract(contract); + }else { + var ua = getUserAmap(getAmap()); + if (ua == null) return false; + if (ua.rights == null) return false; + for (r in ua.rights) { + switch(r) { + case Right.ContractAdmin(cid): + return true; + default: + } + } + return false; + } + } + + public function canManageAllContracts(){ + if (isAdmin()) return true; + var ua = getUserAmap(getAmap()); + if (ua == null) return false; + if (ua.rights == null) return false; + for (r in ua.rights) { + switch(r) { + case Right.ContractAdmin(cid): + if(cid==null) return true; + default: + } + } + return false; + } + + public function canAccessMessages():Bool { + var ua = getUserAmap(getAmap()); + if (ua == null) return false; + if (ua.hasRight(Right.Messages)) return true; + return false; + } + + public function canAccessMembership():Bool { + var ua = getUserAmap(getAmap()); + if (ua == null) return false; + if (ua.hasRight(Right.Membership)) return true; + return false; + } + + public function canManageContract(c:db.Contract):Bool { + var ua = getUserAmap(c.amap); + if (ua == null) return false; + if (ua.hasRight(Right.ContractAdmin())) return true; + if (ua.hasRight(Right.ContractAdmin(c.id))) return true; + return false; + } + + public function getContractManager(?lock=false) { + return Contract.manager.search($amap == amap && $contact == this, false); + } + + public function getName() { + return get_name(); + } + + public function get_name() { + return lastName + " " + firstName; + } + + public function getCoupleName() { + var n = lastName + " " + firstName; + if (lastName2 != null) { + n = n + " / " + lastName2 + " " + firstName2; + } + return n; + } + + public function set_name(name:String) { + var name = name.split(' '); + firstName = name[0]; + lastName = name[1]; + return firstName+" "+lastName; + } + + /** + * Encode a user password + * @param p + */ + public function setPass(p:String) { + if (p == null){ + this.pass = ""; + }else{ + this.pass = haxe.crypto.Md5.encode( App.config.get('key') + StringTools.trim(p)); + } + return this.pass; + } + + /** + * Renvoie les commandes actuelles du user + * @param _amap force une amap + * @param lock=false + */ + public function getOrders(?_amap:db.Amap,?lock = false):List { + var a = _amap == null ? getAmap() : _amap; + var c = a.getActiveContracts(true); + var cids = Lambda.map(c,function(m) return m.id); + var pids = Lambda.map(db.Product.manager.search($contractId in cids,false), function(x) return x.id); + var out = UserContract.manager.search(($userId == id || $userId2 == id) && $productId in pids, lock); + return out; + + } + + /** + * renvoie les commandes à partir d'une liste de contrats + */ + public function getOrdersFromContracts(c:Iterable):List { + var cids = Lambda.map(c,function(m) return m.id); + var pids = Lambda.map(db.Product.manager.search($contractId in cids,false), function(x) return x.id); + return UserContract.manager.search(($userId == id || $userId2 == id) && $productId in pids, false); + } + + /** + * renvoie les commandes de contrat variables à partir d'une distribution + */ + public function getOrdersFromDistrib(d:db.Distribution):List { + var pids = Lambda.map(db.Product.manager.search($contractId == d.contract.id, false), function(x) return x.id); + return UserContract.manager.search(($userId == id || $userId2 == id) && $distributionId==d.id && $productId in pids , false); + } + + public function get_amap():Amap { + return getAmap(); + } + + /** + * renvoie l'amap selectionnée par le user en cours + */ + public function getAmap() { + + if (App.current.user != null && id != App.current.user.id) throw "This function is valid only for the current user"; + if (App.current.session == null) return null; + if (App.current.session.data == null ) return null; + var a = App.current.session.data.amapId; + if (a == null) { + return null; + }else { + return Amap.manager.get(a,false); + } + } + + /** + * get groups this user belongs to + */ + public function getAmaps():List { + return Lambda.map(UserAmap.manager.search($user == this, false), function(o) return o.amap); + } + + public function isMemberOf(amap:Amap) { + return UserAmap.manager.select($user == this && $amapId == amap.id, false) != null; + } + + + /** + * Renvoie la liste des contrats dans lequel l'adherent a des commandes + * @param lock=false + * @return + */ + public function getContracts(?lock=false):Array { + var out = []; + var ucs = getOrders(lock); + for (uc in ucs) { + if (!Lambda.has(out, uc.product.contract)) { + out.push(uc.product.contract); + } + } + return out; + + } + + /** + * Merge fields of 2 users, then delete the second (u2) + * Be carefull : check before calling this function that u2 can be safely deleted ! + */ + public function merge(u2:db.User) { + + this.lock(); + u2.lock(); + + var m = function(a, b) { + return a == null || a=="" ? b : a; + } + + this.address1 = m(this.address1, u2.address1); + this.address2 = m(this.address2, u2.address2); + this.zipCode = m(this.zipCode, u2.zipCode); + this.city = m(this.city, u2.city); + + //find how to merge the 2 names in each account + if (this.email == u2.email) { + + this.firstName = m(this.firstName, u2.firstName); + this.lastName = m(this.lastName, u2.lastName); + this.phone = m(this.phone, u2.phone); + + } else if (this.email == u2.email2) { + + this.firstName = m(this.firstName, u2.firstName2); + this.lastName = m(this.lastName, u2.lastName2); + this.phone = m(this.phone, u2.phone2); + } + + if (this.email2 == u2.email) { + + this.firstName2 = m(this.firstName2, u2.firstName); + this.lastName2 = m(this.lastName2, u2.lastName); + this.phone2 = m(this.phone2, u2.phone); + + } else if (this.email2 == u2.email2) { + + this.firstName2 = m(this.firstName2, u2.firstName2); + this.lastName2 = m(this.lastName2, u2.lastName2); + this.phone2 = m(this.phone2, u2.phone2); + + } + + u2.delete(); + this.update(); + + } + + public static function getOrCreate(firstName:String, lastName:String, email:String):db.User{ + var u = db.User.manager.select($email == email || $email2 == email, true); + if (u == null){ + u = new db.User(); + u.firstName = firstName; + u.lastName = lastName; + u.email = email; + u.insert(); + } + return u; + } + + /** + * Search for similar users in the DB ( same firstName+lastName or same email ) + */ + public static function __getSimilar(firstName:String, lastName:String, email:String,?firstName2:String, ?lastName2:String, ?email2:String):List { + var out = new Array(); + out = Lambda.array(User.manager.search($firstName.like(firstName) && $lastName.like(lastName), false)); + out = out.concat(Lambda.array(User.manager.search($email.like(email), false))); + out = out.concat(Lambda.array(User.manager.search($firstName2.like(firstName) && $lastName2.like(lastName), false))); + out = out.concat(Lambda.array(User.manager.search($email2.like(email), false))); + + //recherche pour le deuxieme user + if (lastName2 != "" && lastName2 != null && firstName2 != "" && firstName2 != null) { + out = out.concat(Lambda.array(User.manager.search($firstName.like(firstName2) && $lastName.like(lastName2), false))); + out = out.concat(Lambda.array(User.manager.search($firstName2.like(firstName2) && $lastName2.like(lastName2), false))); + } + if (email2 != null && email2 != "") { + out = out.concat(Lambda.array(User.manager.search($email.like(email2), false))); + out = out.concat(Lambda.array(User.manager.search($email2.like(email2), false))); + } + + //dedouble + var x = new Map(); + for ( oo in out) { + x.set(oo.id, oo); + } + return Lambda.list(x); + } + + /** + * Search for similar users in the DB ( same email ) + */ + public static function getSameEmail(email:String, ?email2:String){ + + var out = new Array(); + out = out.concat(Lambda.array(User.manager.search($email.like(email), false))); + out = out.concat(Lambda.array(User.manager.search($email2.like(email), false))); + if (email2 != null && email2 != "") { + out = out.concat(Lambda.array(User.manager.search($email.like(email2), false))); + out = out.concat(Lambda.array(User.manager.search($email2.like(email2), false))); + } + return Lambda.list(out); + } + + /** + * Get users with no contracts + **/ + public static function getUsers_NoContracts(?index:Int,?limit:Int):List { + var productsIds = App.current.user.getAmap().getProducts().map(function(x) return x.id); + var uc = UserContract.manager.search($productId in productsIds, false); + var uc2 = uc.map(function(x) return x.user.id); //liste des userId avec un contrat dans cette amap + + // J. Le Clerc - BUGFIX#1 Ne pas oublier les contrats alternés + for (u in uc) { + if (u.user2 != null) { + uc2.add(u.user2.id); + } + } + + if (uc2.length > 0){ + //les gens qui sont dans cette amap et qui n'ont pas de contrat de cette amap + var ua = db.UserAmap.manager.unsafeObjects("select * from UserAmap where amapId=" + App.current.user.getAmap().id +" and userId NOT IN(" + uc2.join(",") + ")", false); + return Lambda.map(ua, function(x) return x.user); + }else{ + return App.current.user.amap.getMembers(); + } + + } + + /** + * User with contracts + */ + public static function getUsers_Contracts(?index:Int,?limit:Int):List { + var productsIds = App.current.user.getAmap().getProducts().map(function(x) return x.id); + if (productsIds.length == 0) return new List(); + return db.User.manager.unsafeObjects("select u.* from User u, UserContract uc where uc.productId IN(" + productsIds.join(",") + ") AND (uc.userId=u.id OR uc.userId2=u.id) group by u.id ORDER BY u.lastName", false); + } + + + public static function getUsers_NoMembership(?index:Int,?limit:Int):List { + var ua = new List(); + if (index == null && limit == null) { + ua = db.UserAmap.manager.search($amap == App.current.user.amap, false); + }else { + ua = db.UserAmap.manager.search($amap == App.current.user.amap,{limit:[index,limit]}, false); + } + + for (u in Lambda.array(ua)) { + if (u.hasValidMembership()) ua.remove(u); + } + + return Lambda.map(ua, function(x) return x.user); + } + + public static function getUsers_NewUsers(?index:Int, ?limit:Int):List { + + var uas = db.UserAmap.manager.search($amap == App.current.user.amap, false); + var ids = Lambda.map(uas, function(x) return x.user.id); + if (index == null && limit == null) { + return db.User.manager.search($pass == "" && ($id in ids), {orderBy:lastName} ,false); + }else { + return db.User.manager.search($pass == "" && ($id in ids), {limit:[index, limit] ,orderBy:lastName} , false); + } + + } + + public function sendInvitation(group:db.Amap) { + + var t = sugoi.i18n.Locale.texts; + + if (isFullyRegistred()) throw t._("This user cannot receive an invitation"); + + /*var group : db.Amap = null; + + if (App.current.user == null) { + group = this.getAmaps().first(); + }else { + //prend l'amap du user connecté qui a lancé l'invite. + group = App.current.user.amap; + }*/ + + //store token + var k = sugoi.db.Session.generateId(); + sugoi.db.Cache.set("validation" + k, this.id, 60 * 60 * 24 * 30); //expire in 1 month + + var e = new sugoi.mail.Mail(); + if (group != null){ + e.setSubject(t._("Invitation")+" "+group.name); + }else{ + e.setSubject(t._("Invitation Cagette.net")); + } + + e.addRecipient(this.email,this.getName()); + e.setSender(App.config.get("default_email"),t._("Cagette.net")); + + var html = App.current.processTemplate("mail/invitation.mtt", { + email:email, + email2:email2, + groupName:(group == null?null:group.name), + name:firstName, + k:k + } ); + e.setHtmlBody(html); + + App.sendMail(e); + } + + /** + * cleaning before saving + */ + override public function insert() { + clean(); + super.insert(); + } + + override public function update() { + clean(); + super.update(); + } + + function clean() { + + //emails + this.email = this.email.toLowerCase(); + if (this.email2 != null) this.email2 = this.email2.toLowerCase(); + + //lastname + if (this.lastName != null) this.lastName = this.lastName.toUpperCase(); + if (this.lastName2 != null) this.lastName2 = this.lastName2.toUpperCase(); + + if(pass==null) pass=""; + } + + public function infos():UserInfo{ + return { + id:id, + name : getName(), + email : email + }; + } + + /** + * get form labels + */ + public static function getLabels():Map{ + var t = sugoi.i18n.Locale.texts; + return [ + "firstName" => t._("First name"), + "lastName" => t._("Last name"), + "email" => t._("Email"), + "phone" => t._("Phone"), + "firstName2"=> t._("Partner first name"), + "lastName2" => t._("Partner last name"), + "email2" => t._("Partner email"), + "phone2" => t._("Partner phone"), + "lang" => t._("Language"), + "address1" => t._("Address 1"), + "address2" => t._("Address 2"), + "zipCode" => t._("Zip code"), + "city" => t._("City"), + "rights" => t._("Rights"), + "cdate" => t._("Registration date"), + "flags" => t._("Options"), + "pass" => t._("Password"), + ]; + } + + +} diff --git a/src/db/UserAmap.hx b/src/db/UserAmap.hx new file mode 100755 index 0000000..68490f7 --- /dev/null +++ b/src/db/UserAmap.hx @@ -0,0 +1,128 @@ +package db; +import sys.db.Object; +import sys.db.Types; + +enum Right{ + GroupAdmin; //can manage whole group + ContractAdmin(?cid:Int); //can manage one or all contracts + Membership; //can manage group members + Messages; //can send messages +} + +/** + * A user which is member of a group + */ +@:id(userId,amapId) +class UserAmap extends Object +{ + @:relation(amapId) public var amap : db.Amap; + @:relation(userId) public var user : db.User; + public var rights : SNull>>; + public var balance : SFloat; //account balance in group currency + static var CACHE = new Map(); + + + public function new(){ + super(); + balance = 0; + } + + public static function get(user:User, amap:Amap, ?lock = false) { + if (user == null || amap == null) return null; + //SPOD doesnt cache elements with double primary key, so lets do it manually + var c = CACHE.get(user.id + "-" + amap.id); + if (c == null) { + c = manager.select($user == user && $amap == amap, true/*lock*/); + CACHE.set(user.id + "-" + amap.id,c); + } + return c; + } + + public static function getOrCreate(user:db.User, group:db.Amap){ + var ua = get(user, group); + if ( ua == null){ + ua = new UserAmap(); + ua.user = user; + ua.amap = group; + ua.insert(); + } + return ua; + } + + /** + * give right and update DB + */ + public function giveRight(r:Right) { + + if (hasRight(r)) return; + if (rights == null) rights = []; + lock(); + rights.push(r); + update(); + } + + /** + * remove right and update DB + */ + public function removeRight(r:Right) { + if (rights == null) return; + var newrights = []; + for (right in rights.copy()) { + if ( !Type.enumEq(right, r) ) { + newrights.push(right); + } + } + rights = newrights; + update(); + } + + public function hasRight(r:Right):Bool { + if (this.user.isAdmin()) return true; + if (rights == null) return false; + for ( right in rights) { + if ( Type.enumEq(r,right) ) return true; + } + return false; + } + + public function getRightName(r:Right):String { + var t = sugoi.i18n.Locale.texts; + return switch(r) { + case Right.GroupAdmin : t._("Administrator"); + case Right.Messages : t._("Messaging"); + case Right.Membership : t._("Members management"); + case Right.ContractAdmin(cid) : + if (cid == null) { + t._("Management of all contracts"); + }else { + var c = db.Contract.manager.get(cid); + if(c==null) { + t._("Deleted contract"); + }else{ + t._("::name:: contract management",{name:c.name}); + } + } + } + } + + public function hasValidMembership():Bool { + + if (amap.membershipRenewalDate == null) return false; + var cotis = db.Membership.get(this.user, this.amap, this.amap.getMembershipYear()); + return cotis != null; + } + + override public function insert(){ + App.current.event(NewMember(this.user,this.amap)); + super.insert(); + } + + public function getPaymentOperations(){ + return db.Operation.getPaymentOperations(user, amap); + } + + public function getLastOperations(limit){ + return db.Operation.getLastOperations(user, amap, limit); + } + +} \ No newline at end of file diff --git a/src/db/UserContract.hx b/src/db/UserContract.hx new file mode 100755 index 0000000..df13e8c --- /dev/null +++ b/src/db/UserContract.hx @@ -0,0 +1,200 @@ +package db; +import sys.db.Object; +import sys.db.Types; +import Common; + +/** + * a product order + */ +class UserContract extends Object +{ + public var id : SId; + + @formPopulate("populate") @:relation(userId) + public var user : User; + + //shared order + @formPopulate("populate") @:relation(userId2) + public var user2 : SNull; + + public var quantity : SFloat; + + @formPopulate("populateProducts") @:relation(productId) + public var product : Product; + + //store price (1 unit price) and fees (percentage not amount ) rate when the order is done + public var productPrice : SFloat; + public var feesRate : SInt; //fees in percentage + + public var paid : SBool; + + //if not null : varying orders + @:relation(distributionId) + public var distribution:SNull; + + + @:relation(basketId) + public var basket:SNull; + + public var date : SDateTime; + public var flags : SFlags; + + public function new() + { + super(); + quantity = 1; + paid = false; + date = Date.now(); + flags = cast 0; + feesRate = 0; + } + + public function populate() { + return App.current.user.getAmap().getMembersFormElementData(); + } + + + /** + * For shared alternated orders in AMAP contracts + * @param distrib + * @return false -> user , true -> user2 + */ + public function getWhosTurn(distrib:Distribution) { + if (distrib == null) throw "distribution is null"; + if (user2 == null) throw "this contract is not shared"; + + //compter le nbre de distrib pour ce contrat + var c = Distribution.manager.count( $contract == product.contract && $date >= product.contract.startDate && $date <= distrib.date); + var r = c % 2 == 0; + if (flags.has(InvertSharedOrder)){ + return !r; + }else{ + return r; + } + } + + override public function toString() { + if(product==null) return quantity +"x produit inconnu"; + return quantity + "x" + product.name; + } + + public function hasInvertSharedOrder():Bool{ + return flags.has(InvertSharedOrder); + } + + /** + * On peut modifier si ça na pas deja été payé + commande encore ouvertes + */ + public function canModify():Bool { + + var can = false; + if (this.product.contract.type == db.Contract.TYPE_VARORDER) { + + if (this.distribution.orderStartDate == null) { + can = true; + }else { + var n = Date.now().getTime(); + can = n > this.distribution.orderStartDate.getTime() && n < this.distribution.orderEndDate.getTime(); + } + }else { + can = this.product.contract.isUserOrderAvailable(); + } + + return can && !this.paid; + } + + /** + * get users orders for a distribution + */ + public static function getOrders(contract:db.Contract, ?distribution:db.Distribution, ?csv = false):Array{ + var view = App.current.view; + var orders = new Array(); + if (contract.type == db.Contract.TYPE_VARORDER ) { + orders = contract.getOrders(distribution); + }else { + orders = contract.getOrders(); + } + + var orders = service.OrderService.prepare(orders); + + //CSV export + if (csv) { + var t = sugoi.i18n.Locale.texts; + var data = new Array(); + + for (o in orders) { + data.push( { + "name":o.userName, + "productName":o.productName, + "price":view.formatNum(o.productPrice), + "quantity":view.formatNum(o.quantity), + "fees":view.formatNum(o.fees), + "total":view.formatNum(o.total), + "paid":o.paid + }); + } + + var exportName = ""; + if (distribution != null){ + exportName = contract.amap.name + " - " + t._("Delivery ::contractName:: ", {contractName:contract.name}) + distribution.date.toString().substr(0, 10); + }else{ + exportName = contract.amap.name + " - " + contract.name; + } + + sugoi.tools.Csv.printCsvDataFromObjects(data, ["name", "productName", "price", "quantity", "fees", "total", "paid"], exportName+" - " + t._("Per member")); + return null; + }else{ + return orders; + } + + } + + /** + * Get the orders (varying orders) of a user for a multidistrib ( distribs with same day + same place ) + * + * @param distribKey "$date|$placeId" + */ + public static function getUserOrdersByMultiDistrib(distribKey:String, user:db.User,group:db.Amap):Array{ + //var contracts = db.Contract.getActiveContracts(group); + var contracts = db.Contract.manager.search($amap == group, false); //should be able to edit a contract which is closed + for ( c in Lambda.array(contracts)){ + if (c.type == db.Contract.TYPE_CONSTORDERS){ + contracts.remove(c); //only varying orders + } + } + + var cids = Lambda.map(contracts, function(x) return x.id); + var start = Date.fromString(distribKey.split("|")[0] + " 00:00:00"); + var end = Date.fromString(distribKey.split("|")[0] + " 23:59:00"); + var ds = db.Distribution.manager.search($date > start && $date < end && ($contractId in cids), false); + var out = []; + for (d in ds) { + out = out.concat(Lambda.array(user.getOrdersFromDistrib(d))); + } + + return out; + } + + public static function getTotalPrice(tmpOrder:OrderInSession){ + var t = 0.0; + for ( o in tmpOrder.products){ + var p = db.Product.manager.get(o.productId, false); + t += o.quantity * p.getPrice(); + } + return t; + } + + function check(){ + if(quantity==null) quantity == 1; + } + + override function update(){ + check(); + super.update(); + } + + override function insert(){ + check(); + super.insert(); + } +} diff --git a/src/db/Vendor.hx b/src/db/Vendor.hx new file mode 100755 index 0000000..b4fe19a --- /dev/null +++ b/src/db/Vendor.hx @@ -0,0 +1,61 @@ +package db; +import sys.db.Object; +import sys.db.Types; +/** + * Vendor (producteur) + */ +class Vendor extends Object +{ + public var id : SId; + public var name : SString<128>; + + public var email : STinyText; + public var phone:SNull>; + + public var address1:SNull>; + public var address2:SNull>; + public var zipCode:SString<32>; + public var city:SString<25>; + + public var desc : SNull; + + public var linkText:SNull>; + public var linkUrl:SNull>; + + @hideInForms @:relation(imageId) public var image : SNull; + + @:relation(amapId) public var amap : SNull; + + public function new() + { + super(); + var t = sugoi.i18n.Locale.texts; + name = t._("Supplier"); + } + + override function toString() { + return name; + } + + public function getActiveContracts(){ + var now = Date.now(); + return db.Contract.manager.search($vendor == this && $startDate < now && $endDate > now ,{orderBy:-startDate}, false); + } + + public static function getLabels(){ + var t = sugoi.i18n.Locale.texts; + return [ + "name" => t._("Supplier name"), + "desc" => t._("Description"), + "email" => t._("Email"), + "phone" => t._("Phone"), + "address1" => t._("Address 1"), + "address2" => t._("Address 2"), + "zipCode" => t._("Zip code"), + "city" => t._("City"), + "linkText" => t._("Link text"), + "linkUrl" => t._("Link URL"), + ]; + } + +} \ No newline at end of file diff --git a/src/db/WaitingList.hx b/src/db/WaitingList.hx new file mode 100644 index 0000000..03c121e --- /dev/null +++ b/src/db/WaitingList.hx @@ -0,0 +1,20 @@ +package db; +import sys.db.Object; +import sys.db.Types; + + +@:id(userId,amapId) +class WaitingList extends Object +{ + @:relation(amapId) public var group : Amap; + @:relation(userId) public var user : db.User; + public var date : SDateTime; + public var message : SText; + + public function new(){ + super(); + message = ""; + date = Date.now(); + } + +} \ No newline at end of file diff --git a/src/form/ColorRadioGroup.hx b/src/form/ColorRadioGroup.hx new file mode 100755 index 0000000..10e882b --- /dev/null +++ b/src/form/ColorRadioGroup.hx @@ -0,0 +1,46 @@ +package form; +import sugoi.form.elements.RadioGroup; +import Common; + +class ColorRadioGroup extends RadioGroup +{ + + public function new(name:String, label:String,selected:String) + { + var data = []; + var i = 0; + for (c in db.CategoryGroup.COLORS) { + data.push( { value:Std.string(c), label:Std.string(i) } ); + i++; + } + + super(name, label, data, selected, "1", false, true); + } + + override public function render():String + { + var s = ""; + var n = parentForm.name + "_" +name; + + var c = 0; + if (data != null) + { + for (row in data) + { + + var radio = "\n"; + + var img = "
    "; + + s += ""; + + c++; + } + } + + return s; + } + + + +} \ No newline at end of file diff --git a/src/form/ProductTypeRadioGroup.hx b/src/form/ProductTypeRadioGroup.hx new file mode 100755 index 0000000..9cf5775 --- /dev/null +++ b/src/form/ProductTypeRadioGroup.hx @@ -0,0 +1,50 @@ +package form; +import db.Product; +import sugoi.form.elements.RadioGroup; +import db.Product; +import Common; +/** + * list des types de produits avec image + * @author + */ +class ProductTypeRadioGroup extends RadioGroup +{ + + public function new(name:String, label:String,selected:String) + { + var data = []; + var i = 0; + for (e in Type.getEnumConstructs(ProductType)) { + data.push( { key:Std.string(i), value:Std.string(e) } ); + i++; + } + + super(name, label, data, selected, "1", false, true); + } + + override public function render():String + { + var s = ""; + var n = parentForm.name + "_" +name; + + var c = 0; + if (data != null) + { + for (row in data) + { + + + var radio = "\n"; + var e = Type.createEnumIndex(ProductType, Std.parseInt(row.key)); + var img = ""; + + s += ""; + + c++; + } + } + + return s; + } + +} \ No newline at end of file diff --git a/src/form/UnitQuantity.hx b/src/form/UnitQuantity.hx new file mode 100644 index 0000000..5df1d83 --- /dev/null +++ b/src/form/UnitQuantity.hx @@ -0,0 +1,27 @@ +package form; +import Common; +/** + * ... + * @author fbarbut + */ +class UnitQuantity extends sugoi.form.elements.FloatInput +{ + + var unit : Unit; + + public function new(name, label, value, ?required=false,unit){ + super(name, label, value, required); + this.unit = unit; + } + + override function render(){ + + var r = super.render(); + return ' +
    + '+r+' +
    '+App.current.view.unit(unit)+'
    +
    '; + } + +} \ No newline at end of file diff --git a/src/payment/Cash.hx b/src/payment/Cash.hx new file mode 100644 index 0000000..1543054 --- /dev/null +++ b/src/payment/Cash.hx @@ -0,0 +1,22 @@ +package payment; + +/** + * ... + * @author fbarbut + */ +class Cash extends payment.Payment +{ + + public static var TYPE = "cash"; + + public function new() + { + var t = sugoi.i18n.Locale.texts; + this.type = TYPE; + this.icon = ''; + this.name = t._("Cash"); + //this.desc = t._("Pay by cash at product distribution"); + this.link = "/transaction/cash"; + } + +} \ No newline at end of file diff --git a/src/payment/Check.hx b/src/payment/Check.hx new file mode 100644 index 0000000..08af25d --- /dev/null +++ b/src/payment/Check.hx @@ -0,0 +1,24 @@ +package payment; + +/** + * ... + * @author fbarbut + */ +class Check extends payment.Payment +{ + public static var TYPE = "check"; + + public function new() + { + var t = sugoi.i18n.Locale.texts; + this.type = TYPE; + this.icon = ''; + this.name = t._("Check"); + this.link = "/transaction/check"; + } + + public static function getCode(date:Date,place:db.Place,user:db.User){ + return date.toString().substr(0, 10) + "-" + place.id + "-" + user.id; + } + +} \ No newline at end of file diff --git a/src/payment/MoneyPot.hx b/src/payment/MoneyPot.hx new file mode 100644 index 0000000..8f89953 --- /dev/null +++ b/src/payment/MoneyPot.hx @@ -0,0 +1,22 @@ +package payment; + +/** + * ... + * @author fbarbut + */ +class MoneyPot extends payment.Payment +{ + + public static var TYPE = "moneypot"; + + public function new() + { + var t = sugoi.i18n.Locale.texts; + this.type = TYPE; + this.icon = ''; + this.name = t._("Money pot"); + //this.desc = t._("Pay by cash at product distribution"); + this.link = "/transaction/moneypot"; + } + +} \ No newline at end of file diff --git a/src/payment/Payment.hx b/src/payment/Payment.hx new file mode 100644 index 0000000..e523c42 --- /dev/null +++ b/src/payment/Payment.hx @@ -0,0 +1,15 @@ +package payment; + +/** + * ... + * @author fbarbut + */ +class Payment +{ + + public var type:String; + public var icon:String; + public var name:String; //translated name + public var link:String; + +} \ No newline at end of file diff --git a/src/payment/Transfer.hx b/src/payment/Transfer.hx new file mode 100644 index 0000000..589d59d --- /dev/null +++ b/src/payment/Transfer.hx @@ -0,0 +1,21 @@ +package payment; + +/** + * ... + * @author fbarbut + */ +class Transfer extends payment.Payment +{ + + public static var TYPE = "transfer"; + + public function new() + { + var t = sugoi.i18n.Locale.texts; + this.type = TYPE; + this.icon = ''; + this.name = t._("Bank transfer"); + this.link = "/transaction/transfer"; + } + +} \ No newline at end of file diff --git a/src/plugin/Tutorial.hx b/src/plugin/Tutorial.hx new file mode 100644 index 0000000..3aa1827 --- /dev/null +++ b/src/plugin/Tutorial.hx @@ -0,0 +1,109 @@ +package plugin; +import Common; +import sugoi.plugin.*; + +/** + * Tutorials internal plugin + * + * Its listening to events to know if + * the user can go to the next step of his tutorial + * + * User's tutorial state is stored in user.tutoState. + * The JS widget is triggered in view.init() + * + * + */ +class Tutorial extends PlugIn implements IPlugIn +{ + public function new() { + super(); + App.current.eventDispatcher.add(onEvent); + } + + /** + * catch events + */ + public function onEvent(e:Event) { + //no need to continue if tutos are disabled + if ( App.current.user==null || App.current.user.tutoState==null ) return; + + switch(e) { + + //a page is displayed + case Page(uri): + + var ts = App.current.user.tutoState; + if (ts == null) return; + var tuto = TutoDatas.get(ts.name); + var step = tuto.steps[ts.step]; + if (step == null ) return; + + //skip steps if action is "next" + while (step.action.equals(TANext)) { + if (ts.step + 1 >= tuto.steps.length) break; + ts.step++; + step = tuto.steps[ts.step]; + } + + //trace( "tuto active, listening to step="+ts.step ); + switch(step.action) { + case TAPage(_uri): + + //trace(""+_uri+"="+uri+" ?"); + if (match(_uri, uri)) { + + //trace("ok"); + var u = App.current.user; + u.lock(); + + if ( ts.step+1 >= tuto.steps.length) { + //tuto finished + u.tutoState = null; + }else { + //next step + u.tutoState.step = ts.step+1; + } + + u.update(); + } + default: + + } + + + default : + } + } + + /** + * to know if the current uri matches with the tuto step uri + */ + public static function match(pattern:String, uri:String):Bool { + + if (pattern.indexOf("*") > -1) { + + //the url contains a wildcard + + // ~/http:\/\/(\w+).com/ match urls like http://anything.com + var s = pattern; + s = StringTools.replace(s, "/", "\\/"); //escape antislashes + s = StringTools.replace(s, "*", "(\\w+)"); + var e = new EReg(s,""); + + return e.match(uri); + + }else { + + return pattern == uri; + } + + } + + public static function all() { + TutoDatas.get("intro");//just to init translation + return TutoDatas.TUTOS; + } + + + +} \ No newline at end of file diff --git a/src/service/DistributionService.hx b/src/service/DistributionService.hx new file mode 100644 index 0000000..1810134 --- /dev/null +++ b/src/service/DistributionService.hx @@ -0,0 +1,423 @@ +package service; +import Common; + +/** + * Distribution Service + * @author web-wizard + */ +class DistributionService +{ + /** + * It will update the name of the operation with the new number of distributions + * as well as the total amount + * @param contract - + */ + public static function updateAmapContractOperations(contract:db.Contract) { + + //Update all operations for this amap contract when payments are enabled + if (contract.type == db.Contract.TYPE_CONSTORDERS && contract.amap.hasPayments()) { + //Get all the users who have orders for this contract + var users = contract.getUsers(); + for ( user in users ){ + + //Get the one operation for this amap contract and user + var operation = db.Operation.findCOrderTransactionFor(contract, user); + + if (operation != null) + { + //Get all the orders for this contract and user + var orders = contract.getUserOrders(user); + //Update this operation with the new number of distributions, this will affect the name of the operation + //as well as the total amount to pay + db.Operation.updateOrderOperation(operation, orders); + } + + } + } + } + + + /** + * checks if dates are correct and if that there is no other distribution in the same time range + * and for the same contract and place + * @param d + */ + public static function checkDistrib(d:db.Distribution) { + + //Generic variables + var t = sugoi.i18n.Locale.texts; + var view = App.current.view; + + var c = d.contract; + + var distribs1; + var distribs2; + var distribs3; + //We are checking that there is no existing distribution with an overlapping time frame for the same place and contract + if (d.id == null) { //We need to check there the id as $id != null doesn't work in the manager.search + //Looking for existing distributions with a time range overlapping the start of the about to be created distribution + distribs1 = db.Distribution.manager.search($contract == c && $place == d.place && $date <= d.date && $end >= d.date, false); + //Looking for existing distributions with a time range overlapping the end of the about to be created distribution + distribs2 = db.Distribution.manager.search($contract == c && $place == d.place && $date <= d.end && $end >= d.end, false); + //Looking for existing distributions with a time range included in the time range of the about to be created distribution + distribs3 = db.Distribution.manager.search($contract == c && $place == d.place && $date >= d.date && $end <= d.end, false); + } + else { + //Looking for existing distributions with a time range overlapping the start of the about to be created distribution + distribs1 = db.Distribution.manager.search($contract == c && $place == d.place && $date <= d.date && $end >= d.date && $id != d.id, false); + //Looking for existing distributions with a time range overlapping the end of the about to be created distribution + distribs2 = db.Distribution.manager.search($contract == c && $place == d.place && $date <= d.end && $end >= d.end && $id != d.id, false); + //Looking for existing distributions with a time range included in the time range of the about to be created distribution + distribs3 = db.Distribution.manager.search($contract == c && $place == d.place && $date >= d.date && $end <= d.end && $id != d.id, false); + } + + if (distribs1.length != 0 || distribs2.length != 0 || distribs3.length != 0) { + throw new tink.core.Error(t._("There is already a distribution at this place overlapping with the time range you've selected.")); + } + + if (d.date.getTime() > c.endDate.getTime()) throw new tink.core.Error(t._("The date of the delivery must be prior to the end of the contract (::contractEndDate::)", {contractEndDate:view.hDate(c.endDate)})); + if (d.date.getTime() < c.startDate.getTime()) throw new tink.core.Error(t._("The date of the delivery must be after the begining of the contract (::contractBeginDate::)", {contractBeginDate:view.hDate(c.startDate)})); + + if (c.type == db.Contract.TYPE_VARORDER ) { + if (d.date.getTime() < d.orderEndDate.getTime() ) throw new tink.core.Error(t._("The distribution start date must be set after the orders end date.")); + if (d.orderStartDate.getTime() > d.orderEndDate.getTime() ) throw new tink.core.Error(t._("The orders end date must be set after the orders start date !")); + } + + } + + /** + * Creates a new distribution and prevents distribution overlapping and other checks + * @param contract - + * @param date - + * @param end - + * @param placeId - + * @param distributor1Id - + * @param distributor2Id - + * @param distributor3Id - + * @param distributor4Id - + * @param orderStartDate - + * @param orderEndDate - + * @param distributionCycle - + * @param dispatchEvent=true - + * @return db.Distribution + */ + public static function create(contract:db.Contract,date:Date,end:Date,placeId:Int, + ?distributor1Id:Int,?distributor2Id:Int,?distributor3Id:Int,?distributor4Id:Int, + ?orderStartDate:Date,?orderEndDate:Date,?distributionCycle:db.DistributionCycle,?dispatchEvent=true):db.Distribution { + + var d = new db.Distribution(); + d.contract = contract; + d.date = date; + d.place = db.Place.manager.get(placeId); + d.distributionCycle = distributionCycle; + if(distributor1Id != null) d.distributor1 = db.User.manager.get(distributor1Id); + if(distributor2Id != null) d.distributor2 = db.User.manager.get(distributor2Id); + if(distributor3Id != null) d.distributor3 = db.User.manager.get(distributor3Id); + if(distributor4Id != null) d.distributor4 = db.User.manager.get(distributor4Id); + if(contract.type==db.Contract.TYPE_VARORDER){ + d.orderStartDate = orderStartDate; + d.orderEndDate = orderEndDate; + } + + if (end == null) { + d.end = DateTools.delta(d.date, 1000.0 * 60 * 60); + } + else { + d.end = new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate(), end.getHours(), end.getMinutes(), 0); + } + + DistributionService.checkDistrib(d); + + if(distributionCycle == null && dispatchEvent) { + var e :Event = NewDistrib(d); + App.current.event(e); + } + + if (d.date == null){ + return d; + } else { + d.insert(); + + //In case this is a distrib for an amap contract with payments enabled, it will update all the operations + //names and amounts with the new number of distribs + updateAmapContractOperations(d.contract); + + return d; + } + } + + /** + * Modifies an existing distribution and prevents distribution overlapping and other checks + * @param d - + * @param date - + * @param end - + * @param placeId - + * @param distributor1Id - + * @param distributor2Id - + * @param distributor3Id - + * @param distributor4Id - + * @param orderStartDate - + * @param orderEndDate - + * @return db.Distribution + */ + public static function edit(d:db.Distribution,date:Date,end:Date,placeId:Int, + distributor1Id:Int,distributor2Id:Int,distributor3Id:Int,distributor4Id:Int, + orderStartDate:Date,orderEndDate:Date,?dispatchEvent=true):db.Distribution { + + //We prevent others from modifying it + d.lock(); + + d.date = date; + d.place = db.Place.manager.get(placeId); + d.distributor1 = db.User.manager.get(distributor1Id); + d.distributor2 = db.User.manager.get(distributor2Id); + d.distributor3 = db.User.manager.get(distributor3Id); + d.distributor4 = db.User.manager.get(distributor4Id); + if(d.contract.type==db.Contract.TYPE_VARORDER){ + d.orderStartDate = orderStartDate; + d.orderEndDate = orderEndDate; + } + + if (end == null) { + d.end = DateTools.delta(d.date, 1000.0 * 60 * 60); + } + else { + d.end = new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate(), end.getHours(), end.getMinutes(), 0); + } + + DistributionService.checkDistrib(d); + + if(dispatchEvent) App.current.event(EditDistrib(d)); + + if (d.date == null){ + return d; + } else { + d.update(); + return d; + } + } + + + /** + * Checks whether there are orders with non zero quantity for non amap contract + * @param d - + * @return Bool + */ + public static function canDelete(d:db.Distribution):Bool{ + + if (d.contract.type == db.Contract.TYPE_CONSTORDERS) return true; + + var quantity = 0.0; + for ( order in d.getOrders() ){ + quantity += order.quantity; + } + return quantity == 0.0; + + } + + + /** + * Deletes a distribution + * @param d - + * @param dispatchEvent=true - + */ + public static function delete(d:db.Distribution,?dispatchEvent=true) { + var t = sugoi.i18n.Locale.texts; + if ( !canDelete(d) ) { + throw new tink.core.Error(t._("Deletion non possible: some orders are saved for this delivery.")); + } + + var contract = d.contract; + d.lock(); + if (dispatchEvent) { + App.current.event(DeleteDistrib(d)); + } + d.delete(); + //In case this is a distrib for an amap contract with payments enabled, it will update all the operations + //names and amounts with the new number of distribs + updateAmapContractOperations(contract); + + } + + /** + * Computes the correct start and end dates + * @param dc - + * @param datePointer - + */ + public static function getDates(dc:db.DistributionCycle, datePointer:Date) { + + //Generic variables + var t = sugoi.i18n.Locale.texts; + + var startDate = new Date(datePointer.getFullYear(),datePointer.getMonth(),datePointer.getDate(),dc.startHour.getHours(),dc.startHour.getMinutes(),0); + var orderStartDate = null; + var orderEndDate = null; + if (dc.contract.type == db.Contract.TYPE_VARORDER){ + + if (dc.daysBeforeOrderEnd == null || dc.daysBeforeOrderStart == null) throw new tink.core.Error(t._("daysBeforeOrderEnd or daysBeforeOrderStart is null")); + + var a = DateTools.delta(startDate, -1.0 * dc.daysBeforeOrderStart * 1000 * 60 * 60 * 24); + var h : Date = dc.openingHour; + orderStartDate = new Date(a.getFullYear(), a.getMonth(), a.getDate(), h.getHours(), h.getMinutes(), 0); + + var a = DateTools.delta(startDate, -1.0 * dc.daysBeforeOrderEnd * 1000 * 60 * 60 * 24); + var h : Date = dc.closingHour; + orderEndDate = new Date(a.getFullYear(), a.getMonth(), a.getDate(), h.getHours(), h.getMinutes(), 0); + } + return { date: startDate, orderStartDate: orderStartDate, orderEndDate: orderEndDate }; + } + + /** + * Creates all the distributions from the first date + * @param dc - + */ + public static function createCycleDistribs(dc:db.DistributionCycle) { + + //Generic variables + var t = sugoi.i18n.Locale.texts; + + //switch end date to 23:59 to avoid the last distribution to be skipped + dc.endDate = tools.DateTool.setHourMinute(dc.endDate,23,59); + + if (dc.id == null) throw new tink.core.Error(t._("this distributionCycle has not been recorded")); + + //iterations + //For first distrib + var datePointer = new Date(dc.startDate.getFullYear(), dc.startDate.getMonth(), dc.startDate.getDate(), 12, 0, 0); + //why hour=12 ? because if we set hour to 0, it switch to 23 (-1) or 1 (+1) on daylight saving time switch dates, thus changing the day!! + var firstDistribDate = new Date(datePointer.getFullYear(),datePointer.getMonth(),datePointer.getDate(),dc.startHour.getHours(),dc.startHour.getMinutes(),0); + for(i in 0...100) { + + if(i != 0){ //All distribs except the first one + var oneDay = 1000 * 60 * 60 * 24.0; + switch(dc.cycleType) { + case Weekly : + datePointer = DateTools.delta(datePointer, oneDay * 7.0); + App.log("on ajoute "+(oneDay * 7.0)+"millisec pour ajouter 7 jours"); + App.log('pointer : $datePointer'); + + case BiWeekly : + datePointer = DateTools.delta(datePointer, oneDay * 14.0); + + case TriWeekly : + datePointer = DateTools.delta(datePointer, oneDay * 21.0); + + case Monthly : + var n = tools.DateTool.getWhichNthDayOfMonth(firstDistribDate); + var dayOfWeek = firstDistribDate.getDay(); + var nextMonth = new Date(datePointer.getFullYear(), datePointer.getMonth() + 1, 1, 0, 0, 0); + datePointer = tools.DateTool.getNthDayOfMonth(nextMonth.getFullYear(), nextMonth.getMonth(), dayOfWeek, n); + if (datePointer.getMonth() != nextMonth.getMonth()) { + datePointer = tools.DateTool.getNthDayOfMonth(nextMonth.getFullYear(), nextMonth.getMonth(), dayOfWeek, n - 1); + } + } + } + + //stop if cycle end is reached + if (datePointer.getTime() > dc.endDate.getTime()) { + break; + } + + var dates = getDates(dc, datePointer); + + service.DistributionService.create(dc.contract,dates.date, + new Date(datePointer.getFullYear(),datePointer.getMonth(),datePointer.getDate(),dc.endHour.getHours(),dc.endHour.getMinutes(),0), + dc.place.id,null,null,null,null,dates.orderStartDate,dates.orderEndDate,dc); + + } + } + + /** + * Deletes all distributions which are part of this cycle + * @param cycle - + */ + public static function deleteCycleDistribs(cycle:db.DistributionCycle){ + + cycle.lock(); + + //Generic variables + var t = sugoi.i18n.Locale.texts; + var view = App.current.view; + + var children = db.Distribution.manager.search($distributionCycle == cycle, true); + var messages = []; + if(children.length != 0) { + + var contract = Lambda.array(children)[0].contract; + for ( d in children ){ + + if (d.contract.type == db.Contract.TYPE_VARORDER && !canDelete(d) ){ + messages.push(t._("The delivery of the ::delivDate:: could not be deleted because it has orders.", {delivDate:view.hDate(d.date)})); + }else{ + d.delete(); + } + } + + //In case this is a distrib cycle for an amap contract with payments enabled, it will update all the operations + //names and amounts with the new number of distribs + updateAmapContractOperations(contract); + + } + cycle.delete(); + + return messages; + } + + /** + * Creates a new distribution cycle and prevents distribution overlapping and other checks + * @param contract - + * @param cycleType - + * @param startDate - + * @param endDate - + * @param startHour - + * @param endHour - + * @param daysBeforeOrderStart - + * @param daysBeforeOrderEnd - + * @param openingHour - + * @param closingHour - + * @param placeId - + * @param dispatchEvent=true - + * @return db.DistributionCycle + */ + public static function createCycle(contract:db.Contract,cycleType:db.DistributionCycle.CycleType,startDate:Date,endDate:Date, + startHour:Date,endHour:Date,daysBeforeOrderStart:Null,daysBeforeOrderEnd:Null,openingHour:Null,closingHour:Null, + placeId:Int,?dispatchEvent=true):db.DistributionCycle { + + //Generic variables + var t = sugoi.i18n.Locale.texts; + var view = App.current.view; + + var dc = new db.DistributionCycle(); + dc.contract = contract; + dc.cycleType = cycleType; + dc.startDate = startDate; + dc.endDate = endDate; + dc.startHour = startHour; + dc.endHour = endHour; + dc.place = db.Place.manager.get(placeId); + + if (contract.type == db.Contract.TYPE_VARORDER) { + dc.daysBeforeOrderStart = daysBeforeOrderStart; + dc.daysBeforeOrderEnd = daysBeforeOrderEnd; + dc.openingHour = openingHour; + dc.closingHour = closingHour; + } + + if (dc.endDate.getTime() > contract.endDate.getTime()) { + throw new tink.core.Error(t._("The date of the delivery must be prior to the end of the contract (::contractEndDate::)", {contractEndDate:view.hDate(contract.endDate)})); + } + if (dc.startDate.getTime() < contract.startDate.getTime()) { + throw new tink.core.Error(t._("The date of the delivery must be after the begining of the contract (::contractBeginDate::)", {contractBeginDate:view.hDate(contract.startDate)})); + } + + if(dispatchEvent){ + App.current.event(NewDistribCycle(dc)); + } + + dc.insert(); + createCycleDistribs(dc); + + return dc; + + } +} \ No newline at end of file diff --git a/src/service/GroupService.hx b/src/service/GroupService.hx new file mode 100644 index 0000000..036a68c --- /dev/null +++ b/src/service/GroupService.hx @@ -0,0 +1,81 @@ +package service; +using Lambda; +using tools.ObjectListTool; +/** + * Service for managing groups + * @author fbarbut + */ +class GroupService +{ + + public function new() + { + + } + + /** + * copy groups. + * @param g + */ + public static function duplicateGroup(g:db.Amap){ + + var d = new db.Amap(); + d.name = g.name+" (copy)"; + d.contact = g.contact; + d.txtIntro = g.txtIntro; + d.txtHome = g.txtHome; + d.txtDistrib = g.txtDistrib; + d.extUrl = g.extUrl; + d.membershipRenewalDate = g.membershipRenewalDate; + d.membershipPrice = g.membershipPrice; + d.vatRates = g.vatRates; + d.flags = g.flags; + d.groupType = g.groupType; + d.image = g.image; + d.regOption = g.regOption; + d.currency = g.currency; + d.currencyCode = g.currencyCode; + d.allowedPaymentsType = g.allowedPaymentsType; + d.checkOrder = g.checkOrder; + d.IBAN = g.IBAN; + d.insert(); + + //put me in the group + + return d; + } + + static function duplicateCategories(from:db.Amap,to:db.Amap){ + + } + + static function duplicateContract(){ + + } + + /** + Get users with rights in this group + **/ + public static function getGroupMembersWithRights(group:db.Amap,?rights:Array):Array{ + + var membersWithAnyRights = db.UserAmap.manager.search($rights!=null && $amap==group,false).array(); + if(rights==null){ + return Lambda.map(membersWithAnyRights,function(ua) return ua.user).array(); + }else{ + var members = []; + for( m in membersWithAnyRights){ + for(r in rights){ + if(m.hasRight(r)){ + members.push(m.user); + break; + } + } + } + + return members.deduplicate(); + } + + + } + +} \ No newline at end of file diff --git a/src/service/OrderService.hx b/src/service/OrderService.hx new file mode 100644 index 0000000..34de67e --- /dev/null +++ b/src/service/OrderService.hx @@ -0,0 +1,468 @@ +package service; +import Common; +import tink.core.Error; + +/** + * Order Service + * @author web-wizard,fbarbut + */ +class OrderService +{ + + static function canHaveFloatQt(product:db.Product):Bool{ + return product.hasFloatQt || product.wholesale || product.variablePrice; + } + + /** + * Make a product Order + * + * @param quantity + * @param productId + */ + public static function make(user:db.User, quantity:Float, product:db.Product, ?distribId:Int, ?paid:Bool, ?user2:db.User, ?invert:Bool):db.UserContract { + + var t = sugoi.i18n.Locale.texts; + + if(product.contract.type==db.Contract.TYPE_VARORDER && distribId==null) throw "You have to provide a distribId"; + if(quantity==null) throw "Quantity is null"; + + //quantity + if ( !canHaveFloatQt(product) ){ + if( !tools.FloatTool.isInt(quantity) ) { + throw new tink.core.Error(t._("Error : product \"::product::\" quantity should be integer",{product:product.name})); + } + } + + //multiweight : make one row per product + if (product.multiWeight && quantity > 1.0){ + if (product.multiWeight && quantity != Math.abs(quantity)) throw t._("multi-weighing products should be ordered only with integer quantities"); + + var o = null; + for ( i in 0...Math.round(quantity)){ + o = make(user, 1, product, distribId, paid, user2, invert); + } + return o; + } + + //checks + if (quantity <= 0) return null; + + //check for previous orders on the same distrib + var prevOrders = new List(); + if (distribId == null) { + prevOrders = db.UserContract.manager.search($product==product && $user==user, true); + }else { + prevOrders = db.UserContract.manager.search($product==product && $user==user && $distributionId==distribId, true); + } + + //Create order object + var o = new db.UserContract(); + o.product = product; + o.quantity = quantity; + o.productPrice = product.price; + if (product.contract.hasPercentageOnOrders()) { + o.feesRate = product.contract.percentageValue; + } + o.user = user; + if (user2 != null) { + o.user2 = user2; + if (invert != null) o.flags.set(InvertSharedOrder); + } + if (paid != null) o.paid = paid; + if (distribId != null) o.distribution = db.Distribution.manager.get(distribId); + + //cumulate quantities if there is a similar previous order + if (prevOrders.length > 0 && !product.multiWeight) { + for (prevOrder in prevOrders) { + //if (!prevOrder.paid) { + o.quantity += prevOrder.quantity; + prevOrder.delete(); + //} + } + } + + //create a basket object + if (distribId != null){ + var dist = o.distribution; + var basket = db.Basket.getOrCreate(user, dist.place, dist.date); + o.basket = basket; + } + + o.insert(); + + //Stocks + if (o.product.stock != null) { + var c = o.product.contract; + if (c.hasStockManagement()) { + //trace("stock for "+quantity+" x "+product.name); + if (o.product.stock == 0) { + if (App.current.session != null) { + App.current.session.addMessage(t._("There is no more '::productName::' in stock, we removed it from your order", {productName:o.product.name}), true); + } + o.quantity -= quantity; + if ( o.quantity <= 0 ) { + o.delete(); + return null; + } + }else if (o.product.stock - quantity < 0) { + var canceled = quantity - o.product.stock; + o.quantity -= canceled; + o.update(); + + if (App.current.session != null) { + var msg = t._("We reduced your order of '::productName::' to quantity ::oQuantity:: because there is no available products anymore", {productName:o.product.name, oQuantity:o.quantity}); + App.current.session.addMessage(msg, true); + } + o.product.lock(); + o.product.stock = 0; + o.product.update(); + App.current.event(StockMove({product:o.product, move:0 - (quantity - canceled) })); + + }else { + o.product.lock(); + o.product.stock -= quantity; + o.product.update(); + App.current.event(StockMove({product:o.product, move:0 - quantity})); + } + } + } + return o; + } + + + /** + * Edit an existing order (quantity) + */ + public static function edit(order:db.UserContract, newquantity:Float, ?paid:Bool , ?user2:db.User,?invert:Bool) { + + var t = sugoi.i18n.Locale.texts; + + order.lock(); + + //quantity + if (newquantity == null) newquantity = 0; + if ( !canHaveFloatQt(order.product) ){ + if( !tools.FloatTool.isInt(newquantity) ) { + throw new tink.core.Error(t._("Error : product \"::product::\" quantity should be integer",{product:order.product.name})); + } + } + + //paid + if (paid != null) { + order.paid = paid; + }else { + if (order.quantity < newquantity) order.paid = false; + } + + //shared order + if (user2 != null){ + order.user2 = user2; + if (invert == true) order.flags.set(InvertSharedOrder); + if (invert == false) order.flags.unset(InvertSharedOrder); + }else{ + order.user2 = null; + order.flags.unset(InvertSharedOrder); + } + + //stocks + var e : Event = null; + if (order.product.stock != null) { + var c = order.product.contract; + + if (c.hasStockManagement()) { + + if (newquantity < order.quantity) { + + //on commande moins que prévu : incrément de stock + order.product.lock(); + order.product.stock += (order.quantity-newquantity); + e = StockMove({product:order.product, move:0 - (order.quantity-newquantity) }); + + }else { + + //on commande plus que prévu : décrément de stock + var addedquantity = newquantity - order.quantity; + + if (order.product.stock - addedquantity < 0) { + + //stock is not enough, reduce order + newquantity = order.quantity + order.product.stock; + if( App.current.session!=null) App.current.session.addMessage(t._("We reduced your order of '::productName::' to quantity ::oQuantity:: because there is no available products anymore", {productName:order.product.name, oQuantity:newquantity}), true); + + e = StockMove({product:order.product, move: 0 - order.product.stock }); + + order.product.lock(); + order.product.stock = 0; + + }else{ + + //stock is big enough + order.product.lock(); + order.product.stock -= addedquantity; + + e = StockMove({ product:order.product, move: 0 - addedquantity }); + } + } + order.product.update(); + } + } + + //update order + if (newquantity == 0) { + order.quantity = 0; + order.paid = true; + order.update(); + }else { + order.quantity = newquantity; + order.update(); + } + + App.current.event(e); + + return order; + } + + + /** + * Delete an order + */ + public static function delete(order:db.UserContract) { + var t = sugoi.i18n.Locale.texts; + + if(order==null) throw new Error(t._("This order has already been deleted.")); + + order.lock(); + + if (order.quantity == 0) { + + var contract = order.product.contract; + var user = order.user; + + //Amap Contract + if ( contract.type == db.Contract.TYPE_CONSTORDERS ) { + + order.delete(); + + if( contract.amap.hasPayments() ){ + var orders = contract.getUserOrders(user); + if( orders.length == 0 ){ + var operation = db.Operation.findCOrderTransactionFor(contract, user); + if(operation!=null) operation.delete(); + } + } + } + else { //Variable orders contract + + //Get the basket for this user + var place = order.distribution.place; + var basket = db.Basket.get(user, place, order.distribution.date); + + if( contract.amap.hasPayments() ){ + var orders = basket.getOrders(); + //Check if it is the last order, if yes then delete the related operation + if( orders.length == 1 && orders.first().id==order.id ){ + var operation = db.Operation.findVOrderTransactionFor(order.distribution.getKey(), user, place.amap); + if(operation!=null) operation.delete(); + } + + } + + order.delete(); + } + } + else { + throw new Error(t._("Deletion not possible: quantity is not zero.")); + } + + } + + /** + * Prepare a simple dataset, ready to be displayed + */ + public static function prepare(orders:Iterable):Array { + var out = new Array(); + var orders = Lambda.array(orders); + var view = App.current.view; + var t = sugoi.i18n.Locale.texts; + for (o in orders) { + + var x : UserOrder = cast { }; + x.id = o.id; + x.userId = o.user.id; + x.userName = o.user.getCoupleName(); + x.userEmail = o.user.email; + + //shared order + if (o.user2 != null){ + x.userId2 = o.user2.id; + x.userName2 = o.user2.getCoupleName(); + x.userEmail2 = o.user2.email; + } + + //deprecated + x.productId = o.product.id; + x.productRef = o.product.ref; + x.productQt = o.product.qt; + x.productUnit = o.product.unitType; + x.productPrice = o.productPrice; + x.productImage = o.product.getImage(); + x.productHasFloatQt = o.product.hasFloatQt; + x.productHasVariablePrice = o.product.variablePrice; + //new way + x.product = o.product.infos(); + x.product.price = o.productPrice;//do not use current price, but price of the order + + + x.quantity = o.quantity; + + //smartQt + if (x.quantity == 0.0){ + x.smartQt = t._("Canceled"); + }else if(x.productHasFloatQt || x.productHasVariablePrice || o.product.wholesale){ + x.smartQt = view.smartQt(x.quantity, x.productQt, x.productUnit); + }else{ + x.smartQt = Std.string(x.quantity); + } + + //product name. + if ( x.productHasVariablePrice || x.productQt==null || x.productUnit==null ){ + x.productName = o.product.name; + }else{ + x.productName = o.product.name + " " + view.formatNum(x.productQt) +" "+ view.unit(x.productUnit,x.productQt>1); + } + + x.subTotal = o.quantity * o.productPrice; + + var c = o.product.contract; + + if ( o.feesRate!=0 ) { + + x.fees = x.subTotal * (o.feesRate/100); + x.percentageName = c.percentageName; + x.percentageValue = o.feesRate; + x.total = x.subTotal + x.fees; + + }else { + x.total = x.subTotal; + } + + //flags + x.paid = o.paid; + x.invertSharedOrder = o.flags.has(InvertSharedOrder); + x.contractId = c.id; + x.contractName = c.name; + x.canModify = o.canModify(); + + out.push(x); + } + + return sort(out); + } + + /** + * Confirms an order : create real orders from tmp orders in session + * @param order + */ + public static function confirmSessionOrder(tmpOrder:OrderInSession){ + var orders = []; + var user = db.User.manager.get(tmpOrder.userId); + for (o in tmpOrder.products){ + o.product = db.Product.manager.get(o.productId); + orders.push( make(user, o.quantity, o.product, o.distributionId) ); + } + + App.current.event(MakeOrder(orders)); + App.current.session.data.order = null; + + return orders; + } + + + /** + * Send an order-by-products report to the coordinator + */ + public static function sendOrdersByProductReport(d:db.Distribution){ + + var m = new sugoi.mail.Mail(); + m.addRecipient(d.contract.contact.email , d.contract.contact.getName()); + m.setSender(App.config.get("default_email"),"Cagette.net"); + m.setSubject('[${d.contract.amap.name}] Distribution du ${App.current.view.dDate(d.date)} (${d.contract.name})'); + var orders = service.ReportService.getOrdersByProduct(d); + + var html = App.current.processTemplate("mail/ordersByProduct.mtt", { + contract:d.contract, + distribution:d, + orders:orders, + formatNum:App.current.view.formatNum, + currency:App.current.view.currency, + dDate:App.current.view.dDate, + hHour:App.current.view.hHour, + group:d.contract.amap + } ); + + m.setHtmlBody(html); + App.sendMail(m); + + } + + + /** + * Order summary for a member + * WARNING : its for one distrib, not for a whole basket ! + */ + public static function sendOrderSummaryToMembers(d:db.Distribution){ + + var title = '[${d.contract.amap.name}] Votre commande pour le ${App.current.view.dDate(d.date)} (${d.contract.name})'; + + for( user in d.getUsers() ){ + + var m = new sugoi.mail.Mail(); + m.addRecipient(user.email , user.getName(),user.id); + if(user.email2!=null) m.addRecipient(user.email2 , user.getName(),user.id); + m.setSender(App.config.get("default_email"),"Cagette.net"); + m.setSubject(title); + var orders = prepare(d.contract.getUserOrders(user,d)); + + var html = App.current.processTemplate("mail/orderSummaryForMember.mtt", { + contract:d.contract, + distribution:d, + orders:orders, + formatNum:App.current.view.formatNum, + currency:App.current.view.currency, + dDate:App.current.view.dDate, + hHour:App.current.view.hHour, + group:d.contract.amap + } ); + + m.setHtmlBody(html); + App.sendMail(m); + } + + } + + + + public static function sort(orders:Array){ + + //order by lastname (+lastname2 if exists), then contract + orders.sort(function(a, b) { + + if (a.userName + a.userId + a.userName2 + a.userId2 + a.contractId > b.userName + b.userId + b.userName2 + b.userId2 + b.contractId ) { + + return 1; + } + if (a.userName + a.userId + a.userName2 + a.userId2 + a.contractId < b.userName + b.userId + b.userName2 + b.userId2 + b.contractId ) { + + return -1; + } + return 0; + }); + + return orders; + } + + + + + +} \ No newline at end of file diff --git a/src/service/PaymentService.hx b/src/service/PaymentService.hx new file mode 100644 index 0000000..cfae939 --- /dev/null +++ b/src/service/PaymentService.hx @@ -0,0 +1,182 @@ +package service; +import Common; + +/** + * Payment Service + * @author web-wizard + */ +class PaymentService +{ + /** + * Get all available payment types, including one from plugins + */ + public static function getAllPaymentTypes(){ + var types = [ + new payment.Cash(), + new payment.Check(), + new payment.Transfer(), + new payment.MoneyPot(), + ]; + + var e = App.current.event(GetPaymentTypes({types:types})); + return switch(e){ + case GetPaymentTypes(d): d.types; + default : null; + } + + } + + public static function getAllowedPaymentTypes(group:db.Amap):Array{ + var out :Array = []; + + //populate with activated payment types. + var all = getAllPaymentTypes(); + if ( group.allowedPaymentsType == null ) return []; + for ( t in group.allowedPaymentsType){ + + var found = Lambda.find(all, function(a) return a.type == t); + if (found != null) out.push(found); + } + return out; + } + + public static function getPaymentTypesForManualEntry(group:db.Amap){ + + var out = []; + var paymentTypes = []; + var allowedPaymentTypes = service.PaymentService.getAllowedPaymentTypes(group); + if ( !Lambda.exists(allowedPaymentTypes, function(obj) return obj.type == "moneypot" ) ) { + paymentTypes = allowedPaymentTypes; + } + else { + paymentTypes = service.PaymentService.getAllPaymentTypes(); + } + for ( t in paymentTypes ){ + if(t.type != "moneypot") out.push({label:t.name,value:t.type}); + } + + return out; + } + + /** + * Auto validate a distribution. + * This is called by the hourly cron + * + * @param distrib + */ + public static function validateDistribution(distrib:db.Distribution) { + + for ( user in distrib.getUsers()){ + var basket = db.Basket.get(user, distrib.place, distrib.date); + validateBasket(basket); + } + //finally validate distrib + distrib.lock(); + distrib.validated = true; + distrib.update(); + } + + public static function unvalidateDistribution(distrib:db.Distribution) { + + for ( user in distrib.getUsers()){ + var basket = db.Basket.get(user, distrib.place, distrib.date); + unvalidateBasket(basket); + } + //finally validate distrib + distrib.lock(); + distrib.validated = false; + distrib.update(); + } + + /** + * Auto validate a basket + * + * @param basket + */ + public static function validateBasket(basket:db.Basket) { + + if (basket == null || basket.isValidated()) return false; + + //mark orders as paid + var orders = basket.getOrders(); + for ( order in orders ){ + + order.lock(); + order.paid = true; + order.update(); + } + + //validate order operation and payments + var operation = basket.getOrderOperation(false); + if (operation != null){ + + operation.lock(); + operation.pending = false; + operation.update(); + + for ( payment in basket.getPayments()){ + + if ( payment.pending){ + payment.lock(); + payment.pending = false; + payment.update(); + } + } + + var o = orders.first(); + updateUserBalance(o.user, o.distribution.place.amap); + } + + return true; + } + + public static function unvalidateBasket(basket:db.Basket) { + + if (basket == null || !basket.isValidated()) return false; + + //mark orders as paid + var orders = basket.getOrders(); + for ( order in orders ){ + + order.lock(); + order.paid = false; + order.update(); + } + + //validate order operation and payments + var operation = basket.getOrderOperation(false); + if (operation != null){ + + operation.lock(); + operation.pending = true; + operation.update(); + + for ( payment in basket.getPayments()){ + + if (!payment.pending){ + payment.lock(); + payment.pending = true; + payment.update(); + } + } + + var o = orders.first(); + updateUserBalance(o.user, o.distribution.place.amap); + } + + return true; + } + + + /** + * update user balance + */ + public static function updateUserBalance(user:db.User,group:db.Amap){ + + var ua = db.UserAmap.getOrCreate(user, group); + var b = sys.db.Manager.cnx.request('SELECT SUM(amount) FROM Operation WHERE userId=${user.id} and groupId=${group.id} and !(type=2 and pending=1)').getFloatResult(0); + b = Math.round(b * 100) / 100; + ua.balance = b; + ua.update(); + } +} \ No newline at end of file diff --git a/src/service/PlaceService.hx b/src/service/PlaceService.hx new file mode 100644 index 0000000..baae15b --- /dev/null +++ b/src/service/PlaceService.hx @@ -0,0 +1,29 @@ +package service; + +class PlaceService{ + + /** + * Geocode a place with Google Geocode API + */ + public static function geocode(p:db.Place):{lat:Float,lng:Float}{ + var apiKey = App.config.get("google_geocoding_key"); + if(apiKey==null) return null; + + var gc = new sugoi.apis.google.GeoCode(apiKey); + var address = p.getAddress(); + //var comp = "administrative_area:" + p.city + "|postal_code:" + p.zipCode + "|country:FR"; + //Sys.print(address+"
    "+comp+"
    "); + + var geo = gc.geocode( address , null); + var coords = geo[0].geometry.location; + + p.lock(); + p.lat = coords.lat; + p.lng = coords.lng; + p.update(); + + return {lat:p.lat,lng:p.lng}; + + } + +} \ No newline at end of file diff --git a/src/service/ProductService.hx b/src/service/ProductService.hx new file mode 100644 index 0000000..cb91873 --- /dev/null +++ b/src/service/ProductService.hx @@ -0,0 +1,57 @@ +package service; + +class ProductService{ + + + /** + * Batch disable products + */ + public static function batchDisableProducts(productIds:Array){ + + var data = {pids:productIds,enable:false}; + var contract = db.Product.manager.get(productIds[0], true).contract; + var products = contract.getProducts(false); + + App.current.event( BatchEnableProducts(data) ); + + for ( pid in data.pids){ + + var p = db.Product.manager.get(pid, true); + + if ( Lambda.find(products,function(p) return p.id==pid)==null ) throw 'product $pid is not in this contract !'; + + p.active = false; + p.update(); + } + } + + + /** + * Batch enable products + */ + public static function batchEnableProducts(productIds:Array){ + + var data = {pids:productIds,enable:true}; + var contract = db.Product.manager.get(productIds[0], true).contract; + var products = contract.getProducts(false); + + App.current.event( BatchEnableProducts(data) ); + + for ( pid in data.pids){ + + var p = db.Product.manager.get(pid, true); + + if ( Lambda.find(products,function(p) return p.id==pid)==null ) throw 'product $pid is not in this contract !'; + + p.active = true; + p.update(); + } + } + + inline public static function getHTPrice(ttcPrice:Float,vatRate:Float):Float{ + return ttcPrice / (1 + vatRate / 100); + } + + + +} \ No newline at end of file diff --git a/src/service/ReportService.hx b/src/service/ReportService.hx new file mode 100644 index 0000000..9706838 --- /dev/null +++ b/src/service/ReportService.hx @@ -0,0 +1,115 @@ +package service; +import Common; + +class ReportService{ + + /** + Get orders grouped by products. + */ + public static function getOrdersByProduct( distribution:db.Distribution, ?csv = false):Array{ + var view = App.current.view; + var t = sugoi.i18n.Locale.texts; + var where = ""; + + var exportName = t._("Delivery ::contractName:: of the ", {contractName:distribution.contract.name}) + distribution.date.toString().substr(0, 10); + where += ' and p.contractId = ${distribution.contract.id}'; + if (distribution.contract.type == db.Contract.TYPE_VARORDER ) { + where += ' and up.distributionId = ${distribution.id}'; + } + + //Product price will be an average if price changed + var sql = 'select + SUM(quantity) as quantity, + MAX(p.id) as pid, + p.name as pname, + AVG(up.productPrice) as price, + AVG(p.vat) as vat, + p.ref as ref, + SUM(quantity*up.productPrice) as totalTTC + from UserContract up, Product p + where up.productId = p.id + $where + group by ref,pname,price + order by pname asc;'; + + var res = sys.db.Manager.cnx.request(sql).results(); + var orders = []; + + //populate with full product names + for ( r in res){ + + var o : OrderByProduct = { + quantity:1.0 * r.quantity, + smartQt:"", + pid:r.pid, + pname:r.pname, + ref:r.ref, + priceHT: service.ProductService.getHTPrice(r.price,r.vat), + priceTTC: r.price, + vat:r.vat, + totalTTC : r.totalTTC, + totalHT : service.ProductService.getHTPrice( r.totalTTC ,r.vat), + weightOrVolume:"", + }; + + //smartQt + var p = db.Product.manager.get(r.pid, false); + if( p.hasFloatQt || p.variablePrice ){ + o.smartQt = view.smartQt(o.quantity, p.qt, p.unitType); + }else{ + o.smartQt = Std.string(o.quantity); + } + o.weightOrVolume = view.smartQt(o.quantity, p.qt, p.unitType); + + if ( /*p.hasFloatQt || p.variablePrice ||*/ p.qt==null || p.unitType==null){ + o.pname = p.name; + }else{ + o.pname = p.name + " " + view.formatNum(p.qt) +" " + view.unit(p.unitType, o.quantity > 1); + } + + //special case : if product is multiweight, we should count the records number ( and not SUM quantities ) + if (p.multiWeight){ + sql = 'select + COUNT(up.id) as quantity + from UserContract up, Product p + where up.productId = p.id and up.quantity > 0 and p.id=${p.id} + $where'; + var count = sys.db.Manager.cnx.request(sql).getIntResult(0); + o.smartQt = ""+count; + } + + orders.push(o); + } + + if (csv) { + var data = new Array(); + for (o in orders) { + data.push({ + "quantity":view.formatNum(o.quantity), + "pname":o.pname, + "ref":o.ref, + "priceHT":view.formatNum(o.priceHT), + "priceTTC":view.formatNum(o.priceTTC), + "totalHT":view.formatNum(o.totalHT), + "totalTTC":view.formatNum(o.totalTTC), + }); + } + + sugoi.tools.Csv.printCsvDataFromObjects(data, ["quantity", "pname","ref", "priceHT","priceTTC","totalHT","totalTTC"],"Export-"+exportName+"-par produits"); + return null; + }else{ + return orders; + } + } + + + public static function getTurnoverFromOrdersByProducts(ordersByProduct:Array):{turnoverHT:Float,turnoverTTC:Float}{ + var out = {turnoverHT:0.0,turnoverTTC:0.0}; + for( o in ordersByProduct){ + out.turnoverHT += o.totalHT; + out.turnoverTTC += o.totalTTC; + } + return out; + } + +} \ No newline at end of file diff --git a/src/service/UserService.hx b/src/service/UserService.hx new file mode 100644 index 0000000..09fd01d --- /dev/null +++ b/src/service/UserService.hx @@ -0,0 +1,120 @@ +package service; +import tink.core.Error; + +/** + * User Service + * @author fbarbut + */ +class UserService +{ + + var user : db.User; + + public function new(u:db.User) + { + this.user = u; + } + + /** + * User login service + * @param email + * @param password + */ + public static function login(email:String, password:String){ + + var t = sugoi.i18n.Locale.texts; + + //user exists ? + var user = db.User.manager.select( $email == email || $email2 == email , true); + if (user == null) throw new Error(404,t._("There is no account with this email")); + + //new account + if (!user.isFullyRegistred()) { + var group = user.getAmaps().first(); + user.sendInvitation(group); + var text = t._("Your account have not been validated yet. We sent an e-mail to ::email:: to finalize your subscription!",{email:user.email}); + throw new Error(403,text); + } + + var pass = haxe.crypto.Md5.encode( App.config.get('key') + password ); + + if (user.pass != pass) { + throw new Error(403,t._("Invalid password")); + } + + db.User.login(user, email); + + //register the user to the current group if needed + var group = App.current.getCurrentGroup(); + if (group != null && group.regOption == db.Amap.RegOption.Open && db.UserAmap.get(user, group) == null){ + user.makeMemberOf(group); + } + + } + + /** + Full registration by a user himself + **/ + public static function register(firstName:String, lastName:String, email:String, phone:String, pass:String){ + + var t = sugoi.i18n.Locale.texts; + + if (!sugoi.form.validators.EmailValidator.check(email)){ + throw new Error(500,t._("Invalid email address")); + } + + if ( db.User.getSameEmail(email).length > 0 ) { + throw new Error(409,t._("We already have an account with this email address")); + } + + var user = new db.User(); + user.email = email; + user.firstName = firstName; + user.lastName = lastName; + user.phone = phone; + user.setPass(pass); + user.insert(); + + var group = App.current.getCurrentGroup(); + if (group != null && group.regOption == db.Amap.RegOption.Open){ + user.makeMemberOf(group); + } + + db.User.login(user, email); + } + + /** + Soft registration : + - Somebody creates/import a new user , + - or pre-registration in a waiting list + **/ + public static function softRegistration(firstName:String, lastName:String, email:String){ + + var t = sugoi.i18n.Locale.texts; + + if (!sugoi.form.validators.EmailValidator.check(email)){ + throw new Error(500,t._("Invalid email address")); + } + + if ( db.User.getSameEmail(email).length > 0 ) { + throw new Error(409,t._("We already have an account with this email address")); + } + + var user = new db.User(); + user.email = email; + user.firstName = firstName; + user.lastName = lastName; + user.insert(); + + return user; + } + + /** + * get users belonging to a group + * @param group - + * @return Array + */ + public static function getFromGroup(group:db.Amap):Array{ + return Lambda.array( group.getMembers() ); + } +} \ No newline at end of file diff --git a/src/service/WaitingListService.hx b/src/service/WaitingListService.hx new file mode 100644 index 0000000..0a79fc3 --- /dev/null +++ b/src/service/WaitingListService.hx @@ -0,0 +1,140 @@ +package service; +import tink.core.Error; +import db.UserAmap.Right; + +class WaitingListService{ + + + public static function registerToWl(user:db.User,group:db.Amap,message:String){ + var t = sugoi.i18n.Locale.texts; + + canRegister(user,group); + + var w = new db.WaitingList(); + w.user = user; + w.group = group; + w.message = message; + w.insert(); + + //emails + var html = t._("

    ::name:: suscribed to the waiting list of ::group:: on ::date::

    ",{ + group:group.name, + name:user.name, + date:App.current.view.hDate(Date.now()) + }); + if(message!=null && message!=""){ + html += t._("

    He/she left this message :
    \"::message::\"

    ",{message:message}); + } + + for( u in service.GroupService.getGroupMembersWithRights(group,[Right.GroupAdmin,Right.Membership]) ){ + + App.quickMail( + u.email, + t._("[::group::] ::name:: suscribed to the waiting list.",{group:group.name,name:user.name}), + html, + group + ); + } + } + + public static function canRegister(user:db.User,group:db.Amap){ + var t = sugoi.i18n.Locale.texts; + + if ( db.WaitingList.manager.select($amapId == group.id && $user == user) != null) { + throw new Error(t._("You are already in the waiting list of this group")); + } + if ( db.UserAmap.manager.select($amapId == group.id && $user == user) != null) { + throw new Error(t._("You are already member of this group.")); + } + } + + /** + the user cancels his request + **/ + public static function removeFromWl(user:db.User,group:db.Amap){ + var t = sugoi.i18n.Locale.texts; + if ( user == null) { + throw new Error(t._("You should be logged in.")); + } + + var wl = db.WaitingList.manager.select($amapId == group.id && $user == user,true); + + if ( wl == null) { + throw new Error(t._("You are not in the waiting list of this group")); + } + wl.delete(); + } + + /** + an admin cancels a request + **/ + public static function cancelRequest(user:db.User,group:db.Amap){ + var t = sugoi.i18n.Locale.texts; + if ( user == null) throw "user is null"; + var wl = db.WaitingList.manager.select($amapId == group.id && $user == user,true); + if ( wl == null) throw "this user is not in waiting list"; + + //email the requester + App.quickMail( + wl.user.email, + t._("[::group::] Membership request refused.",{group:group.name}), + t._("Your membership request for ::group:: has been refused.",{group:group.name}) + ); + + //email others admin + for( u in service.GroupService.getGroupMembersWithRights(group,[Right.GroupAdmin,Right.Membership]) ){ + if(u.id==App.current.user.id) continue; + App.quickMail( + u.email, + t._("[::group::] ::name:: membership request has been refused by ::admin::.",{group:group.name, name:user.name, admin:App.current.user.name}), + t._("

    ::name:: was registred to the waiting list.

    ::admin:: has refused his/her request.

    ",{name:user.name, admin:App.current.user.name}), + group + ); + } + + wl.delete(); + } + + /** + an admin approves a request + **/ + public static function approveRequest(user:db.User,group:db.Amap){ + var t = sugoi.i18n.Locale.texts; + if ( user == null) throw "user is null"; + var wl = db.WaitingList.manager.select($amapId == group.id && $user == user,true); + if ( wl == null) throw "this user is not in waiting list"; + + if (db.UserAmap.get(user, group, false) == null){ + var ua = new db.UserAmap(); + ua.amap = wl.group; + ua.user = wl.user; + ua.insert(); + } + + wl.delete(); + + //email the requester + App.quickMail( + wl.user.email, + t._("[::group::] Membership request accepted.",{group:group.name}), + t._("

    Your membership request for ::group:: has been accepted !

    You're now a member of the group.

    ",{group:group.name}), + group + ); + + //email others admin + for( u in service.GroupService.getGroupMembersWithRights(group,[Right.GroupAdmin,Right.Membership]) ){ + if(u.id==App.current.user.id) continue; + App.quickMail( + u.email, + t._("[::group::] ::name:: membership request has been accepted by ::admin::.",{group:group.name, name:user.name, admin:App.current.user.name}), + t._("

    ::name:: was registred to the waiting list.

    ::admin:: has accepted his/her request.

    ",{name:user.name, admin:App.current.user.name}), + group + ); + } + + wl.delete(); + } + + + +} \ No newline at end of file diff --git a/src/test/TestDistributions.hx b/src/test/TestDistributions.hx new file mode 100644 index 0000000..fb6e5c0 --- /dev/null +++ b/src/test/TestDistributions.hx @@ -0,0 +1,299 @@ +package test; + +/** + * Test distribution creation + * + * @author web-wizard + */ +class TestDistributions extends haxe.unit.TestCase +{ + + public function new(){ + super(); + } + + /** + * + */ + override function setup(){ + TestSuite.initDB(); + TestSuite.initDatas(); + } + + /** + * Check that we can add a distribution outside existing time range for a specific contract and place + * Check that we can't add a distribution overlapping with existing distribution for a specific contract and place + */ + function testOverlapping() { + var existingDistrib = TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE; + var e1 = null; + try { + var distrib1 = service.DistributionService.create(existingDistrib.contract,new Date(2018, 5, 1, 18, 0, 0),new Date(2018, 5, 1, 18, 30, 0), + existingDistrib.place.id,null,null,null,null,new Date(2018, 4, 1, 18, 0, 0),new Date(2018, 4, 30, 18, 30, 0)); + } + catch(x:tink.core.Error) { + e1 = x; + } + assertEquals(e1, null); + + //existingDistrib.date <= distrib2.date && existingDistrib.end >= distrib2.date + var e2 = null; + try { + var distrib2 = service.DistributionService.create(existingDistrib.contract,new Date(2017, 5, 1, 19, 30, 0),new Date(2017, 5, 1, 20, 30, 0), + existingDistrib.place.id,null,null,null,null,new Date(2017, 4, 1, 18, 0, 0),new Date(2017, 4, 30, 18, 30, 0)); + } + catch(x:tink.core.Error) { + e2 = x; + } + assertEquals(e2.message, "There is already a distribution at this place overlapping with the time range you've selected."); + + //existingDistrib.date <= distrib3.end && existingDistrib.end >= distrib3.end + var e3 = null; + try{ + var distrib3 = service.DistributionService.create(existingDistrib.contract,new Date(2017, 5, 1, 17, 30, 0),new Date(2017, 5, 1, 19, 30, 0), + existingDistrib.place.id,null,null,null,null,new Date(2017, 4, 1, 18, 0, 0),new Date(2017, 4, 30, 18, 30, 0)); + } + catch(x:tink.core.Error){ + e3 = x; + } + assertEquals(e3.message, "There is already a distribution at this place overlapping with the time range you've selected."); + + //existingDistrib.date >= distrib4.date && existingDistrib.end <= distrib4.end + var e4 = null; + try{ + var distrib4 = service.DistributionService.create(existingDistrib.contract,new Date(2017, 5, 1, 17, 30, 0),new Date(2017, 5, 1, 21, 30, 0), + existingDistrib.place.id,null,null,null,null,new Date(2017, 4, 1, 18, 0, 0),new Date(2017, 4, 30, 18, 30, 0)); + } + catch(x:tink.core.Error){ + e4 = x; + } + assertEquals(e4.message, "There is already a distribution at this place overlapping with the time range you've selected."); + + //existingDistrib.date > distrib5.end + var e5 = null; + try{ + var distrib5 = service.DistributionService.create(existingDistrib.contract,new Date(2017, 5, 1, 17, 30, 0),new Date(2017, 5, 1, 18, 59, 0), + existingDistrib.place.id,null,null,null,null,new Date(2017, 4, 1, 18, 0, 0),new Date(2017, 4, 30, 18, 30, 0)); + } + catch(x:tink.core.Error){ + e5 = x; + } + assertEquals(e5, null); + + //existingDistrib.date > distrib6.end + var e6 = null; + try{ + var distrib6 = service.DistributionService.create(existingDistrib.contract,new Date(2017, 3, 1, 17, 30, 0),new Date(2017, 3, 1, 18, 59, 0), + existingDistrib.place.id,null,null,null,null,new Date(2017, 2, 1, 18, 0, 0),new Date(2017, 3, 30, 18, 30, 0)); + } + catch(x:tink.core.Error){ + e6 = x; + } + assertEquals(e6.message, "The distribution start date must be set after the orders end date."); + + } + + /** + * Check that we can add a distribution outside existing time range for a specific contract and place + * Check that we can't add a distribution overlapping with existing distribution for a specific contract and place + */ + function testEdit() { + + //TO DO + assertEquals(true, true); + } + + function testCreateCycle() { + TestSuite.CONTRAT_LEGUMES.startDate = new Date(2018, 0, 1, 0, 0, 0); + TestSuite.CONTRAT_LEGUMES.endDate = new Date(2019, 11, 31, 23, 59, 0); + TestSuite.CONTRAT_LEGUMES.update(); + + var weeklyDistribCycle = service.DistributionService.createCycle(TestSuite.CONTRAT_LEGUMES,Weekly,new Date(2018, 11, 24, 0, 0, 0), + new Date(2019, 0, 24, 0, 0, 0),new Date(2018, 5, 4, 13, 0, 0),new Date(2018, 5, 4, 14, 0, 0),10,2, + new Date(2018, 5, 4, 8, 0, 0),new Date(2018, 5, 4, 23, 0, 0),TestSuite.PLACE_DU_VILLAGE.id); + + var weeklyDistribs = Lambda.array(db.Distribution.manager.search($distributionCycle == weeklyDistribCycle, false)); + assertEquals(weeklyDistribs.length, 5); + assertEquals(weeklyDistribs[0].date.toString(), new Date(2018, 11, 24, 13, 0, 0).toString()); + assertEquals(weeklyDistribs[0].end.toString(), new Date(2018, 11, 24, 14, 0, 0).toString()); + assertEquals(weeklyDistribs[1].date.toString(), new Date(2018, 11, 31, 13, 0, 0).toString()); + assertEquals(weeklyDistribs[1].end.toString(), new Date(2018, 11, 31, 14, 0, 0).toString()); + assertEquals(weeklyDistribs[2].date.toString(), new Date(2019, 0, 7, 13, 0, 0).toString()); + assertEquals(weeklyDistribs[2].end.toString(), new Date(2019, 0, 7, 14, 0, 0).toString()); + assertEquals(weeklyDistribs[3].date.toString(), new Date(2019, 0, 14, 13, 0, 0).toString()); + assertEquals(weeklyDistribs[3].end.toString(), new Date(2019, 0, 14, 14, 0, 0).toString()); + assertEquals(weeklyDistribs[4].date.toString(), new Date(2019, 0, 21, 13, 0, 0).toString()); + assertEquals(weeklyDistribs[4].end.toString(), new Date(2019, 0, 21, 14, 0, 0).toString()); + service.DistributionService.deleteCycleDistribs(weeklyDistribCycle); + + var monthlyDistribCycle = service.DistributionService.createCycle(TestSuite.CONTRAT_LEGUMES,Monthly,new Date(2018, 9, 30, 0, 0, 0), + new Date(2019, 2, 31, 0, 0, 0),new Date(2018, 5, 4, 13, 0, 0),new Date(2018, 5, 4, 14, 0, 0),10,2, + new Date(2018, 5, 4, 8, 0, 0),new Date(2018, 5, 4, 23, 0, 0),TestSuite.PLACE_DU_VILLAGE.id); + + var monthlyDistribs = Lambda.array(db.Distribution.manager.search($distributionCycle == monthlyDistribCycle, false)); + assertEquals(monthlyDistribs.length, 6); + assertEquals(monthlyDistribs[0].date.toString(), new Date(2018, 9, 30, 13, 0, 0).toString()); + assertEquals(monthlyDistribs[0].end.toString(), new Date(2018, 9, 30, 14, 0, 0).toString()); + assertEquals(monthlyDistribs[1].date.toString(), new Date(2018, 10, 27, 13, 0, 0).toString()); + assertEquals(monthlyDistribs[1].end.toString(), new Date(2018, 10, 27, 14, 0, 0).toString()); + assertEquals(monthlyDistribs[2].date.toString(), new Date(2018, 11, 25, 13, 0, 0).toString()); + assertEquals(monthlyDistribs[2].end.toString(), new Date(2018, 11, 25, 14, 0, 0).toString()); + assertEquals(monthlyDistribs[3].date.toString(), new Date(2019, 0, 29, 13, 0, 0).toString()); + assertEquals(monthlyDistribs[3].end.toString(), new Date(2019, 0, 29, 14, 0, 0).toString()); + assertEquals(monthlyDistribs[4].date.toString(), new Date(2019, 1, 26, 13, 0, 0).toString()); + assertEquals(monthlyDistribs[4].end.toString(), new Date(2019, 1, 26, 14, 0, 0).toString()); + assertEquals(monthlyDistribs[5].date.toString(), new Date(2019, 2, 26, 13, 0, 0).toString()); + assertEquals(monthlyDistribs[5].end.toString(), new Date(2019, 2, 26, 14, 0, 0).toString()); + service.DistributionService.deleteCycleDistribs(monthlyDistribCycle); + + var biweeklyDistribCycle = service.DistributionService.createCycle(TestSuite.CONTRAT_LEGUMES,BiWeekly,new Date(2018, 9, 30, 0, 0, 0), + new Date(2019, 0, 31, 0, 0, 0),new Date(2018, 5, 4, 13, 0, 0),new Date(2018, 5, 4, 14, 0, 0),10,2, + new Date(2018, 5, 4, 8, 0, 0),new Date(2018, 5, 4, 23, 0, 0),TestSuite.PLACE_DU_VILLAGE.id); + + var biweeklyDistribs = Lambda.array(db.Distribution.manager.search($distributionCycle == biweeklyDistribCycle, false)); + assertEquals(biweeklyDistribs.length, 7); + assertEquals(biweeklyDistribs[0].date.toString(), new Date(2018, 9, 30, 13, 0, 0).toString()); + assertEquals(biweeklyDistribs[0].end.toString(), new Date(2018, 9, 30, 14, 0, 0).toString()); + assertEquals(biweeklyDistribs[1].date.toString(), new Date(2018, 10, 13, 13, 0, 0).toString()); + assertEquals(biweeklyDistribs[1].end.toString(), new Date(2018, 10, 13, 14, 0, 0).toString()); + assertEquals(biweeklyDistribs[2].date.toString(), new Date(2018, 10, 27, 13, 0, 0).toString()); + assertEquals(biweeklyDistribs[2].end.toString(), new Date(2018, 10, 27, 14, 0, 0).toString()); + assertEquals(biweeklyDistribs[3].date.toString(), new Date(2018, 11, 11, 13, 0, 0).toString()); + assertEquals(biweeklyDistribs[3].end.toString(), new Date(2018, 11, 11, 14, 0, 0).toString()); + assertEquals(biweeklyDistribs[4].date.toString(), new Date(2018, 11, 25, 13, 0, 0).toString()); + assertEquals(biweeklyDistribs[4].end.toString(), new Date(2018, 11, 25, 14, 0, 0).toString()); + assertEquals(biweeklyDistribs[5].date.toString(), new Date(2019, 0, 8, 13, 0, 0).toString()); + assertEquals(biweeklyDistribs[5].end.toString(), new Date(2019, 0, 8, 14, 0, 0).toString()); + assertEquals(biweeklyDistribs[6].date.toString(), new Date(2019, 0, 22, 13, 0, 0).toString()); + assertEquals(biweeklyDistribs[6].end.toString(), new Date(2019, 0, 22, 14, 0, 0).toString()); + service.DistributionService.deleteCycleDistribs(biweeklyDistribCycle); + + var triweeklyDistribCycle = service.DistributionService.createCycle(TestSuite.CONTRAT_LEGUMES,TriWeekly,new Date(2018, 9, 30, 0, 0, 0), + new Date(2019, 0, 31, 0, 0, 0),new Date(2018, 5, 4, 13, 0, 0),new Date(2018, 5, 4, 14, 0, 0),10,2, + new Date(2018, 5, 4, 8, 0, 0),new Date(2018, 5, 4, 23, 0, 0),TestSuite.PLACE_DU_VILLAGE.id); + + var triweeklyDistribs = Lambda.array(db.Distribution.manager.search($distributionCycle == triweeklyDistribCycle, false)); + assertEquals(triweeklyDistribs.length, 5); + assertEquals(triweeklyDistribs[0].date.toString(), new Date(2018, 9, 30, 13, 0, 0).toString()); + assertEquals(triweeklyDistribs[0].end.toString(), new Date(2018, 9, 30, 14, 0, 0).toString()); + assertEquals(triweeklyDistribs[1].date.toString(), new Date(2018, 10, 20, 13, 0, 0).toString()); + assertEquals(triweeklyDistribs[1].end.toString(), new Date(2018, 10, 20, 14, 0, 0).toString()); + assertEquals(triweeklyDistribs[2].date.toString(), new Date(2018, 11, 11, 13, 0, 0).toString()); + assertEquals(triweeklyDistribs[2].end.toString(), new Date(2018, 11, 11, 14, 0, 0).toString()); + assertEquals(triweeklyDistribs[3].date.toString(), new Date(2019, 0, 1, 13, 0, 0).toString()); + assertEquals(triweeklyDistribs[3].end.toString(), new Date(2019, 0, 1, 14, 0, 0).toString()); + assertEquals(triweeklyDistribs[4].date.toString(), new Date(2019, 0, 22, 13, 0, 0).toString()); + assertEquals(triweeklyDistribs[4].end.toString(), new Date(2019, 0, 22, 14, 0, 0).toString()); + service.DistributionService.deleteCycleDistribs(triweeklyDistribCycle); + } + + function testDelete() { + //A variable contract with a distribution that has orders + var ordersDistrib = TestSuite.DISTRIB_LEGUMES_RUE_SAUCISSE; + var ordersDistribId = ordersDistrib.id; + var chicken = TestSuite.CHICKEN; + var order = service.OrderService.make(TestSuite.FRANCOIS, 1, chicken, ordersDistrib.id); + + var e = null; + try{ + service.DistributionService.delete(ordersDistrib); + } + catch(x:tink.core.Error){ + e = x; + } + assertEquals(e.message, "Deletion non possible: some orders are saved for this delivery."); + assertTrue(db.Distribution.manager.get(ordersDistribId) != null); + + //A variable contract with a distribution that has no orders + var noOrdersDistrib = TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE; + var noOrdersDistribId = noOrdersDistrib.id; + + var e = null; + try{ + service.DistributionService.delete(noOrdersDistrib); + } + catch(x:tink.core.Error){ + e = x; + } + assertEquals(e, null); + assertEquals(db.Distribution.manager.get(noOrdersDistribId), null); + + //An Amap contract with a distribution that has orders + var amapDistrib = TestSuite.DISTRIB_CONTRAT_AMAP; + var amapDistribId = amapDistrib.id; + var panier = TestSuite.PANIER_AMAP_LEGUMES; + var amapOrder = service.OrderService.make(TestSuite.FRANCOIS, 1, panier, amapDistrib.id); + + var e = null; + try{ + service.DistributionService.delete(amapDistrib); + } + catch(x:tink.core.Error){ + e = x; + } + assertEquals(e, null); + assertEquals(db.Distribution.manager.get(amapDistribId), null); + + } + + function testUpdateAmapContractOperations(){ + + var t = sugoi.i18n.Locale.texts; + + //Take an amap contract with payments enabled + //Take 2 users and make orders for each + var amapDistrib = TestSuite.DISTRIB_CONTRAT_AMAP; + var contract = amapDistrib.contract; + var panier = TestSuite.PANIER_AMAP_LEGUMES; + var francoisOrder = service.OrderService.make(TestSuite.FRANCOIS, 1, panier, amapDistrib.id); + db.Operation.onOrderConfirm([francoisOrder]); + var sebOrder = service.OrderService.make(TestSuite.SEB, 3, panier, amapDistrib.id); + db.Operation.onOrderConfirm([sebOrder]); + + //Check initial operation names and amounts + var francoisOperation = db.Operation.findCOrderTransactionFor(contract, TestSuite.FRANCOIS); + assertEquals(francoisOperation.name, "Contrat AMAP Légumes (La ferme de la Galinette) 1 deliveries"); + assertEquals(francoisOperation.amount, -13); + var sebOperation = db.Operation.findCOrderTransactionFor(contract, TestSuite.SEB); + assertEquals(sebOperation.name, "Contrat AMAP Légumes (La ferme de la Galinette) 1 deliveries"); + assertEquals(sebOperation.amount, -39); + + //Add a distrib + var distrib = null; + var e = null; + try{ + distrib = service.DistributionService.create(contract,new Date(2018, 5, 1, 18, 0, 0),new Date(2018, 5, 1, 18, 30, 0), + amapDistrib.place.id,null,null,null,null,new Date(2018, 4, 1, 18, 0, 0),new Date(2018, 4, 30, 18, 30, 0)); + } + catch(x:tink.core.Error){ + e = x; + } + //Check names and amounts are modified accordingly + assertEquals(francoisOperation.name, "Contrat AMAP Légumes (La ferme de la Galinette) 2 deliveries"); + assertEquals(francoisOperation.amount, -26); + var sebOperation = db.Operation.findCOrderTransactionFor(contract, TestSuite.SEB); + assertEquals(sebOperation.name, "Contrat AMAP Légumes (La ferme de la Galinette) 2 deliveries"); + assertEquals(sebOperation.amount, -78); + + //Add a distrib cycle + var weeklyDistribCycle = service.DistributionService.createCycle(contract,Weekly,new Date(2018, 11, 24, 0, 0, 0), + new Date(2019, 0, 24, 0, 0, 0),new Date(2018, 5, 4, 13, 0, 0),new Date(2018, 5, 4, 14, 0, 0),10,2, + new Date(2018, 5, 4, 8, 0, 0),new Date(2018, 5, 4, 23, 0, 0),TestSuite.PLACE_DU_VILLAGE.id); + //Check names and amounts are modified accordingly + assertEquals(francoisOperation.name, "Contrat AMAP Légumes (La ferme de la Galinette) 7 deliveries"); + assertEquals(francoisOperation.amount, -91); + var sebOperation = db.Operation.findCOrderTransactionFor(contract, TestSuite.SEB); + assertEquals(sebOperation.name, "Contrat AMAP Légumes (La ferme de la Galinette) 7 deliveries"); + assertEquals(sebOperation.amount, -273); + + //Delete the distrib cycle + service.DistributionService.deleteCycleDistribs(weeklyDistribCycle); + //Check names and amounts are modified accordingly + assertEquals(francoisOperation.name, "Contrat AMAP Légumes (La ferme de la Galinette) 2 deliveries"); + assertEquals(francoisOperation.amount, -26); + var sebOperation = db.Operation.findCOrderTransactionFor(contract, TestSuite.SEB); + assertEquals(sebOperation.name, "Contrat AMAP Légumes (La ferme de la Galinette) 2 deliveries"); + assertEquals(sebOperation.amount, -78); + } + +} \ No newline at end of file diff --git a/src/test/TestOrders.hx b/src/test/TestOrders.hx new file mode 100644 index 0000000..5d80007 --- /dev/null +++ b/src/test/TestOrders.hx @@ -0,0 +1,473 @@ +package test; +import Common; +import service.OrderService; +/** + * Test order making, updating and deleting + * + * @author fbarbut + */ +class TestOrders extends haxe.unit.TestCase +{ + + public function new(){ + super(); + } + + var c : db.Contract; + var p : db.Product; + var bob : db.User; + + /** + * get a contract + a user + a product + empty orders + */ + override function setup(){ + + TestSuite.initDB(); + TestSuite.initDatas(); + + db.Basket.emptyCache(); + + c = TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE.contract; + + p = TestSuite.STRAWBERRIES; + p.lock(); + p.stock = 8; + p.update(); + + bob = db.User.manager.get(1); + + } + + /** + Test Basket creation and numbering + **/ + public function testBasket(){ + + var d = TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE; + + var o = OrderService.make(TestSuite.FRANCOIS, 3, TestSuite.STRAWBERRIES, d.id); + assertEquals(1, o.basket.num); + + var o = OrderService.make(TestSuite.SEB, 1, TestSuite.STRAWBERRIES, d.id); + assertEquals(2, o.basket.num); + + //order again, should keep existing basket number + var o = OrderService.make(TestSuite.FRANCOIS, 1, TestSuite.APPLES, d.id); + assertEquals(1, o.basket.num); + + //check bug of 2018-07 : changing the date and place of the distribution leads to lost basket (because basket were indexed on user-date-place) + d.lock(); + d.date = new Date(2028,1,1,0,0,0); + + var place = new db.Place(); + place.name = "Chez Momo"; + place.zipCode = "54"; + place.amap = d.contract.amap; + place.insert(); + + d.place = place; + d.update(); + + //Seb's basket is still 2 + var basket = db.Basket.get(TestSuite.SEB,place,d.date); + assertEquals(2, basket.num); + + var o = OrderService.make(TestSuite.SEB, 1, TestSuite.APPLES, d.id); + assertEquals(2, o.basket.num); + + var o2 = OrderService.edit(o,5,true,null,false); + assertEquals(2, o2.basket.num); + + //order to a different distrib in same contract should start a new numbering + var d2 = service.DistributionService.create(d.contract,new Date(2026,6,6,0,0,0),new Date(2026,6,6,1,0,0),place.id,null,null,null,null,new Date(2026,6,4,0,0,0),new Date(2026,6,5,0,0,0)); + var o = OrderService.make(TestSuite.SEB, 12, TestSuite.APPLES, d2.id); + assertEquals(1, o.basket.num); + + + } + + /** + * make orders & stock management + */ + public function testStocks(){ + + var stock = p.stock; + + assertTrue(c.type == db.Contract.TYPE_VARORDER); + assertTrue(c.flags.has(db.Contract.ContractFlags.StockManagement)); + assertTrue(stock == 8); + + //bob orders 3 strawberries, stock fall to 2 + //order is update to 6 berries + App.current.eventDispatcher.addOnce(function(e:Event){ + switch(e){ + case StockMove(e): + assertTrue(e.move==-3); + assertTrue(e.product==p); + default: + } + }); + var o = OrderService.make(bob, 3, p, TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE.id); + assertTrue(p.stock == 5); + assertTrue(o.quantity == 3); + + //bob orders 6 more. stock fall to 0, order is reduced to 5 + //quantity is not 9 but 8 + App.current.eventDispatcher.addOnce(function(e:Event){ + switch(e){ + case StockMove(e): + assertTrue(e.move==-5); + assertTrue(e.product==p); + default: + } + }); + var o = OrderService.make(bob, 6, p, TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE.id); + assertTrue(p.stock == 0); + assertTrue(o.quantity == 8); + + //bob orders again but cant order anything + var o = OrderService.make(bob, 3, p, TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE.id); + assertTrue(p.stock == 0); + assertTrue(o.quantity == 8); + + } + + /** + * test edit orders and stock management + */ + function testOrderEdit(){ + + var o = db.UserContract.manager.select( $user == bob && $product == p, true); + + //no order, stock at 8 + assertEquals(p.stock , 8); + assertEquals(o , null); + + //bob orders 3 strawberries + var o = OrderService.make(bob, 3, p , TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE.id); + assertEquals(o.product.name, p.name); + assertEquals(o.quantity, 3); + assertEquals(p.stock , 5); + + //order edit, order 6 berries + App.current.eventDispatcher.addOnce(function(e:Event){ + switch(e){ + case StockMove(e): + assertTrue(e.move==-3); + assertTrue(e.product==p); + default: + } + }); + var o = OrderService.edit(o, 6); + assertTrue(p.stock == 2); + assertTrue(o.quantity == 6); + + //order edit, order 9 berries. ( 3 more, but stock fall to 0, reduced to 2 ) + App.current.eventDispatcher.addOnce(function(e:Event){ + switch(e){ + case StockMove(e): + assertEquals( -2.0 , e.move ); + assertEquals( p , e.product); + default: + } + }); + var o = OrderService.edit(o, 9); + assertEquals(0.0 , p.stock); + assertEquals(8.0 , o.quantity); + + //order more, but stock at 0 + var o = OrderService.edit(o, 12); + assertEquals(0.0 , p.stock); + assertEquals(8.0 , o.quantity); + + //order less + var o = OrderService.edit(o, 6); + assertEquals(2.0 , p.stock); + assertEquals(6.0 , o.quantity); + + + //floatQt : ordering float quantities should throw an exception + var err = null; + try{ + var o = OrderService.edit(o, 6.4); + }catch(e:tink.core.Error){ + err = e.message; + } + assertTrue( err!=null ); + } + + /** + * test orders with multiweight product + */ + function testOrderWithMultiWeightProduct(){ + + var chicken = TestSuite.CHICKEN; + var distrib = db.Distribution.manager.select($contract == chicken.contract, false); + + var order = OrderService.make(bob, 1, chicken, distrib.id); + assertEquals(1.0, order.quantity); + assertEquals(chicken.id, order.product.id); + assertEquals(chicken.price, order.productPrice); + + //order 2 more, should not aggregate because multiWeight is true + var order2 = OrderService.make(bob, 2, chicken, distrib.id); + + assertTrue(order2.id != order.id); + + //we should get 3 different orders + var orders = distrib.getOrders(); + + //trace(OrderService.prepare(orders)); + + assertEquals(3, orders.length); + for ( o in orders){ + assertEquals(o.user.id, bob.id); + assertEquals(o.product.id, chicken.id); + assertEquals(1.0, o.quantity); + } + + } + + /** + * @author fbarbut + * @date 2018-01-26 + * order a product, edit and set qt to zero, order again. + * the same record should be re-used ( if not multiweight ) + */ + function testMakeOrderAndZeroQuantity(){ + var fraises = TestSuite.STRAWBERRIES; + var distrib = db.Distribution.manager.select($contract == fraises.contract, false); + + var order = OrderService.make(bob, 1, fraises, distrib.id); + + order = OrderService.edit(order, 0); + + assertEquals(0.0, order.quantity); + + var order2 = OrderService.make(bob, 1, fraises, distrib.id); + + var bobOrders = []; + for ( o in distrib.getOrders()) if (o.user.id == bob.id) bobOrders.push(o); + + assertFalse(fraises.multiWeight); + assertEquals(1, bobOrders.length); + assertEquals(1.0, bobOrders[0].quantity); + } + + /** + * Test order deletion and operation deletion in various contexts + @author jbarbic + */ + function testDelete(){ + + var t = sugoi.i18n.Locale.texts; + + //[Test case] Should throw an error when trying to delete order and that the quantity is not zero + var amapDistrib = TestSuite.DISTRIB_CONTRAT_AMAP; + var amapContract = amapDistrib.contract; + var order = OrderService.make(TestSuite.FRANCOIS, 1, TestSuite.PANIER_AMAP_LEGUMES, amapDistrib.id); + var orderId = order.id; + db.Operation.onOrderConfirm([order]); + var e1 = null; + try { + service.OrderService.delete(order); + } + catch(x:tink.core.Error){ + e1 = x; + } + assertEquals(e1.message, "Deletion not possible: quantity is not zero."); + assertTrue(db.UserContract.manager.get(orderId) != null); + + //[Test case] Amap contract and quantity zero with payments disabled + //Check that order is deleted + order = OrderService.edit(order, 0); + var e2 = null; + try { + OrderService.delete(order); + } + catch(x:tink.core.Error){ + e2 = x; + } + assertEquals(e2, null); + assertEquals(db.UserContract.manager.get(orderId), null); + + //[Test case] Amap contract and quantity zero with payments enabled and 2 orders + //Check that first order is deleted but operation amount is at 0 + //Check that operation is deleted only at the second order deletion + var order1 = OrderService.make(TestSuite.FRANCOIS, 1, TestSuite.PANIER_AMAP_LEGUMES, amapDistrib.id); + db.Operation.onOrderConfirm([order1]); + var order1Id = order1.id; + order1 = OrderService.edit(order1, 0); + db.Operation.onOrderConfirm([order1]); + var order2 = OrderService.make(TestSuite.FRANCOIS, 1, TestSuite.PANIER_AMAP_LEGUMES, amapDistrib.id); + db.Operation.onOrderConfirm([order2]); + var order2Id = order2.id; + order2 = OrderService.edit(order2, 0); + db.Operation.onOrderConfirm([order2]); + var operation = db.Operation.findCOrderTransactionFor(amapContract, TestSuite.FRANCOIS); + var operationId = operation.id; + var e3 = null; + try { + service.OrderService.delete(order1); + } + catch(x:tink.core.Error){ + e3 = x; + } + assertEquals(e3, null); + assertEquals(db.UserContract.manager.get(order1Id), null); + assertTrue(db.Operation.manager.get(operationId) != null); + assertEquals(operation.name, "Contrat AMAP Légumes (La ferme de la Galinette) 1 deliveries"); + assertEquals(operation.amount, 0); + var e4 = null; + try { + service.OrderService.delete(order2); + } + catch(x:tink.core.Error){ + e4 = x; + } + assertEquals(null, e4); + assertEquals(null, db.UserContract.manager.get(order2Id)); + assertEquals(null, db.Operation.manager.get(operationId)); + + //[Test case] Var Order contract and quantity zero with payments disabled + //Check that order is deleted + var variableDistrib = TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE; + + var g = variableDistrib.contract.amap; + g.lock(); + g.flags.unset(HasPayments); + g.update(); + + var order = OrderService.make(TestSuite.FRANCOIS, 2, TestSuite.STRAWBERRIES, variableDistrib.id); + var orderId = order.id; + db.Operation.onOrderConfirm([order]); + order = OrderService.edit(order, 0); + db.Operation.onOrderConfirm([order]); + var e1 = null; + try { + service.OrderService.delete(order); + } + catch(x:tink.core.Error){ + e1 = x; + } + assertEquals(false,variableDistrib.contract.amap.hasPayments() ); + assertEquals(null, e1); + assertEquals(null, db.UserContract.manager.get(orderId)); + + //[Test case] Var Order contract and quantity zero with payments enabled and 2 orders + //Check that first order is deleted + //Check that operation is deleted only at the second order deletion + variableDistrib = TestSuite.DISTRIB_FRUITS_PLACE_DU_VILLAGE; + var variableContract = variableDistrib.contract; + var g = variableDistrib.contract.amap; + g.lock(); + g.flags.set(HasPayments); + g.update(); + assertTrue(variableContract.amap.hasPayments()); + + var order1 = OrderService.make(TestSuite.FRANCOIS, 2, TestSuite.STRAWBERRIES, variableDistrib.id); + db.Operation.onOrderConfirm([order1]); + var order1Id = order1.id; + + var order2 = OrderService.make(TestSuite.FRANCOIS, 3, TestSuite.APPLES, variableDistrib.id); + db.Operation.onOrderConfirm([order2]); + var order2Id = order2.id; + + order1 = OrderService.edit(order1, 0); + db.Operation.onOrderConfirm([order1]); + + order2 = OrderService.edit(order2, 0); + db.Operation.onOrderConfirm([order2]); + + assertEquals(2, variableContract.getUserOrders(TestSuite.FRANCOIS,variableDistrib).length); //François has 2 orders + var basket = db.Basket.get(TestSuite.FRANCOIS,variableDistrib.place,variableDistrib.date); + assertEquals(2, basket.getOrders().length); + + var operation1 = db.Operation.findVOrderTransactionFor(order1.distribution.getKey(), TestSuite.FRANCOIS, variableContract.amap); + var operation1Id = operation1.id; + var operation2 = db.Operation.findVOrderTransactionFor(order2.distribution.getKey(), TestSuite.FRANCOIS, variableContract.amap); + var operation2Id = operation2.id; + assertEquals(operation1Id,operation2Id); + var e2 = null; + try { + //delete strawberries order + service.OrderService.delete(order1); + } + catch(x:tink.core.Error){ + e2 = x; + } + assertEquals(null, e2); + assertEquals(null, db.UserContract.manager.get(order1Id) ); //order 1 is deleted + assertTrue( db.Operation.manager.get(operation1Id) != null); //operation should be here + assertEquals(0.0,operation1.amount); + assertEquals(1 , basket.getOrders().length); + var e3 = null; + try { + //delete apple order + service.OrderService.delete(order2); + } + catch(x:tink.core.Error){ + e3 = x; + } + assertEquals(null, e3); + assertEquals(null, db.UserContract.manager.get(order2Id)); //order 2 is deleted + assertEquals(null, db.Operation.manager.get(operation1Id)); //operation should be deleted + assertEquals(0 , basket.getOrders().length); + + //[Test case] 2 VarOrderContracts and quantity zero with payments enabled and 1 order each + //Check that first order is deleted but operation amount is at 0 + //Check that operation is deleted only at the second order deletion + var variableDistrib1 = TestSuite.DISTRIB_LEGUMES_RUE_SAUCISSE; + var order1 = OrderService.make(TestSuite.FRANCOIS, 2, TestSuite.COURGETTES, variableDistrib1.id); + assertTrue(order1.basket!=null); + // trace("WE GOT A BASKET "+order1.basket.id); + // trace("... THEN RE-GET BASKET user "+TestSuite.FRANCOIS.id+" place "+variableDistrib1.place.id+" date "+variableDistrib1.date); + var basket = db.Basket.get(TestSuite.FRANCOIS,variableDistrib1.place,variableDistrib1.date); + assertTrue(basket!=null); + assertEquals(1, basket.getOrders().length); + db.Operation.onOrderConfirm([order1]); + var order1Id = order1.id; + + var variableDistrib2 = TestSuite.DISTRIB_PATISSERIES; + var order2 = OrderService.make(TestSuite.FRANCOIS, 3, TestSuite.FLAN, variableDistrib2.id); + db.Operation.onOrderConfirm([order2]); + var order2Id = order2.id; + + order1 = OrderService.edit(order1, 0); + db.Operation.onOrderConfirm([order1]); + + order2 = OrderService.edit(order2, 0); + db.Operation.onOrderConfirm([order2]); + + //check basket + var basket = db.Basket.get(TestSuite.FRANCOIS,variableDistrib1.place,variableDistrib1.date); + assertEquals(2, basket.getOrders().length); + + var operation = db.Operation.findVOrderTransactionFor(order1.distribution.getKey(), TestSuite.FRANCOIS, variableDistrib1.contract.amap); + var operationId = operation.id; + + + var e4 = null; + try { + service.OrderService.delete(order1); + } + catch(x:tink.core.Error){ + e4 = x; + } + assertEquals(e4, null); + assertEquals(db.UserContract.manager.get(order1Id), null); + assertTrue(db.Operation.manager.get(operationId) != null); //op should still be here + assertEquals(0.0,operation.amount);//...with amount 0 + + var e5 = null; + try { + service.OrderService.delete(order2); + } + catch(x:tink.core.Error){ + e5 = x; + } + assertEquals(e5, null); + assertEquals(db.UserContract.manager.get(order2Id), null); + assertEquals(null, db.Operation.manager.get(operationId), null); //op should have been deleted + } +} \ No newline at end of file diff --git a/src/test/TestPayments.hx b/src/test/TestPayments.hx new file mode 100644 index 0000000..d5ee953 --- /dev/null +++ b/src/test/TestPayments.hx @@ -0,0 +1,64 @@ +package test; + +/** + * Test payments + * + * @author web-wizard + */ +class TestPayments extends haxe.unit.TestCase +{ + + public function new(){ + super(); + } + + override function setup(){ + TestSuite.initDB(); + TestSuite.initDatas(); + db.Basket.emptyCache(); + } + + function testValidateDistribution() { + + //Take a contract with payments enabled + //Take 2 users and make orders for each + var distrib = TestSuite.DISTRIB_LEGUMES_RUE_SAUCISSE; + var contract = distrib.contract; + var product = TestSuite.COURGETTES; + var francoisOrder = service.OrderService.make(TestSuite.FRANCOIS, 1, product, distrib.id); + var francoisOrderOperation = db.Operation.onOrderConfirm([francoisOrder]); + var sebOrder = service.OrderService.make(TestSuite.SEB, 3, product, distrib.id); + var sebOrderOperation = db.Operation.onOrderConfirm([sebOrder]); + //They both pay by check + var francoisPayment = db.Operation.makePaymentOperation(TestSuite.FRANCOIS,contract.amap, payment.Check.TYPE, product.price, "Payment by check", francoisOrderOperation[0]); + var sebPayment = db.Operation.makePaymentOperation(TestSuite.SEB,contract.amap, payment.Check.TYPE, 3 * product.price, "Payment by check", sebOrderOperation[0] ); + + //Autovalidate this old distrib and check that all the payments are validated + service.PaymentService.validateDistribution(distrib); + + //distrib should be validated + assertTrue(contract.amap.hasPayments()); + assertEquals(true, distrib.validated); + + //orders should be marked as paid + assertEquals(true, francoisOrder.paid); + assertEquals(true, sebOrder.paid); + + //order operation is not pending + var francoisOperation = db.Operation.findVOrderTransactionFor(francoisOrder.distribution.getKey(), TestSuite.FRANCOIS, contract.amap, false); + var sebOperation = db.Operation.findVOrderTransactionFor(sebOrder.distribution.getKey(), TestSuite.SEB, contract.amap, false); + assertEquals(francoisOperation.pending, false); + assertEquals(sebOperation.pending, false); + + //payment operation is not pending + assertEquals(francoisPayment.pending, false); + assertEquals(sebPayment.pending, false); + + //basket are validated + var b = db.Basket.get(TestSuite.SEB,distrib.place,distrib.date); + assertEquals(true, b.isValidated()); + var b = db.Basket.get(TestSuite.FRANCOIS,distrib.place,distrib.date); + assertEquals(true, b.isValidated()); + } + +} \ No newline at end of file diff --git a/src/test/TestReports.hx b/src/test/TestReports.hx new file mode 100644 index 0000000..c1b6fdb --- /dev/null +++ b/src/test/TestReports.hx @@ -0,0 +1,171 @@ +package test; +import Common; +import test.TestSuite; +import service.ReportService; +import service.OrderService; + +/** + * Test order reports + * + * @author fbarbut + */ +class TestReports extends haxe.unit.TestCase +{ + + public function new(){ + + super(); + } + + override function setup(){ + + TestSuite.initDB(); + TestSuite.initDatas(); + + } + + + function testOrdersByProduct(){ + + //record orders + var seb = TestSuite.SEB; + var francois = TestSuite.FRANCOIS; + var julie = TestSuite.JULIE; + + //distrib de légumes + var d = TestSuite.DISTRIB_LEGUMES_RUE_SAUCISSE; + var carrots = TestSuite.CARROTS; + var courgettes = TestSuite.COURGETTES; + var poulet = TestSuite.CHICKEN; + + OrderService.make(seb,4,courgettes,d.id); + OrderService.make(seb,1,poulet,d.id); + + OrderService.make(francois,6,courgettes,d.id); + OrderService.make(francois,2,poulet,d.id); + OrderService.make(francois,3,carrots,d.id); + + OrderService.make(julie,8,carrots,d.id); + OrderService.make(julie,3,poulet,d.id); + + //record orders on ANOTHER distrib + var d2 = service.DistributionService.create( + d.contract, + new Date(2018,2,12,0,0,0), + new Date(2018,2,12,0,3,0), + d.contract.amap.getPlaces().first().id, + null,null,null,null, + new Date(2018,2,8,0,0,0), + new Date(2018,2,11,0,0,0) + ); + OrderService.make(julie,6,carrots,d2.id); + OrderService.make(julie,1,poulet,d2.id); + + var orders = ReportService.getOrdersByProduct(d); + + //courgettes x 10 + var courgettesOrder = Lambda.find(orders, function(o) return o.pid==courgettes.id); + assertEquals( 10.0 , courgettesOrder.quantity ); + assertEquals( 35.0 , courgettesOrder.totalTTC ); + assertEquals( 33.18 , tools.FloatTool.clean(courgettesOrder.totalHT) ); + + //the report stays the same, even if the product has a new price. + courgettes.lock(); + courgettes.price+=4; + courgettes.update(); + var orders = ReportService.getOrdersByProduct(d); + var courgettesOrder = Lambda.find(orders, function(o) return o.pid==courgettes.id); + assertEquals( 10.0 , courgettesOrder.quantity ); + assertEquals( 35.0 , courgettesOrder.totalTTC ); + assertEquals( 33.18 , tools.FloatTool.clean(courgettesOrder.totalHT) ); + + + } + + /** + * run once at the beginning + */ + /*override function setup(){ + + sys.db.Manager.cnx.request("TRUNCATE TABLE UserContract;"); + + var bubar = db.User.manager.get(1); + var seb = db.User.manager.get(2); + + //fruits from group 1 + var fraises = db.Product.manager.get(2); + fraises.stock = 30; + var pommes = db.Product.manager.get(3); + var distrib = fraises.contract.getDistribs().first(); + + db.UserContract.make(bubar, 4, fraises, distrib.id); + db.UserContract.make(seb, 2, pommes, distrib.id); + + //vegetables from group 2 + var courgettes = db.Product.manager.get(4); + var carottes = db.Product.manager.get(5); + var distrib = courgettes.contract.getDistribs().first(); + + db.UserContract.make(bubar, 1, carottes ,distrib.id); + db.UserContract.make(seb, 5, courgettes ,distrib.id); + }*/ + + + /** + * test a simple report with just a time frame + */ + /*public function testTimeFrameReport(){ + + var fraises = db.Product.manager.get(2); + var pommes = db.Product.manager.get(3); + var courgettes = db.Product.manager.get(4); + var carottes = db.Product.manager.get(5); + + //check we got the right products + assertEquals("Fraises",fraises.name); + assertEquals("Pommes",pommes.name); + assertEquals("Courgettes",courgettes.name); + assertEquals("Carottes", carottes.name); + + var options = { startDate:new Date(2017, 5, 1, 0, 0, 0), endDate:new Date(2017, 5, 31, 0, 0, 0), groups:[], contracts:[] }; + + var rep = new pro.OrderReport(options); + + var data = rep.byProduct(); + + assertEquals(4,data.length); //should be the 4 products + + for ( d in data){ + switch(d.pname){ + case "Fraises": assertEquals(d.qt, 4); + case "Pommes": assertEquals(d.qt, 2); + case "Carottes": assertEquals(d.qt, 1); + case "Courgettes": assertEquals(d.qt, 5); + } + } + + }*/ + + + /** + * test a report with time frame + group + */ + /*public function testGroupReport(){ + + var options = { startDate:new Date(2017, 5, 1, 0, 0, 0), endDate:new Date(2017, 5, 31, 0, 0, 0), groups:[1], contracts:[] }; + + var rep = new pro.OrderReport(options); + var data = rep.byProduct(); + assertEquals(2,data.length); //should be the 2 products + + for ( d in data){ + switch(d.pname){ + case "Fraises": assertEquals(d.qt, 4); + case "Pommes": assertEquals(d.qt, 2); + } + } + }*/ + + + +} \ No newline at end of file diff --git a/src/test/TestSuite.hx b/src/test/TestSuite.hx new file mode 100644 index 0000000..6acf049 --- /dev/null +++ b/src/test/TestSuite.hx @@ -0,0 +1,413 @@ +package test; +import Common; +/** + * CAGETTE.NET TEST SUITE + * @author fbarbut + */ +class TestSuite +{ + + static function main() { + + connectDb(); + var r = new haxe.unit.TestRunner(); + + //Cagette core tests + r.add(new test.TestUser()); + r.add(new test.TestOrders()); + r.add(new test.TestTools()); + r.add(new test.TestDistributions()); + r.add(new test.TestPayments()); + r.add(new test.TestReports()); + + #if plugins + //Cagette-pro tests, keep in this order + r.add(new pro.test.TestProductService()); + r.add(new pro.test.TestRemoteCatalog()); + r.add(new pro.test.TestDistribService()); + r.add(new pro.test.TestReports()); + //wholesale-order tests + r.add(new who.test.TestWho()); + #end + + r.run(); + } + + static function connectDb() { + var dbstr = Sys.args()[0]; + var dbreg = ~/([^:]+):\/\/([^:]+):([^@]*?)@([^:]+)(:[0-9]+)?\/(.*?)$/; + if( !dbreg.match(dbstr) ) + throw "Configuration requires a valid database attribute, format is : mysql://user:password@host:port/dbname"; + var port = dbreg.matched(5); + var dbparams = { + user:dbreg.matched(2), + pass:dbreg.matched(3), + host:dbreg.matched(4), + port:port == null ? 3306 : Std.parseInt(port.substr(1)), + database:dbreg.matched(6), + socket:null + }; + + sys.db.Manager.cnx = sys.db.Mysql.connect(dbparams); + sys.db.Manager.initialize(); + } + + + public static function initDB(){ + //NUKE EVERYTHING BWAAAAH !! + sys.db.Manager.cleanup(); //cleanup cache objects + sql("DROP DATABASE tests;"); + sql("CREATE DATABASE tests;"); + sql("USE tests;"); + + var tables : Array = [ + + //cagette + db.TxpProduct.manager, + db.TxpCategory.manager, + db.TxpSubCategory.manager, + db.Category.manager, + db.CategoryGroup.manager, + db.ProductCategory.manager, + + db.Basket.manager, + db.UserContract.manager, + db.UserAmap.manager, + db.Operation.manager, + + db.User.manager, + db.Amap.manager, + db.Contract.manager, + db.Product.manager, + db.Vendor.manager, + db.Place.manager, + db.Distribution.manager, + db.DistributionCycle.manager, + + //sugoi tables + sugoi.db.Cache.manager, + sugoi.db.Error.manager, + sugoi.db.File.manager, + sugoi.db.Session.manager, + sugoi.db.Variable.manager, + ]; + + for(t in tables) createTable(t); + + #if plugins + //add Cpro datas : we need those tables even in cagette core tests + pro.test.ProTestSuite.initDB(); + pro.test.ProTestSuite.initDatas(); + #end + } + + public static function createTable( m ){ + if ( sys.db.TableCreate.exists(m) ){ + drop(m); + } + // Sys.println("Creating table "+ m.dbInfos().name); + sys.db.TableCreate.create(m); + } + + public static function truncate(m){ + sql("TRUNCATE TABLE "+m.dbInfos().name+";"); + } + + public static function drop(m){ + sql("DROP TABLE "+m.dbInfos().name+";"); + } + + public static function sql(sql){ + return sys.db.Manager.cnx.request(sql); + } + + //shortcut to datas + public static var FRANCOIS:db.User = null; + public static var SEB:db.User = null; + public static var JULIE:db.User = null; + + public static var CHICKEN:db.Product = null; + public static var STRAWBERRIES:db.Product = null; + public static var APPLES:db.Product = null; + public static var AMAP_DU_JARDIN:db.Amap = null; + public static var LOCAVORES:db.Amap = null; + public static var PANIER_AMAP_LEGUMES:db.Product = null; + public static var DISTRIB_CONTRAT_AMAP:db.Distribution = null; + public static var DISTRIB_FRUITS_PLACE_DU_VILLAGE:db.Distribution = null; + public static var DISTRIB_LEGUMES_RUE_SAUCISSE:db.Distribution = null; + public static var CONTRAT_LEGUMES:db.Contract = null; + public static var PLACE_DU_VILLAGE:db.Place = null; + public static var COURGETTES:db.Product = null; + public static var CARROTS:db.Product = null; + public static var FLAN:db.Product = null; + public static var CROISSANT:db.Product = null; + public static var DISTRIB_PATISSERIES:db.Distribution = null; + + public static function initDatas(){ + + //USERS + + var f = new db.User(); + f.firstName = "François"; + f.lastName = "B"; + f.email = "francois@alilo.fr"; + f.insert(); + + FRANCOIS = f; + + var u = new db.User(); + u.firstName = "Seb"; + u.lastName = "Z"; + u.email = "sebastien@alilo.fr"; + u.insert(); + + SEB = u; + + var u = new db.User(); + u.firstName = "Julie"; + u.lastName = "B"; + u.email = "julie@alilo.fr"; + u.insert(); + + JULIE = u; + + initApp(u); + + //GROUP "AMAP du Jardin public" + var a = new db.Amap(); + a.name = "AMAP du Jardin public"; + a.contact = f; + a.flags.set(db.Amap.AmapFlags.HasPayments); + a.insert(); + AMAP_DU_JARDIN = a; + + var place = new db.Place(); + place.name = "Place du village"; + place.zipCode = "00000"; + place.city = "St Martin"; + place.amap = a; + place.insert(); + + PLACE_DU_VILLAGE = place; + + //VENDOR "Ferme de la galinette" + var v = new db.Vendor(); + v.name = "La ferme de la Galinette"; + v.email = "galinette@gmail.com"; + v.zipCode = "00000"; + v.city = "Bourligheim"; + v.insert(); + + var c = new db.Contract(); + c.name = "Contrat AMAP Légumes"; + c.startDate = new Date(2017, 1, 1, 0, 0, 0); + c.endDate = new Date(2030, 12, 31, 23, 59, 0); + c.vendor = v; + c.amap = a; + c.type = db.Contract.TYPE_CONSTORDERS; + c.insert(); + + var p = new db.Product(); + p.name = "Panier Légumes"; + p.price = 13; + p.contract = c; + p.insert(); + + PANIER_AMAP_LEGUMES = p; + + var d = new db.Distribution(); + d.date = new Date(2017, 5, 1, 19, 0, 0); + d.end = new Date(2017, 5, 1, 20, 0, 0); + d.orderStartDate = new Date(2017, 4, 1, 20, 0, 0); + d.orderEndDate = new Date(2017, 4, 30, 20, 0, 0); + d.contract = c; + d.place = place; + d.insert(); + + DISTRIB_CONTRAT_AMAP = d; + + //varying contract for strawberries with stock mgmt + var c = new db.Contract(); + c.name = "Commande fruits"; + c.vendor = v; + c.startDate = new Date(2017, 1, 1, 0, 0, 0); + c.endDate = new Date(2030, 12, 31, 23, 59, 0); + c.flags.set(db.Contract.ContractFlags.StockManagement); + c.type = db.Contract.TYPE_VARORDER; + c.amap = a; + c.insert(); + + var p = new db.Product(); + p.name = "Fraises"; + p.qt = 1; + p.unitType = Common.Unit.Kilogram; + p.price = 10; + p.organic = true; + p.contract = c; + p.stock = 8; + p.insert(); + + STRAWBERRIES = p; + + var p = new db.Product(); + p.name = "Pommes"; + p.qt = 1; + p.unitType = Common.Unit.Kilogram; + p.price = 6; + p.organic = true; + p.contract = c; + p.stock = 12; + p.insert(); + + APPLES = p; + + var d = new db.Distribution(); + d.date = new Date(2017, 5, 1, 19, 0, 0); + d.end = new Date(2017, 5, 1, 20, 0, 0); + d.orderStartDate = new Date(2017, 4, 1, 20, 0, 0); + d.orderEndDate = new Date(2017, 4, 30, 20, 0, 0); + d.contract = c; + d.place = place; + d.insert(); + + DISTRIB_FRUITS_PLACE_DU_VILLAGE = d; + + //second group + var a = new db.Amap(); + a.name = "Les Locavores de la Rue Saucisse"; + a.contact = f; + a.flags.set(db.Amap.AmapFlags.HasPayments); + a.insert(); + LOCAVORES = a; + + var place = new db.Place(); + place.name = "Rue Saucisse"; + place.zipCode = "00000"; + place.city = "St Martin"; + place.amap = a; + place.insert(); + + var v = new db.Vendor(); + v.name = "La ferme de la courgette enragée"; + v.email = "courgette@gmail.com"; + v.zipCode = "00000"; + v.city = "Bourligeac"; + v.insert(); + + var c = new db.Contract(); + c.name = "Commande Legumes"; + c.startDate = new Date(2017, 1, 1, 0, 0, 0); + c.endDate = new Date(2030, 12, 31, 23, 59, 0); + c.vendor = v; + c.amap = a; + c.type = db.Contract.TYPE_VARORDER; + c.insert(); + + CONTRAT_LEGUMES = c; + + var p = new db.Product(); + p.name = "Courgettes"; + p.qt = 1; + p.unitType = Common.Unit.Kilogram; + p.price = 3.5; + p.vat = 5.5; + p.organic = true; + p.contract = c; + p.insert(); + + COURGETTES = p; + + var p = new db.Product(); + p.name = "Carottes"; + p.qt = 1; + p.unitType = Common.Unit.Kilogram; + p.price = 2.8; + p.vat = 5.5; + p.contract = c; + p.insert(); + + CARROTS = p; + + var p = new db.Product(); + p.name = "Poulet"; + p.qt = 1.5; + p.unitType = Common.Unit.Kilogram; + p.price = 15; + p.vat = 5.5; + p.multiWeight = true; + p.hasFloatQt = true; + p.contract = c; + p.insert(); + + CHICKEN = p; + + var d = service.DistributionService.create(c,new Date(2017, 5, 1, 19, 0, 0),new Date(2017, 5, 1, 19, 2, 0),place.id,null,null,null,null,new Date(2017, 4, 10, 19, 0, 0),new Date(2017, 4, 20, 19, 0, 0)); + DISTRIB_LEGUMES_RUE_SAUCISSE = d; + + //PASTRY + + var c = new db.Contract(); + c.name = "Commande Pâtisseries"; + c.startDate = new Date(2017, 1, 1, 0, 0, 0); + c.endDate = new Date(2017, 12, 31, 23, 59, 0); + c.vendor = v; + c.amap = a; + c.type = db.Contract.TYPE_VARORDER; + c.insert(); + + var p = new db.Product(); + p.name = "Flan"; + p.qt = 1; + p.unitType = Common.Unit.Kilogram; + p.price = 3.5; + p.organic = true; + p.contract = c; + p.insert(); + + FLAN = p; + + var p = new db.Product(); + p.name = "Croissant"; + p.qt = 1; + p.unitType = Common.Unit.Kilogram; + p.price = 2.8; + p.contract = c; + p.insert(); + + CROISSANT = p; + + var d = new db.Distribution(); + d.date = new Date(2017, 5, 1, 19, 0, 0); + d.contract = c; + d.place = place; + d.insert(); + + DISTRIB_PATISSERIES = d; + } + + static function initApp(u:db.User){ + + //setup App + var app = App.current = new App(); + App.config.DEBUG = true; + app.initLang("en"); + + app.eventDispatcher = new hxevents.Dispatcher(); + app.plugins = []; + //internal plugins + app.plugins.push(new plugin.Tutorial()); + + //optionnal plugins + #if plugins + //app.plugins.push( new hosted.HostedPlugIn() ); + app.plugins.push( new pro.ProPlugIn() ); + app.plugins.push( new connector.ConnectorPlugIn() ); + //app.plugins.push( new pro.LemonwayEC() ); + app.plugins.push( new who.WhoPlugIn() ); + #end + + App.current.user = u; + App.current.view = new View(); + } +} + diff --git a/src/test/TestTools.hx b/src/test/TestTools.hx new file mode 100644 index 0000000..4e27d33 --- /dev/null +++ b/src/test/TestTools.hx @@ -0,0 +1,92 @@ +package test; +import Common; + +/** + * Test various tools + * + * @author fbarbut + */ +class TestTools extends haxe.unit.TestCase +{ + + public function new(){ + super(); + } + + + function testDateRanges(){ + // test last hour range + var now = Date.fromString("2018-01-01 00:30:12"); + var r = tools.DateTool.getLastHourRange(now); + assertEquals("2017-12-31 23:00:00",r.from.toString()); + assertEquals("2018-01-01 00:00:00",r.to.toString()); + + // test last minute + var now = Date.fromString("2018-01-01 00:30:12"); + var r = tools.DateTool.getLastMinuteRange(now); + assertEquals("2018-01-01 00:29:00",r.from.toString()); + assertEquals("2018-01-01 00:30:00",r.to.toString()); + } + + + function testFloatTool(){ + + assertEquals( true , tools.FloatTool.isEqual(10.0,10.000) ); + assertEquals( true , tools.FloatTool.isEqual(10.0,10) ); + assertEquals( true , tools.FloatTool.isEqual(10,10) ); + assertEquals( true , tools.FloatTool.isEqual(10.00000001,10) ); + assertEquals( false , tools.FloatTool.isEqual(10.02,10) ); + + assertEquals( false , tools.FloatTool.isInt(10.08) ); + assertEquals( true , tools.FloatTool.isInt(10.00) ); + + + } + +/* + @admin + public function doTests() { + + var assertTrue = function(val, ?desc="") { + if (val) { + Sys.println("OK :
    "); + }else { + Sys.println("ERROR : "+desc+"
    "); + } + } + + + //test les fonctions de cotisation + + var amap = db.Amap.manager.get(1); + + amap.membershipRenewalDate = new Date(2015, 0, 1,0,0,0); + amap.update(); + + assertTrue(amap.getMembershipYear(new Date(2015, 3, 3, 0, 0, 0) ) == 2015); + assertTrue(amap.getPeriodName(new Date(2015, 3, 3, 0, 0, 0)) == "2015"); + + assertTrue(amap.getMembershipYear(new Date(2014, 8, 8, 0, 0, 0) ) == 2014); + assertTrue(amap.getPeriodName(new Date(2014, 8, 8, 0, 0, 0) ) == "2014"); + + assertTrue(amap.getMembershipYear(new Date(2013, 11, 12, 0, 0, 0) ) == 2013); + assertTrue(amap.getPeriodName(new Date(2013, 11, 12, 0, 0, 0) ) == "2013"); + + amap.membershipRenewalDate = new Date(2015, 8, 1,0,0,0); + amap.update(); + + assertTrue(amap.getMembershipYear(new Date(2015, 3, 3, 0, 0, 0) ) == 2014); + assertTrue(amap.getPeriodName(new Date(2015, 3, 3, 0, 0, 0)) == "2014-2015"); + + var d = amap.getMembershipYear(new Date(2015, 8, 8, 0, 0, 0) ); + assertTrue( d == 2015, "le 8 sept 2015, on doit etre en cotis 2015, la c " + d); + assertTrue(amap.getPeriodName(new Date(2015, 8, 8, 0, 0, 0)) == "2015-2016"); + + var d = new Date(2013, 11, 12, 0, 0, 0) ; + assertTrue( amap.getMembershipYear(d) == 2013, "le 12 oct 2013, on doit etre en cotis 2013 , là c " + amap.getMembershipYear(d) ); + assertTrue(amap.getPeriodName(d) == "2013-2014", "le 12 oct 2013, on doit etre en 2013-2014 , là c " + amap.getPeriodName(d) ); + + + }*/ + +} \ No newline at end of file diff --git a/src/test/TestUser.hx b/src/test/TestUser.hx new file mode 100644 index 0000000..b4ffb3c --- /dev/null +++ b/src/test/TestUser.hx @@ -0,0 +1,48 @@ + + +package test; +import db.UserAmap; +import Common; + +/** + * Test user rights to view contracts + * + * @author web-wizard + */ +class TestUser extends haxe.unit.TestCase +{ + + public function new(){ + super(); + } + + var contract : db.Contract; + var user : db.User; + var group1 : db.Amap; + var group2 : db.Amap; + var userAmap : db.UserAmap; + + /** + * get a contract + a user + */ + override function setup(){ + TestSuite.initDB(); + TestSuite.initDatas(); + + contract = db.Contract.manager.get(3); + user = db.User.manager.get(1); + group1 = db.Amap.manager.get(1); + group2 = db.Amap.manager.get(2); + } + + /** + * Check that a user who has admin rights for his/her group can't view a contract from a group + he/she doesn't belong to + */ + function testViewContract(){ + userAmap = db.UserAmap.getOrCreate(user, group1); + userAmap.giveRight(Right.GroupAdmin); + assertFalse(user.canManageContract(contract)); + } + +} \ No newline at end of file diff --git a/src/tools/ArrayTool.hx b/src/tools/ArrayTool.hx new file mode 100644 index 0000000..aa9fd0b --- /dev/null +++ b/src/tools/ArrayTool.hx @@ -0,0 +1,51 @@ +package tools; + +/** + * Some utility functions for arrays + */ +class ArrayTool +{ + /** + * shuffle (randomize) an array + */ + //public static function shuffle(arr:Array):Array + //{ + //if (arr!=null) { + //for (i in 0...arr.length) { + //var j = Std.random(arr.length); + //var a = arr[i]; + //var b = arr[j]; + //arr[i] = b; + //arr[j] = a; + //} + //} + //return arr; + //} + + /** + * Group a list of objects by date + * @param objs List of objects + * @param dateParamName Name of the object field which is a date + * @return + */ + public static function groupByDate(objs:Array,dateFieldName:String):Map>{ + + var out = new Map >(); + for ( o in objs){ + var d : Date = Reflect.field(o, dateFieldName); + var group = out.get(d.toString()); + if (group == null) group = new Array(); + group.push(o); + out.set(d.toString(), group); + } + return out; + } + + + public static function mapLength(m:Map):Int{ + var i = 0; + for (x in m) i++; + return i; + } + +} \ No newline at end of file diff --git a/src/tools/DateTool.hx b/src/tools/DateTool.hx new file mode 100644 index 0000000..90b6461 --- /dev/null +++ b/src/tools/DateTool.hx @@ -0,0 +1,52 @@ +package tools; + +/** + * Date tool + * @author fbarbut + */ +class DateTool +{ + + public static function now():Date{ + return Date.now(); + } + + public static function deltaDays(d:Date,n:Int):Date{ + return DateTools.delta(d, n * 1000 * 60 * 60 * 24.0); + } + + public static function setHourMinute(d:Date, hour:Int, minute:Int):Date{ + return new Date(d.getFullYear(), d.getMonth(), d.getDate(), hour, minute, 0); + } + + public static function setDateMonth(d:Date, date:Int, month:Int):Date{ + return new Date(d.getFullYear(), month, date, d.getHours(), d.getMinutes(), 0); + } + + + public static function getLastHourRange(?now:Date){ + if(now==null) now = Date.now(); + var HOUR = 1000.0 * 60 * 60; + var to = setHourMinute(now,now.getHours(),0); + var from = DateTools.delta(to, -HOUR ); + return {from:from,to:to}; + } + + public static function getLastMinuteRange(?now:Date){ + if(now==null) now = Date.now(); + var MIN = 1000.0 * 60; + var to = setHourMinute(now,now.getHours(),now.getMinutes()); + var from = DateTools.delta(to, -MIN ); + return {from:from,to:to}; + } + + public static function getWhichNthDayOfMonth(date:Date):Int { + return Math.floor((date.getDate() - 1) / 7) + 1; + } + + public static function getNthDayOfMonth(year:Int,month:Int,dayOfWeek:Int,n:Int):Date { + //dayOfWeek and getDay() follow the same value system: from 0 (Sunday) to 6 (Saturday) + return deltaDays(new Date(year,month,1+7*n, 0, 0, 0), -new Date(year,month,8-(dayOfWeek+1), 0, 0, 0).getDay()-1); + } + +} \ No newline at end of file diff --git a/src/tools/FloatTool.hx b/src/tools/FloatTool.hx new file mode 100644 index 0000000..61aa2c2 --- /dev/null +++ b/src/tools/FloatTool.hx @@ -0,0 +1,24 @@ +package tools; + + +class FloatTool{ + + /** + Tries to fix buddy float comparison in Neko... + **/ + public static function isEqual(a:Float,b:Float){ + a = Math.round(a*10000); + b = Math.round(b*10000); + return a==b; + } + + public static function isInt(f:Float){ + return isEqual(f , Math.round(f) ); + } + + //prevent float comparison bug in Neko + public static function clean(f:Float){ + return Math.round(f*100)/100; + } + +} \ No newline at end of file diff --git a/src/tools/ObjectListTool.hx b/src/tools/ObjectListTool.hx new file mode 100644 index 0000000..eadc1ca --- /dev/null +++ b/src/tools/ObjectListTool.hx @@ -0,0 +1,142 @@ +package tools; +import Common.UserOrder; + +/** + * Utility to work on sys.db.Object lists + * @author fbarbut + */ +class ObjectListTool +{ + + /** + * Get a list of IDs from an object list + */ + public static function getIds( objs:Iterable ):Array{ + var out = new Array(); + for ( o in objs ) out.push(untyped o.id); + return out; + } + + /** + * deduplicate objects on IDs + */ + public static function deduplicate(objs:Iterable):Array{ + var out = new Map(); + for ( u in objs) out.set( untyped u.id, u ); + return Lambda.array(out); + } + + + public static function toIdMap(objs:Iterable):Map{ + var out = new Map(); + for ( u in objs) out.set( untyped u.id, u ); + return out; + } + + /** + * Deduplicate user orders. (merge orders on same product from a same user) + * @param orders + * @return + */ + public static function deduplicateOrders(orders:Array):Array{ + + var out = new Map(); + + for ( o in orders){ + + var key = o.userId + "-" + o.userId2 + "-" + o.productId; + var x = out.get(key); + if ( x == null){ + x = o; + }else{ + //null safety + if (x.fees == null) x.fees = 0; + if (o.fees == null) o.fees = 0; + + //merge + x.quantity += o.quantity; + x.fees += o.fees; + x.subTotal += o.subTotal; + x.total += o.total; + } + + out.set(key, x); + } + + return Lambda.array(out); + } + + /** + * Deduplicate distributions by key (date+placeId) + * @param distribs + */ + public static function deduplicateDistribsByKey(distribs:Iterable){ + + var out = new Map(); + for ( d in distribs) out.set(d.getKey(), d); + return Lambda.array(out); + } + + /** + * Group distributions by key (date+placeId) + * @param distribs + */ + public static function groupDistribsByKey(distribs:Iterable){ + + var out = new Map>(); + for ( d in distribs) { + + var v = out.get(d.getKey()); + if (v == null) v = []; + v.push(d); + out.set(d.getKey(), v); + } + return out; + } + + /** + * Groupe orders by multidistrib key (date+placeId) + */ + public static function groupOrdersByKey(ucs:Iterable){ + + var out = new Map>(); + for ( uc in ucs) { + var k = uc.distribution.getKey(); + var v = out.get(k); + if (v == null) v = []; + v.push(uc); + out.set(k, v); + } + return out; + } + + /** + * Group distributions by group and day, order by day + */ + public static function groupDistributionsByGroupAndDay(dists:Iterable){ + + var out = new Map>(); + for ( d in dists){ + + var k = d.date.toString().substr(0, 10) + "-" + d.contract.amap.id; + + var x = out[k]; + if (x == null) x = []; + x.push(d); + out[k] = x; + + } + + //sort keys + var keys = []; + for ( k in out.keys()) keys.push(k); + keys.sort(function(a, b){ if (a > b) return 1 else return -1; }); + + var out2 = []; + for ( k in keys) out2.push(out[k]); + + return out2; + + } + +} \ No newline at end of file diff --git a/src/tools/StockTool.hx b/src/tools/StockTool.hx new file mode 100644 index 0000000..5f170da --- /dev/null +++ b/src/tools/StockTool.hx @@ -0,0 +1,36 @@ +package tools; + +/** + * ... + * @author fbarbut + */ +class StockTool +{ + + /** + * Stock dispatching between groups. + * + * i.e we got 10kg of potatoes to dispatch with 3 groups. + * I dont want to have 3,3333 kg for each group , but something like [3,3,4] + */ + public static function dispatch(stock:Int, groups:Int){ + + var out = []; + + var modulo = stock % groups; + + stock -= modulo; + + var s = Math.round(stock / groups); + + for ( i in 0...groups) out.push(s); + + for (i in 0...modulo){ + out[i % out.length]++; + } + + return out; + + } + +} \ No newline at end of file diff --git a/tests.hxml b/tests.hxml new file mode 100644 index 0000000..1194f2e --- /dev/null +++ b/tests.hxml @@ -0,0 +1,17 @@ +-lib dbadmin +-lib templo +-lib sugoi +-lib hxevents +-lib thx.semver +-lib thx.csv +-lib datetime +-lib hscript +-lib smtpmailer +-lib record-macros +-lib tink_json +-cp common +-cp src +-cp lang/master/tpl +-neko www/tests.n + -main test.TestSuite + diff --git a/testsJS.hxml b/testsJS.hxml new file mode 100644 index 0000000..012f003 --- /dev/null +++ b/testsJS.hxml @@ -0,0 +1,9 @@ +# JS +-js js/test/tests.js +-main test.TestAll +-cp js +-cp common +-lib utest +-lib hxnodejs +-lib sugoi:git:https://github.com/bablukid/sugoi#i18n +-D test diff --git a/testsPlugins.hxml b/testsPlugins.hxml new file mode 100644 index 0000000..7a203a9 --- /dev/null +++ b/testsPlugins.hxml @@ -0,0 +1,21 @@ +-lib dbadmin +-lib templo +-lib sugoi +-lib hxevents +-lib thx.semver +-lib thx.csv +-lib datetime +-lib hscript +-lib smtpmailer +-lib record-macros +-lib cagette-pro +-lib cagette-hosted +-lib cagette-wholesale-order +-lib tink_json +-cp common +-cp src +-cp lang/master/tpl +-neko www/tests.n +-D plugins +-main test.TestSuite + diff --git a/www/.htaccess b/www/.htaccess new file mode 100755 index 0000000..dbcdcdd --- /dev/null +++ b/www/.htaccess @@ -0,0 +1,8 @@ + + RewriteEngine On + RewriteRule (.*)/css/[a-z0-9]+/(.*) /css/$2 [L] + RewriteRule (.*)/js/[a-z0-9]+/(.*) /js/$2 [L] + + RewriteCond %{REQUEST_URI} !\.(png|gif|jpg|ico|swf|css|js|dat)$ + RewriteRule (.*) /index.n + diff --git a/www/adherents.xls b/www/adherents.xls new file mode 100755 index 0000000..ecda370 Binary files /dev/null and b/www/adherents.xls differ diff --git a/www/bower.json b/www/bower.json new file mode 100755 index 0000000..01acb5f --- /dev/null +++ b/www/bower.json @@ -0,0 +1,21 @@ +{ + "name": "cagette", + "version": "1.0.0", + "homepage": "http://www.cagette.net", + "authors": [ + "Bubar " + ], + "description": "Cagette.net le logiciel des AMAP et groupements d'achat", + "license": "GNU GPL", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "eonasdan-bootstrap-datetimepicker": "~4.7.14", + "bootstrap": "~3.3.4" + } +} diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/.bower.json b/www/bower_components/eonasdan-bootstrap-datetimepicker/.bower.json new file mode 100755 index 0000000..98b9260 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/.bower.json @@ -0,0 +1,47 @@ +{ + "name": "eonasdan-bootstrap-datetimepicker", + "version": "4.7.14", + "main": [ + "build/css/bootstrap-datetimepicker.min.css", + "build/js/bootstrap-datetimepicker.min.js", + "src/less/_bootstrap-datetimepicker.less", + "src/less/bootstrap-datetimepicker-build.less", + "src/js/bootstrap-datetimepicker.js" + ], + "dependencies": { + "jquery": ">=1.8.3", + "moment": ">=2.8.0" + }, + "homepage": "https://github.com/Eonasdan/bootstrap-datetimepicker", + "authors": [ + "Eonasdan" + ], + "description": "bootstrap3 datetimepicker", + "keywords": [ + "twitter-bootstrap", + "bootstrap", + "datepicker", + "datetimepicker", + "timepicker", + "moment" + ], + "license": "MIT", + "private": false, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "_release": "4.7.14", + "_resolution": { + "type": "version", + "tag": "4.7.14", + "commit": "d004434a5ff76e7b97c8b07c01f34ca69e635d97" + }, + "_source": "git://github.com/Eonasdan/bootstrap-datetimepicker.git", + "_target": "~4.7.14", + "_originalSource": "eonasdan-bootstrap-datetimepicker", + "_direct": true +} \ No newline at end of file diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/CONTRIBUTING.md b/www/bower_components/eonasdan-bootstrap-datetimepicker/CONTRIBUTING.md new file mode 100755 index 0000000..a27fd66 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/CONTRIBUTING.md @@ -0,0 +1,32 @@ +Submitting Issues +================= + +If you are submitting a bug, please test and/or fork [this jsfiddle](http://jsfiddle.net/Eonasdan/0Ltv25o8/) demonstrating the issue. Code issues and fringe case bugs that do not include a jsfiddle (or similar) will be closed. + +Contributing code +================= + +To contribute, fork the library and install grunt and dependencies. You need [node](http://nodejs.org/); use [nvm](https://github.com/creationix/nvm) or [nenv](https://github.com/ryuone/nenv) to install it. + +```bash +git clone https://github.com/Eonasdan/bootstrap-datetimepicker.git +cd bootstrap-datetimepicker +npm install -g grunt-cli +npm install +git checkout development # all patches against development branch, please! +grunt # this runs tests and jshint +``` + +Very important notes +==================== + + * **Pull requests to the `master` branch will be closed.** Please submit all pull requests to the `development` branch. + * **Do not include the minified files in your pull request.** Don't worry, we'll build them when we cut a release. + +Grunt tasks +=========== + +We use Grunt for managing the build. Here are some useful Grunt tasks: + + * `grunt` The default task lints the code and runs the tests. You should make sure you do this before submitting a PR. + * `grunt build` Compiles the less stylesheet and minifies the javascript source in build directory. diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/Gruntfile.js b/www/bower_components/eonasdan-bootstrap-datetimepicker/Gruntfile.js new file mode 100755 index 0000000..7b0ceac --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/Gruntfile.js @@ -0,0 +1,194 @@ +module.exports = function (grunt) { + 'use strict'; + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + + uglify : { + target: { + files: { + 'build/js/bootstrap-datetimepicker.min.js' : 'src/js/bootstrap-datetimepicker.js' + } + }, + options: { + mangle: true, + compress: { + dead_code: false // jshint ignore:line + }, + output: { + ascii_only: true // jshint ignore:line + }, + report: 'min', + preserveComments: 'some' + } + }, + + jshint: { + all: [ + 'Gruntfile.js', 'src/js/*.js', 'test/*.js' + ], + options: { + 'browser' : true, + 'node' : true, + 'jquery' : true, + 'boss' : false, + 'curly' : true, + 'debug' : false, + 'devel' : false, + 'eqeqeq' : true, + 'bitwise' : true, + 'eqnull' : true, + 'evil' : false, + 'forin' : true, + 'immed' : false, + 'laxbreak' : false, + 'newcap' : true, + 'noarg' : true, + 'noempty' : false, + 'nonew' : false, + 'onevar' : true, + 'plusplus' : false, + 'regexp' : false, + 'undef' : true, + 'sub' : true, + 'strict' : true, + 'unused' : true, + 'white' : true, + 'es3' : true, + 'camelcase' : true, + 'quotmark' : 'single', + 'globals': { + 'define': false, + 'moment': false, + // Jasmine + 'jasmine': false, + 'describe': false, + 'xdescribe': false, + 'expect': false, + 'it': false, + 'xit': false, + 'spyOn': false, + 'beforeEach': false, + 'afterEach': false + } + } + }, + + jscs: { + all: [ + 'Gruntfile.js', 'src/js/*.js', 'test/*.js' + ], + options: { + config: '.jscs.json' + } + }, + + less: { + production: { + options: { + cleancss: true + }, + files: { + 'build/css/bootstrap-datetimepicker.min.css': 'src/less/bootstrap-datetimepicker-build.less' + } + }, + development: { + files: { + 'build/css/bootstrap-datetimepicker.css': 'src/less/bootstrap-datetimepicker-build.less' + } + } + }, + + jasmine: { + customTemplate: { + src: 'src/js/*.js', + options: { + specs: 'test/*Spec.js', + helpers: 'test/*Helper.js', + styles: [ + 'node_modules/bootstrap/dist/css/bootstrap.min.css', + 'build/css/bootstrap-datetimepicker.min.css' + ], + vendor: [ + 'node_modules/jquery/dist/jquery.min.js', + 'node_modules/moment/min/moment-with-locales.min.js', + 'node_modules/bootstrap/dist/js/bootstrap.min.js' + ], + display: 'none', + summary: 'true' + } + } + } + + }); + + grunt.loadTasks('tasks'); + + grunt.loadNpmTasks('grunt-contrib-jasmine'); + + // These plugins provide necessary tasks. + require('load-grunt-tasks')(grunt); + + // Default task. + grunt.registerTask('default', ['jshint', 'jscs', 'less', 'jasmine']); + + // travis build task + grunt.registerTask('build:travis', [ + // code style + 'jshint', 'jscs', + // build + 'uglify', 'less', + // tests + 'jasmine' + ]); + + // Task to be run when building + grunt.registerTask('build', [ + 'jshint', 'jscs', 'uglify', 'less' + ]); + + grunt.registerTask('nuget', 'Create a nuget package', function () { + var target = grunt.option('target') || 'less', done = this.async(); + if (target === 'less') { + grunt.util.spawn({ + cmd: 'src/nuget/nuget.exe', + args: [ + 'pack', + 'src/nuget/Bootstrap.v3.Datetimepicker.nuspec', + '-OutputDirectory', + 'build/nuget', + '-Version', + grunt.config.get('pkg').version + ] + }, function (error, result) { + if (error) { + grunt.log.error(error); + } else { + grunt.log.write(result); + } + done(); + }); + } + else { //--target=css + grunt.util.spawn({ + cmd: 'src/nuget/nuget.exe', + args: [ + 'pack', + 'src/nuget/Bootstrap.v3.Datetimepicker.CSS.nuspec', + '-OutputDirectory', + 'build/nuget', + '-Version', + grunt.config.get('pkg').version + ] + }, function (error, result) { + if (error) { + grunt.log.error(error); + } else { + grunt.log.write(result); + } + done(); + }); + } + }); + + grunt.registerTask('test', ['jshint', 'jscs', 'uglify', 'less', 'jasmine']); +}; diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/LICENSE b/www/bower_components/eonasdan-bootstrap-datetimepicker/LICENSE new file mode 100755 index 0000000..58f47d1 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jonathan Peterson (@Eonasdan) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/README.md b/www/bower_components/eonasdan-bootstrap-datetimepicker/README.md new file mode 100755 index 0000000..6ddd566 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/README.md @@ -0,0 +1,25 @@ +# Bootstrap 3 Date/Time Picker +![GitHub version](https://badge.fury.io/gh/Eonasdan%2Fbootstrap-datetimepicker.png)   ![Travis](https://travis-ci.org/Eonasdan/bootstrap-datetimepicker.svg?branch=development) + +![DateTimePicker](http://i.imgur.com/nfnvh5g.png) + +## [View the manual and demos](http://eonasdan.github.io/bootstrap-datetimepicker/) + +#v4 +v4 is out now! For v4 related bugs and issues see: /Eonasdan/bootstrap-datetimepicker/labels/v4. + +v3 is going into an archive state. Please be sure to check the documents. v4 has breaking changes and is a major rewrite. + +## Submitting Issues +If you have issues, please check the following first: +* Have you read the docs? +* Do you have the latest version of momentjs? +* Do you have the latest version of jQuery? +* Please test and/or fork [this jsfiddle](http://jsfiddle.net/Eonasdan/0Ltv25o8/) with an example of your issue before you post an issue here. + +## Where do you use this? +I'd love to know if your public site is using this plugin and list your logo on the documentation site. Please email me `eonasdan at outlook dot com`. Do not submit issue/feature request to this email, they will be ignored. + +## [Installation instructions](https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Installation) + +## [Change Log](https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Version-4-changelog) \ No newline at end of file diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/bower.json b/www/bower_components/eonasdan-bootstrap-datetimepicker/bower.json new file mode 100755 index 0000000..248ec32 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/bower.json @@ -0,0 +1,37 @@ +{ + "name": "eonasdan-bootstrap-datetimepicker", + "version": "4.7.14", + "main": [ + "build/css/bootstrap-datetimepicker.min.css", + "build/js/bootstrap-datetimepicker.min.js", + "src/less/_bootstrap-datetimepicker.less", + "src/less/bootstrap-datetimepicker-build.less", + "src/js/bootstrap-datetimepicker.js" + ], + "dependencies": { + "jquery": ">=1.8.3", + "moment": ">=2.8.0" + }, + "homepage": "https://github.com/Eonasdan/bootstrap-datetimepicker", + "authors": [ + "Eonasdan" + ], + "description": "bootstrap3 datetimepicker", + "keywords": [ + "twitter-bootstrap", + "bootstrap", + "datepicker", + "datetimepicker", + "timepicker", + "moment" + ], + "license": "MIT", + "private": false, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.css b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.css new file mode 100755 index 0000000..cfb7c9a --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.css @@ -0,0 +1,366 @@ +/*! + * Datetimepicker for Bootstrap 3 + * ! version : 4.7.14 + * https://github.com/Eonasdan/bootstrap-datetimepicker/ + */ +.bootstrap-datetimepicker-widget { + list-style: none; +} +.bootstrap-datetimepicker-widget.dropdown-menu { + margin: 2px 0; + padding: 4px; + width: 19em; +} +@media (min-width: 768px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +@media (min-width: 992px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +@media (min-width: 1200px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +.bootstrap-datetimepicker-widget.dropdown-menu:before, +.bootstrap-datetimepicker-widget.dropdown-menu:after { + content: ''; + display: inline-block; + position: absolute; +} +.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #cccccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + top: -7px; + left: 7px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + top: -6px; + left: 8px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.top:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid #cccccc; + border-top-color: rgba(0, 0, 0, 0.2); + bottom: -7px; + left: 6px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.top:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid white; + bottom: -6px; + left: 7px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before { + left: auto; + right: 6px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after { + left: auto; + right: 7px; +} +.bootstrap-datetimepicker-widget .list-unstyled { + margin: 0; +} +.bootstrap-datetimepicker-widget a[data-action] { + padding: 6px 0; +} +.bootstrap-datetimepicker-widget a[data-action]:active { + box-shadow: none; +} +.bootstrap-datetimepicker-widget .timepicker-hour, +.bootstrap-datetimepicker-widget .timepicker-minute, +.bootstrap-datetimepicker-widget .timepicker-second { + width: 54px; + font-weight: bold; + font-size: 1.2em; + margin: 0; +} +.bootstrap-datetimepicker-widget button[data-action] { + padding: 6px; +} +.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Increment Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Increment Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Decrement Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Decrement Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Show Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Show Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Toggle AM/PM"; +} +.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Clear the picker"; +} +.bootstrap-datetimepicker-widget .btn[data-action="today"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Set the date to today"; +} +.bootstrap-datetimepicker-widget .picker-switch { + text-align: center; +} +.bootstrap-datetimepicker-widget .picker-switch::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Toggle Date and Time Screens"; +} +.bootstrap-datetimepicker-widget .picker-switch td { + padding: 0; + margin: 0; + height: auto; + width: auto; + line-height: inherit; +} +.bootstrap-datetimepicker-widget .picker-switch td span { + line-height: 2.5; + height: 2.5em; + width: 100%; +} +.bootstrap-datetimepicker-widget table { + width: 100%; + margin: 0; +} +.bootstrap-datetimepicker-widget table td, +.bootstrap-datetimepicker-widget table th { + text-align: center; + border-radius: 4px; +} +.bootstrap-datetimepicker-widget table th { + height: 20px; + line-height: 20px; + width: 20px; +} +.bootstrap-datetimepicker-widget table th.picker-switch { + width: 145px; +} +.bootstrap-datetimepicker-widget table th.disabled, +.bootstrap-datetimepicker-widget table th.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget table th.prev::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Previous Month"; +} +.bootstrap-datetimepicker-widget table th.next::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Next Month"; +} +.bootstrap-datetimepicker-widget table thead tr:first-child th { + cursor: pointer; +} +.bootstrap-datetimepicker-widget table thead tr:first-child th:hover { + background: #eeeeee; +} +.bootstrap-datetimepicker-widget table td { + height: 54px; + line-height: 54px; + width: 54px; +} +.bootstrap-datetimepicker-widget table td.cw { + font-size: .8em; + height: 20px; + line-height: 20px; + color: #777777; +} +.bootstrap-datetimepicker-widget table td.day { + height: 20px; + line-height: 20px; + width: 20px; +} +.bootstrap-datetimepicker-widget table td.day:hover, +.bootstrap-datetimepicker-widget table td.hour:hover, +.bootstrap-datetimepicker-widget table td.minute:hover, +.bootstrap-datetimepicker-widget table td.second:hover { + background: #eeeeee; + cursor: pointer; +} +.bootstrap-datetimepicker-widget table td.old, +.bootstrap-datetimepicker-widget table td.new { + color: #777777; +} +.bootstrap-datetimepicker-widget table td.today { + position: relative; +} +.bootstrap-datetimepicker-widget table td.today:before { + content: ''; + display: inline-block; + border: 0 0 7px 7px solid transparent; + border-bottom-color: #337ab7; + border-top-color: rgba(0, 0, 0, 0.2); + position: absolute; + bottom: 4px; + right: 4px; +} +.bootstrap-datetimepicker-widget table td.active, +.bootstrap-datetimepicker-widget table td.active:hover { + background-color: #337ab7; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.bootstrap-datetimepicker-widget table td.active.today:before { + border-bottom-color: #fff; +} +.bootstrap-datetimepicker-widget table td.disabled, +.bootstrap-datetimepicker-widget table td.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget table td span { + display: inline-block; + width: 54px; + height: 54px; + line-height: 54px; + margin: 2px 1.5px; + cursor: pointer; + border-radius: 4px; +} +.bootstrap-datetimepicker-widget table td span:hover { + background: #eeeeee; +} +.bootstrap-datetimepicker-widget table td span.active { + background-color: #337ab7; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.bootstrap-datetimepicker-widget table td span.old { + color: #777777; +} +.bootstrap-datetimepicker-widget table td span.disabled, +.bootstrap-datetimepicker-widget table td span.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget.usetwentyfour td.hour { + height: 27px; + line-height: 27px; +} +.input-group.date .input-group-addon { + cursor: pointer; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css new file mode 100755 index 0000000..cfb7c9a --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css @@ -0,0 +1,366 @@ +/*! + * Datetimepicker for Bootstrap 3 + * ! version : 4.7.14 + * https://github.com/Eonasdan/bootstrap-datetimepicker/ + */ +.bootstrap-datetimepicker-widget { + list-style: none; +} +.bootstrap-datetimepicker-widget.dropdown-menu { + margin: 2px 0; + padding: 4px; + width: 19em; +} +@media (min-width: 768px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +@media (min-width: 992px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +@media (min-width: 1200px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +.bootstrap-datetimepicker-widget.dropdown-menu:before, +.bootstrap-datetimepicker-widget.dropdown-menu:after { + content: ''; + display: inline-block; + position: absolute; +} +.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #cccccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + top: -7px; + left: 7px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + top: -6px; + left: 8px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.top:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid #cccccc; + border-top-color: rgba(0, 0, 0, 0.2); + bottom: -7px; + left: 6px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.top:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid white; + bottom: -6px; + left: 7px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before { + left: auto; + right: 6px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after { + left: auto; + right: 7px; +} +.bootstrap-datetimepicker-widget .list-unstyled { + margin: 0; +} +.bootstrap-datetimepicker-widget a[data-action] { + padding: 6px 0; +} +.bootstrap-datetimepicker-widget a[data-action]:active { + box-shadow: none; +} +.bootstrap-datetimepicker-widget .timepicker-hour, +.bootstrap-datetimepicker-widget .timepicker-minute, +.bootstrap-datetimepicker-widget .timepicker-second { + width: 54px; + font-weight: bold; + font-size: 1.2em; + margin: 0; +} +.bootstrap-datetimepicker-widget button[data-action] { + padding: 6px; +} +.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Increment Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Increment Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Decrement Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Decrement Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Show Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Show Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Toggle AM/PM"; +} +.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Clear the picker"; +} +.bootstrap-datetimepicker-widget .btn[data-action="today"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Set the date to today"; +} +.bootstrap-datetimepicker-widget .picker-switch { + text-align: center; +} +.bootstrap-datetimepicker-widget .picker-switch::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Toggle Date and Time Screens"; +} +.bootstrap-datetimepicker-widget .picker-switch td { + padding: 0; + margin: 0; + height: auto; + width: auto; + line-height: inherit; +} +.bootstrap-datetimepicker-widget .picker-switch td span { + line-height: 2.5; + height: 2.5em; + width: 100%; +} +.bootstrap-datetimepicker-widget table { + width: 100%; + margin: 0; +} +.bootstrap-datetimepicker-widget table td, +.bootstrap-datetimepicker-widget table th { + text-align: center; + border-radius: 4px; +} +.bootstrap-datetimepicker-widget table th { + height: 20px; + line-height: 20px; + width: 20px; +} +.bootstrap-datetimepicker-widget table th.picker-switch { + width: 145px; +} +.bootstrap-datetimepicker-widget table th.disabled, +.bootstrap-datetimepicker-widget table th.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget table th.prev::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Previous Month"; +} +.bootstrap-datetimepicker-widget table th.next::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Next Month"; +} +.bootstrap-datetimepicker-widget table thead tr:first-child th { + cursor: pointer; +} +.bootstrap-datetimepicker-widget table thead tr:first-child th:hover { + background: #eeeeee; +} +.bootstrap-datetimepicker-widget table td { + height: 54px; + line-height: 54px; + width: 54px; +} +.bootstrap-datetimepicker-widget table td.cw { + font-size: .8em; + height: 20px; + line-height: 20px; + color: #777777; +} +.bootstrap-datetimepicker-widget table td.day { + height: 20px; + line-height: 20px; + width: 20px; +} +.bootstrap-datetimepicker-widget table td.day:hover, +.bootstrap-datetimepicker-widget table td.hour:hover, +.bootstrap-datetimepicker-widget table td.minute:hover, +.bootstrap-datetimepicker-widget table td.second:hover { + background: #eeeeee; + cursor: pointer; +} +.bootstrap-datetimepicker-widget table td.old, +.bootstrap-datetimepicker-widget table td.new { + color: #777777; +} +.bootstrap-datetimepicker-widget table td.today { + position: relative; +} +.bootstrap-datetimepicker-widget table td.today:before { + content: ''; + display: inline-block; + border: 0 0 7px 7px solid transparent; + border-bottom-color: #337ab7; + border-top-color: rgba(0, 0, 0, 0.2); + position: absolute; + bottom: 4px; + right: 4px; +} +.bootstrap-datetimepicker-widget table td.active, +.bootstrap-datetimepicker-widget table td.active:hover { + background-color: #337ab7; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.bootstrap-datetimepicker-widget table td.active.today:before { + border-bottom-color: #fff; +} +.bootstrap-datetimepicker-widget table td.disabled, +.bootstrap-datetimepicker-widget table td.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget table td span { + display: inline-block; + width: 54px; + height: 54px; + line-height: 54px; + margin: 2px 1.5px; + cursor: pointer; + border-radius: 4px; +} +.bootstrap-datetimepicker-widget table td span:hover { + background: #eeeeee; +} +.bootstrap-datetimepicker-widget table td span.active { + background-color: #337ab7; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.bootstrap-datetimepicker-widget table td span.old { + color: #777777; +} +.bootstrap-datetimepicker-widget table td span.disabled, +.bootstrap-datetimepicker-widget table td span.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget.usetwentyfour td.hour { + height: 27px; + line-height: 27px; +} +.input-group.date .input-group-addon { + cursor: pointer; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js new file mode 100755 index 0000000..ed9c03a --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js @@ -0,0 +1,8 @@ +/*! version : 4.7.14 + ========================================================= + bootstrap-datetimejs + https://github.com/Eonasdan/bootstrap-datetimepicker + Copyright (c) 2015 Jonathan Peterson + ========================================================= + */ +!function(a){"use strict";if("function"==typeof define&&define.amd)define(["jquery","moment"],a);else if("object"==typeof exports)a(require("jquery"),require("moment"));else{if("undefined"==typeof jQuery)throw"bootstrap-datetimepicker requires jQuery to be loaded first";if("undefined"==typeof moment)throw"bootstrap-datetimepicker requires Moment.js to be loaded first";a(jQuery,moment)}}(function(a,b){"use strict";if(!b)throw new Error("bootstrap-datetimepicker requires Moment.js to be loaded first");var c=function(c,d){var e,f,g,h,i,j={},k=b().startOf("d"),l=k.clone(),m=!0,n=!1,o=!1,p=0,q=[{clsName:"days",navFnc:"M",navStep:1},{clsName:"months",navFnc:"y",navStep:1},{clsName:"years",navFnc:"y",navStep:10}],r=["days","months","years"],s=["top","bottom","auto"],t=["left","right","auto"],u=["default","top","bottom"],v={up:38,38:"up",down:40,40:"down",left:37,37:"left",right:39,39:"right",tab:9,9:"tab",escape:27,27:"escape",enter:13,13:"enter",pageUp:33,33:"pageUp",pageDown:34,34:"pageDown",shift:16,16:"shift",control:17,17:"control",space:32,32:"space",t:84,84:"t","delete":46,46:"delete"},w={},x=function(a){if("string"!=typeof a||a.length>1)throw new TypeError("isEnabled expects a single character string parameter");switch(a){case"y":return-1!==g.indexOf("Y");case"M":return-1!==g.indexOf("M");case"d":return-1!==g.toLowerCase().indexOf("d");case"h":case"H":return-1!==g.toLowerCase().indexOf("h");case"m":return-1!==g.indexOf("m");case"s":return-1!==g.indexOf("s");default:return!1}},y=function(){return x("h")||x("m")||x("s")},z=function(){return x("y")||x("M")||x("d")},A=function(){var b=a("").append(a("").append(a("").addClass("prev").attr("data-action","previous").append(a("").addClass(d.icons.previous))).append(a("").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",d.calendarWeeks?"6":"5")).append(a("").addClass("next").attr("data-action","next").append(a("").addClass(d.icons.next)))),c=a("").append(a("").append(a("").attr("colspan",d.calendarWeeks?"8":"7")));return[a("
    ").addClass("datepicker-days").append(a("").addClass("table-condensed").append(b).append(a(""))),a("
    ").addClass("datepicker-months").append(a("
    ").addClass("table-condensed").append(b.clone()).append(c.clone())),a("
    ").addClass("datepicker-years").append(a("
    ").addClass("table-condensed").append(b.clone()).append(c.clone()))]},B=function(){var b=a(""),c=a(""),e=a("");return x("h")&&(b.append(a("'}),a+=""}),a+="
    ").append(a("").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","incrementHours").append(a("").addClass(d.icons.up)))),c.append(a("").append(a("").addClass("timepicker-hour").attr("data-time-component","hours").attr("data-action","showHours"))),e.append(a("").append(a("").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","decrementHours").append(a("").addClass(d.icons.down))))),x("m")&&(x("h")&&(b.append(a("").addClass("separator")),c.append(a("").addClass("separator").html(":")),e.append(a("").addClass("separator"))),b.append(a("").append(a("").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","incrementMinutes").append(a("").addClass(d.icons.up)))),c.append(a("").append(a("").addClass("timepicker-minute").attr("data-time-component","minutes").attr("data-action","showMinutes"))),e.append(a("").append(a("").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","decrementMinutes").append(a("").addClass(d.icons.down))))),x("s")&&(x("m")&&(b.append(a("").addClass("separator")),c.append(a("").addClass("separator").html(":")),e.append(a("").addClass("separator"))),b.append(a("").append(a("").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","incrementSeconds").append(a("").addClass(d.icons.up)))),c.append(a("").append(a("").addClass("timepicker-second").attr("data-time-component","seconds").attr("data-action","showSeconds"))),e.append(a("").append(a("").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","decrementSeconds").append(a("").addClass(d.icons.down))))),f||(b.append(a("").addClass("separator")),c.append(a("").append(a("").addClass("separator"))),a("
    ").addClass("timepicker-picker").append(a("").addClass("table-condensed").append([b,c,e]))},C=function(){var b=a("
    ").addClass("timepicker-hours").append(a("
    ").addClass("table-condensed")),c=a("
    ").addClass("timepicker-minutes").append(a("
    ").addClass("table-condensed")),d=a("
    ").addClass("timepicker-seconds").append(a("
    ").addClass("table-condensed")),e=[B()];return x("h")&&e.push(b),x("m")&&e.push(c),x("s")&&e.push(d),e},D=function(){var b=[];return d.showTodayButton&&b.push(a("":/^(UL|OL)$/.test(r.nodeName)&&(e="
  • "+o+"
  • "),i=n.settings.forced_root_block,i&&n.schema.isValidChild(r.nodeName.toLowerCase(),i.toLowerCase())?(e=o,e=n.dom.createHTML(i,n.settings.forced_root_block_attrs,e)):V||e||(e='
    '),n.dom.setHTML(r,e),n.fire("SetContent",t)):("raw"!==t.format&&(e=new s({validate:n.validate},n.schema).serialize(n.parser.parse(e,{isRootContent:!0}))),t.content=F(e),n.dom.setHTML(r,t.content),t.no_events||n.fire("SetContent",t)),t.content},getContent:function(e){var t=this,n,r=t.getBody();return e=e||{},e.format=e.format||"html",e.get=!0,e.getInner=!0,e.no_events||t.fire("BeforeGetContent",e),n="raw"==e.format?t.serializer.getTrimmedContent():"text"==e.format?r.innerText||r.textContent:t.serializer.serialize(r,e),"text"!=e.format?e.content=F(n):e.content=n,e.no_events||t.fire("GetContent",e),e.content},insertContent:function(e,t){t&&(e=P({content:e},t)),this.execCommand("mceInsertContent",!1,e)},isDirty:function(){return!this.isNotDirty},setDirty:function(e){var t=!this.isNotDirty;this.isNotDirty=!e,e&&e!=t&&this.fire("dirty")},setMode:function(e){S.setMode(this,e)},getContainer:function(){var e=this;return e.container||(e.container=D.get(e.editorContainer||e.id+"_parent")),e.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return this.targetElm||(this.targetElm=D.get(this.id)),this.targetElm},getWin:function(){var e=this,t;return e.contentWindow||(t=e.iframeElement,t&&(e.contentWindow=t.contentWindow)),e.contentWindow},getDoc:function(){var e=this,t;return e.contentDocument||(t=e.getWin(),t&&(e.contentDocument=t.document)),e.contentDocument},getBody:function(){var e=this.getDoc();return this.bodyElement||(e?e.body:null)},convertURL:function(e,t,n){var r=this,i=r.settings;return i.urlconverter_callback?r.execCallback("urlconverter_callback",e,n,!0,t):!i.convert_urls||n&&"LINK"==n.nodeName||0===e.indexOf("file:")||0===e.length?e:i.relative_urls?r.documentBaseURI.toRelative(e):e=r.documentBaseURI.toAbsolute(e,i.remove_script_host)},addVisual:function(e){var n=this,r=n.settings,i=n.dom,o;e=e||n.getBody(),n.hasVisual===t&&(n.hasVisual=r.visual),O(i.select("table,a",e),function(e){var t;switch(e.nodeName){case"TABLE":return o=r.visual_table_class||"mce-item-table",t=i.getAttrib(e,"border"),void(t&&"0"!=t||!n.hasVisual?i.removeClass(e,o):i.addClass(e,o));case"A":return void(i.getAttrib(e,"href",!1)||(t=i.getAttrib(e,"name")||e.id,o=r.visual_anchor_class||"mce-item-anchor",t&&n.hasVisual?i.addClass(e,o):i.removeClass(e,o)))}}),n.fire("VisualAid",{element:e,hasVisual:n.hasVisual})},remove:function(){var e=this;e.removed||(e.save(),e.removed=1,e.unbindAllNativeEvents(),e.hasHiddenInput&&D.remove(e.getElement().nextSibling),e.inline||(V&&10>V&&e.getDoc().execCommand("SelectAll",!1,null),D.setStyle(e.id,"display",e.orgDisplay),e.getBody().onload=null),e.fire("remove"),e.editorManager.remove(e),D.remove(e.getContainer()),e._selectionOverrides.destroy(),e.editorUpload.destroy(),e.destroy())},destroy:function(e){var t=this,n;if(!t.destroyed){if(!e&&!t.removed)return void t.remove();e||(t.editorManager.off("beforeunload",t._beforeUnload),t.theme&&t.theme.destroy&&t.theme.destroy(),t.selection.destroy(),t.dom.destroy()),n=t.formElement,n&&(n._mceOldSubmit&&(n.submit=n._mceOldSubmit,n._mceOldSubmit=null),D.unbind(n,"submit reset",t.formEventDelegate)),t.contentAreaContainer=t.formElement=t.container=t.editorContainer=null,t.bodyElement=t.contentDocument=t.contentWindow=null,t.iframeElement=t.targetElm=null,t.selection&&(t.selection=t.selection.win=t.selection.dom=t.selection.dom.doc=null),t.destroyed=1}},uploadImages:function(e){return this.editorUpload.uploadImages(e)},_scanForImages:function(){return this.editorUpload.scanForImages()}},P(B.prototype,_),B}),r(nt,[],function(){var e={},t="en";return{setCode:function(e){e&&(t=e,this.rtl=this.data[e]?"rtl"===this.data[e]._dir:!1)},getCode:function(){return t},rtl:!1,add:function(t,n){var r=e[t];r||(e[t]=r={});for(var i in n)r[i]=n[i];this.setCode(t)},translate:function(n){var r;if(r=e[t],r||(r={}),"undefined"==typeof n)return n;if("string"!=typeof n&&n.raw)return n.raw;if(n.push){var i=n.slice(1);n=(r[n[0]]||n[0]).replace(/\{([0-9]+)\}/g,function(e,t){return i[t]})}return(r[n]||n).replace(/{context:\w+}$/,"")},data:e}}),r(rt,[w,u,d],function(e,t,n){function r(e){function l(){try{return document.activeElement}catch(e){return document.body}}function c(e,t){if(t&&t.startContainer){if(!e.isChildOf(t.startContainer,e.getRoot())||!e.isChildOf(t.endContainer,e.getRoot()))return;return{startContainer:t.startContainer, +startOffset:t.startOffset,endContainer:t.endContainer,endOffset:t.endOffset}}return t}function u(e,t){var n;return t.startContainer?(n=e.getDoc().createRange(),n.setStart(t.startContainer,t.startOffset),n.setEnd(t.endContainer,t.endOffset)):n=t,n}function d(e){return!!s.getParent(e,r.isEditorUIElement)}function f(r){var f=r.editor;f.on("init",function(){(f.inline||n.ie)&&("onbeforedeactivate"in document&&n.ie<9?f.dom.bind(f.getBody(),"beforedeactivate",function(e){if(e.target==f.getBody())try{f.lastRng=f.selection.getRng()}catch(t){}}):f.on("nodechange mouseup keyup",function(e){var t=l();"nodechange"==e.type&&e.selectionChange||(t&&t.id==f.id+"_ifr"&&(t=f.getBody()),f.dom.isChildOf(t,f.getBody())&&(f.lastRng=f.selection.getRng()))}),n.webkit&&!i&&(i=function(){var t=e.activeEditor;if(t&&t.selection){var n=t.selection.getRng();n&&!n.collapsed&&(f.lastRng=n)}},s.bind(document,"selectionchange",i)))}),f.on("setcontent",function(){f.lastRng=null}),f.on("mousedown",function(){f.selection.lastFocusBookmark=null}),f.on("focusin",function(){var t=e.focusedEditor,n;f.selection.lastFocusBookmark&&(n=u(f,f.selection.lastFocusBookmark),f.selection.lastFocusBookmark=null,f.selection.setRng(n)),t!=f&&(t&&t.fire("blur",{focusedEditor:f}),e.setActive(f),e.focusedEditor=f,f.fire("focus",{blurredEditor:t}),f.focus(!0)),f.lastRng=null}),f.on("focusout",function(){t.setEditorTimeout(f,function(){var t=e.focusedEditor;d(l())||t!=f||(f.fire("blur",{focusedEditor:null}),e.focusedEditor=null,f.selection&&(f.selection.lastFocusBookmark=null))})}),o||(o=function(t){var n=e.activeEditor,r;r=t.target,n&&r.ownerDocument==document&&(n.selection&&r!=n.getBody()&&(n.selection.lastFocusBookmark=c(n.dom,n.lastRng)),r==document.body||d(r)||e.focusedEditor!=n||(n.fire("blur",{focusedEditor:null}),e.focusedEditor=null))},s.bind(document,"focusin",o)),f.inline&&!a&&(a=function(t){var n=e.activeEditor,r=n.dom;if(n.inline&&r&&!r.isChildOf(t.target,n.getBody())){var i=n.selection.getRng();i.collapsed||(n.lastRng=i)}},s.bind(document,"mouseup",a))}function h(t){e.focusedEditor==t.editor&&(e.focusedEditor=null),e.activeEditor||(s.unbind(document,"selectionchange",i),s.unbind(document,"focusin",o),s.unbind(document,"mouseup",a),i=o=a=null)}e.on("AddEditor",f),e.on("RemoveEditor",h)}var i,o,a,s=e.DOM;return r.isEditorUIElement=function(e){return-1!==e.className.toString().indexOf("mce-")},r}),r(it,[tt,g,w,ae,d,m,c,ue,nt,rt],function(e,t,n,r,i,o,a,s,l,c){function u(e){g(C.editors,function(t){"scroll"===e.type?t.fire("ScrollWindow",e):t.fire("ResizeWindow",e)})}function d(e,n){n!==x&&(n?t(window).on("resize scroll",u):t(window).off("resize scroll",u),x=n)}function f(e){var t=C.editors,n;delete t[e.id];for(var r=0;r0&&g(m(t),function(e){var t;(t=p.get(e))?n.push(t):g(document.forms,function(t){g(t.elements,function(t){t.name===e&&(e="mce_editor_"+y++,p.setAttrib(t,"id",e),n.push(t))})})});break;case"textareas":case"specific_textareas":g(p.select("textarea"),function(t){e.editor_deselector&&c(t,e.editor_deselector)||e.editor_selector&&!c(t,e.editor_selector)||n.push(t)})}return n}function d(){function a(t,n,r){var i=new e(t,n,f);m.push(i),i.on("init",function(){++c===y.length&&x(m)}),i.targetElm=i.targetElm||r,i.render()}var c=0,m=[],y;return p.unbind(window,"ready",d),l("onpageload"),y=t.unique(u(n)),n.types?void g(n.types,function(e){o.each(y,function(t){return p.is(t,e.selector)?(a(s(t),v({},n,e),t),!1):!0})}):(o.each(y,function(e){h(f.get(e.id))}),y=o.grep(y,function(e){return!f.get(e.id)}),void g(y,function(e){r(n,e)?i("Could not initialize inline editor on invalid inline target element",e):a(s(e),n,e)}))}var f=this,b,C;C=o.makeMap("area base basefont br col frame hr img input isindex link meta param embed source wbr track colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu"," ");var x=function(e){b=e};return f.settings=n,p.bind(window,"ready",d),new a(function(e){b?e(b):x=function(t){e(t)}})},get:function(e){return arguments.length?e in this.editors?this.editors[e]:null:this.editors},add:function(e){var t=this,n=t.editors;return n[e.id]=e,n.push(e),d(n,!0),t.activeEditor=e,t.fire("AddEditor",{editor:e}),b||(b=function(){t.fire("BeforeUnload")},p.bind(window,"beforeunload",b)),e},createEditor:function(t,n){return this.add(new e(t,n,this))},remove:function(e){var t=this,n,r=t.editors,i;{if(e)return"string"==typeof e?(e=e.selector||e,void g(p.select(e),function(e){i=r[e.id],i&&t.remove(i)})):(i=e,r[i.id]?(f(i)&&t.fire("RemoveEditor",{editor:i}),r.length||p.unbind(window,"beforeunload",b),i.remove(),d(r,r.length>0),i):null);for(n=r.length-1;n>=0;n--)t.remove(r[n])}},execCommand:function(t,n,r){var i=this,o=i.get(r);switch(t){case"mceAddEditor":return i.get(r)||new e(r,i.settings,i).render(),!0;case"mceRemoveEditor":return o&&o.remove(),!0;case"mceToggleEditor":return o?(o.isHidden()?o.show():o.hide(),!0):(i.execCommand("mceAddEditor",0,r),!0)}return i.activeEditor?i.activeEditor.execCommand(t,n,r):!1},triggerSave:function(){g(this.editors,function(e){e.save()})},addI18n:function(e,t){l.add(e,t)},translate:function(e){return l.translate(e)},setActive:function(e){var t=this.activeEditor;this.activeEditor!=e&&(t&&t.fire("deactivate",{relatedTarget:e}),e.fire("activate",{relatedTarget:t})),this.activeEditor=e}},v(C,s),C.setup(),window.tinymce=window.tinyMCE=C,C}),r(ot,[it,m],function(e,t){var n=t.each,r=t.explode;e.on("AddEditor",function(e){var t=e.editor;t.on("preInit",function(){function e(e,t){n(t,function(t,n){t&&s.setStyle(e,n,t)}),s.rename(e,"span")}function i(e){s=t.dom,l.convert_fonts_to_spans&&n(s.select("font,u,strike",e.node),function(e){o[e.nodeName.toLowerCase()](s,e)})}var o,a,s,l=t.settings;l.inline_styles&&(a=r(l.font_size_legacy_values),o={font:function(t,n){e(n,{backgroundColor:n.style.backgroundColor,color:n.color,fontFamily:n.face,fontSize:a[parseInt(n.size,10)-1]})},u:function(n,r){"html4"===t.settings.schema&&e(r,{textDecoration:"underline"})},strike:function(t,n){e(n,{textDecoration:"line-through"})}},t.on("PreProcess SetContent",i))})})}),r(at,[ue,m],function(e,t){var n={send:function(e){function r(){!e.async||4==i.readyState||o++>1e4?(e.success&&1e4>o&&200==i.status?e.success.call(e.success_scope,""+i.responseText,i,e):e.error&&e.error.call(e.error_scope,o>1e4?"TIMED_OUT":"GENERAL",i,e),i=null):setTimeout(r,10)}var i,o=0;if(e.scope=e.scope||this,e.success_scope=e.success_scope||e.scope,e.error_scope=e.error_scope||e.scope,e.async=e.async!==!1,e.data=e.data||"",n.fire("beforeInitialize",{settings:e}),i=new XMLHttpRequest){if(i.overrideMimeType&&i.overrideMimeType(e.content_type),i.open(e.type||(e.data?"POST":"GET"),e.url,e.async),e.crossDomain&&(i.withCredentials=!0),e.content_type&&i.setRequestHeader("Content-Type",e.content_type),e.requestheaders&&t.each(e.requestheaders,function(e){i.setRequestHeader(e.key,e.value)}),i.setRequestHeader("X-Requested-With","XMLHttpRequest"),i=n.fire("beforeSend",{xhr:i,settings:e}).xhr,i.send(e.data),!e.async)return r();setTimeout(r,10)}}};return t.extend(n,e),n}),r(st,[],function(){function e(t,n){var r,i,o,a;if(n=n||'"',null===t)return"null";if(o=typeof t,"string"==o)return i="\bb t\nn\ff\rr\"\"''\\\\",n+t.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(e,t){return'"'===n&&"'"===e?e:(r=i.indexOf(t),r+1?"\\"+i.charAt(r+1):(e=t.charCodeAt().toString(16),"\\u"+"0000".substring(e.length)+e))})+n;if("object"==o){if(t.hasOwnProperty&&"[object Array]"===Object.prototype.toString.call(t)){for(r=0,i="[";r0?",":"")+e(t[r],n);return i+"]"}i="{";for(a in t)t.hasOwnProperty(a)&&(i+="function"!=typeof t[a]?(i.length>1?","+n:n)+a+n+":"+e(t[a],n):"");return i+"}"}return""+t}return{serialize:e,parse:function(e){try{return window[String.fromCharCode(101)+"val"]("("+e+")")}catch(t){}}}}),r(lt,[st,at,m],function(e,t,n){function r(e){this.settings=i({},e),this.count=0}var i=n.extend;return r.sendRPC=function(e){return(new r).send(e)},r.prototype={send:function(n){var r=n.error,o=n.success;n=i(this.settings,n),n.success=function(t,i){t=e.parse(t),"undefined"==typeof t&&(t={error:"JSON Parse error."}),t.error?r.call(n.error_scope||n.scope,t.error,i):o.call(n.success_scope||n.scope,t.result)},n.error=function(e,t){r&&r.call(n.error_scope||n.scope,e,t)},n.data=e.serialize({id:n.id||"c"+this.count++,method:n.method,params:n.params}),n.content_type="application/json",t.send(n)}},r}),r(ct,[w],function(e){return{callbacks:{},count:0,send:function(n){var r=this,i=e.DOM,o=n.count!==t?n.count:r.count,a="tinymce_jsonp_"+o;r.callbacks[o]=function(e){i.remove(a),delete r.callbacks[o],n.callback(e)},i.add(i.doc.body,"script",{id:a,src:n.url,type:"text/javascript"}),r.count++}}}),r(ut,[],function(){function e(){s=[];for(var e in a)s.push(e);i.length=s.length}function n(){function n(e){var n,r;return r=e!==t?u+e:i.indexOf(",",u),-1===r||r>i.length?null:(n=i.substring(u,r),u=r+1,n)}var r,i,s,u=0;if(a={},c){o.load(l),i=o.getAttribute(l)||"";do{var d=n();if(null===d)break;if(r=n(parseInt(d,32)||0),null!==r){if(d=n(),null===d)break;s=n(parseInt(d,32)||0),r&&(a[r]=s)}}while(null!==r);e()}}function r(){var t,n="";if(c){for(var r in a)t=a[r],n+=(n?",":"")+r.length.toString(32)+","+r+","+t.length.toString(32)+","+t;o.setAttribute(l,n);try{o.save(l)}catch(i){}e()}}var i,o,a,s,l,c;try{if(window.localStorage)return localStorage}catch(u){}return l="tinymce",o=document.documentElement,c=!!o.addBehavior,c&&o.addBehavior("#default#userData"),i={key:function(e){return s[e]},getItem:function(e){return e in a?a[e]:null},setItem:function(e,t){a[e]=""+t,r()},removeItem:function(e){delete a[e],r()},clear:function(){a={},r()}},n(),i}),r(dt,[w,f,N,E,m,d],function(e,t,n,r,i,o){var a=window.tinymce;return a.DOM=e.DOM,a.ScriptLoader=n.ScriptLoader,a.PluginManager=r.PluginManager,a.ThemeManager=r.ThemeManager,a.dom=a.dom||{},a.dom.Event=t.Event,i.each(i,function(e,t){a[t]=e}),i.each("isOpera isWebKit isIE isGecko isMac".split(" "),function(e){a[e]=o[e.substr(2).toLowerCase()]}),{}}),r(ft,[se,m],function(e,t){return e.extend({Defaults:{firstControlClass:"first",lastControlClass:"last"},init:function(e){this.settings=t.extend({},this.Defaults,e)},preRender:function(e){e.bodyClasses.add(this.settings.containerClass)},applyClasses:function(e){var t=this,n=t.settings,r,i,o,a;r=n.firstControlClass,i=n.lastControlClass,e.each(function(e){e.classes.remove(r).remove(i).add(n.controlClass),e.visible()&&(o||(o=e),a=e)}),o&&o.classes.add(r),a&&a.classes.add(i)},renderHtml:function(e){var t=this,n="";return t.applyClasses(e.items()),e.items().each(function(e){n+=e.renderHtml()}),n},recalc:function(){},postRender:function(){},isNative:function(){return!1}})}),r(ht,[ft],function(e){return e.extend({Defaults:{containerClass:"abs-layout",controlClass:"abs-layout-item"},recalc:function(e){e.items().filter(":visible").each(function(e){var t=e.settings;e.layoutRect({x:t.x,y:t.y,w:t.w,h:t.h}),e.recalc&&e.recalc()})},renderHtml:function(e){return'
    '+this._super(e)}})}),r(pt,[De],function(e){return e.extend({Defaults:{classes:"widget btn",role:"button"},init:function(e){var t=this,n;t._super(e),e=t.settings,n=t.settings.size,t.on("click mousedown",function(e){e.preventDefault()}),t.on("touchstart",function(e){t.fire("click",e),e.preventDefault()}),e.subtype&&t.classes.add(e.subtype),n&&t.classes.add("btn-"+n),e.icon&&t.icon(e.icon)},icon:function(e){return arguments.length?(this.state.set("icon",e),this):this.state.get("icon")},repaint:function(){var e=this.getEl().firstChild,t;e&&(t=e.style,t.width=t.height="100%"),this._super()},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.state.get("icon"),i,o=e.state.get("text"),a="";return i=e.settings.image,i?(r="none","string"!=typeof i&&(i=window.getSelection?i[0]:i[1]),i=" style=\"background-image: url('"+i+"')\""):i="",o&&(e.classes.add("btn-has-text"),a=''+e.encode(o)+""),r=e.settings.icon?n+"ico "+n+"i-"+r:"",'
    "},bindStates:function(){function e(e){var i=n("span."+r,t.getEl());e?(i[0]||(n("button:first",t.getEl()).append(''),i=n("span."+r,t.getEl())),i.html(t.encode(e))):i.remove(),t.classes.toggle("btn-has-text",!!e)}var t=this,n=t.$,r=t.classPrefix+"txt";return t.state.on("change:text",function(t){e(t.value)}),t.state.on("change:icon",function(n){var r=n.value,i=t.classPrefix;t.settings.icon=r,r=r?i+"ico "+i+"i-"+t.settings.icon:"";var o=t.getEl().firstChild,a=o.getElementsByTagName("i")[0];r?(a&&a==o.firstChild||(a=document.createElement("i"),o.insertBefore(a,o.firstChild)),a.className=r):a&&o.removeChild(a),e(t.state.get("text"))}),t._super()}})}),r(mt,[xe],function(e){return e.extend({Defaults:{defaultType:"button",role:"group"},renderHtml:function(){var e=this,t=e._layout;return e.classes.add("btn-group"),e.preRender(),t.preRender(e),'
    '+(e.settings.html||"")+t.renderHtml(e)+"
    "}})}),r(gt,[De],function(e){return e.extend({Defaults:{classes:"checkbox",role:"checkbox",checked:!1},init:function(e){var t=this;t._super(e),t.on("click mousedown",function(e){e.preventDefault()}),t.on("click",function(e){e.preventDefault(),t.disabled()||t.checked(!t.checked())}),t.checked(t.settings.checked)},checked:function(e){return arguments.length?(this.state.set("checked",e),this):this.state.get("checked")},value:function(e){return arguments.length?this.checked(e):this.checked()},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix;return'
    '+e.encode(e.state.get("text"))+"
    "},bindStates:function(){function e(e){t.classes.toggle("checked",e),t.aria("checked",e)}var t=this;return t.state.on("change:text",function(e){t.getEl("al").firstChild.data=t.translate(e.value)}),t.state.on("change:checked change:value",function(n){t.fire("change"),e(n.value)}),t.state.on("change:icon",function(e){var n=e.value,r=t.classPrefix;if("undefined"==typeof n)return t.settings.icon;t.settings.icon=n,n=n?r+"ico "+r+"i-"+t.settings.icon:"";var i=t.getEl().firstChild,o=i.getElementsByTagName("i")[0];n?(o&&o==i.firstChild||(o=document.createElement("i"),i.insertBefore(o,i.firstChild)),o.className=n):o&&i.removeChild(o)}),t.state.get("checked")&&e(!0),t._super()}})}),r(vt,[De,be,pe,g],function(e,t,n,r){return e.extend({init:function(e){var t=this;t._super(e),e=t.settings,t.classes.add("combobox"),t.subinput=!0,t.ariaTarget="inp",e.menu=e.menu||e.values,e.menu&&(e.icon="caret"),t.on("click",function(n){var i=n.target,o=t.getEl();if(r.contains(o,i)||i==o)for(;i&&i!=o;)i.id&&-1!=i.id.indexOf("-open")&&(t.fire("action"),e.menu&&(t.showMenu(),n.aria&&t.menu.items()[0].focus())),i=i.parentNode}),t.on("keydown",function(e){"INPUT"==e.target.nodeName&&13==e.keyCode&&t.parents().reverse().each(function(n){var r=t.state.get("value"),i=t.getEl("inp").value;return e.preventDefault(),t.state.set("value",i),r!=i&&t.fire("change"),n.hasEventListeners("submit")&&n.toJSON?(n.fire("submit",{data:n.toJSON()}),!1):void 0})}),t.on("keyup",function(e){"INPUT"==e.target.nodeName&&t.state.set("value",e.target.value)})},showMenu:function(){var e=this,n=e.settings,r;e.menu||(r=n.menu||[],r.length?r={type:"menu",items:r}:r.type=r.type||"menu",e.menu=t.create(r).parent(e).renderTo(e.getContainerElm()),e.fire("createmenu"),e.menu.reflow(),e.menu.on("cancel",function(t){t.control===e.menu&&e.focus()}),e.menu.on("show hide",function(t){t.control.items().each(function(t){t.active(t.value()==e.value())})}).fire("show"),e.menu.on("select",function(t){e.value(t.control.value())}),e.on("focusin",function(t){"INPUT"==t.target.tagName.toUpperCase()&&e.menu.hide()}),e.aria("expanded",!0)),e.menu.show(),e.menu.layoutRect({w:e.layoutRect().w}),e.menu.moveRel(e.getEl(),e.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"])},focus:function(){this.getEl("inp").focus()},repaint:function(){var e=this,t=e.getEl(),i=e.getEl("open"),o=e.layoutRect(),a,s;a=i?o.w-n.getSize(i).width-10:o.w-10;var l=document;return l.all&&(!l.documentMode||l.documentMode<=8)&&(s=e.layoutRect().h-2+"px"),r(t.firstChild).css({width:a,lineHeight:s}),e._super(),e},postRender:function(){var e=this;return r(this.getEl("inp")).on("change",function(t){e.state.set("value",t.target.value),e.fire("change",t)}),e._super()},renderHtml:function(){var e=this,t=e._id,n=e.settings,r=e.classPrefix,i=e.state.get("value")||"",o,a,s="",l="";return"spellcheck"in n&&(l+=' spellcheck="'+n.spellcheck+'"'),n.maxLength&&(l+=' maxlength="'+n.maxLength+'"'),n.size&&(l+=' size="'+n.size+'"'),n.subtype&&(l+=' type="'+n.subtype+'"'),e.disabled()&&(l+=' disabled="disabled"'),o=n.icon,o&&"caret"!=o&&(o=r+"ico "+r+"i-"+n.icon),a=e.state.get("text"),(o||a)&&(s='
    ",e.classes.add("has-open")),'
    '+s+"
    "},value:function(e){return arguments.length?(this.state.set("value",e),this):(this.state.get("rendered")&&this.state.set("value",this.getEl("inp").value),this.state.get("value"))},bindStates:function(){var e=this;return e.state.on("change:value",function(t){e.getEl("inp").value!=t.value&&(e.getEl("inp").value=t.value)}),e.state.on("change:disabled",function(t){e.getEl("inp").disabled=t.value}),e._super()},remove:function(){r(this.getEl("inp")).off(),this._super()}})}),r(yt,[vt],function(e){return e.extend({init:function(e){var t=this;e.spellcheck=!1,e.onaction&&(e.icon="none"),t._super(e),t.classes.add("colorbox"),t.on("change keyup postrender",function(){t.repaintColor(t.value())})},repaintColor:function(e){var t=this.getEl().getElementsByTagName("i")[0];if(t)try{t.style.background=e}catch(n){}},bindStates:function(){var e=this;return e.state.on("change:value",function(t){e.state.get("rendered")&&e.repaintColor(t.value)}),e._super()}})}),r(bt,[pt,ke],function(e,t){return e.extend({showPanel:function(){var e=this,n=e.settings;if(e.active(!0),e.panel)e.panel.show();else{var r=n.panel;r.type&&(r={layout:"grid",items:r}),r.role=r.role||"dialog",r.popover=!0,r.autohide=!0,r.ariaRoot=!0,e.panel=new t(r).on("hide",function(){e.active(!1)}).on("cancel",function(t){t.stopPropagation(),e.focus(),e.hidePanel()}).parent(e).renderTo(e.getContainerElm()),e.panel.fire("show"),e.panel.reflow()}e.panel.moveRel(e.getEl(),n.popoverAlign||(e.isRtl()?["bc-tr","bc-tc"]:["bc-tl","bc-tc"]))},hidePanel:function(){var e=this;e.panel&&e.panel.hide()},postRender:function(){var e=this;return e.aria("haspopup",!0),e.on("click",function(t){t.control===e&&(e.panel&&e.panel.visible()?e.hidePanel():(e.showPanel(),e.panel.focus(!!t.aria)))}),e._super()},remove:function(){return this.panel&&(this.panel.remove(),this.panel=null),this._super()}})}),r(Ct,[bt,w],function(e,t){var n=t.DOM;return e.extend({init:function(e){this._super(e),this.classes.add("colorbutton")},color:function(e){return e?(this._color=e,this.getEl("preview").style.backgroundColor=e,this):this._color},resetColor:function(){return this._color=null,this.getEl("preview").style.backgroundColor=null,this},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.state.get("text"),i=e.settings.icon?n+"ico "+n+"i-"+e.settings.icon:"",o=e.settings.image?" style=\"background-image: url('"+e.settings.image+"')\"":"",a="";return r&&(e.classes.add("btn-has-text"),a=''+e.encode(r)+""),'
    '},postRender:function(){var e=this,t=e.settings.onclick;return e.on("click",function(r){r.aria&&"down"==r.aria.key||r.control!=e||n.getParent(r.target,"."+e.classPrefix+"open")||(r.stopImmediatePropagation(),t.call(e,r))}),delete e.settings.onclick,e._super()}})}),r(xt,[],function(){function e(e){function i(e,i,o){var a,s,l,c,u,d;return a=0,s=0,l=0,e/=255,i/=255,o/=255,u=t(e,t(i,o)),d=n(e,n(i,o)),u==d?(l=u,{h:0,s:0,v:100*l}):(c=e==u?i-o:o==u?e-i:o-e,a=e==u?3:o==u?1:5,a=60*(a-c/(d-u)),s=(d-u)/d,l=d,{h:r(a),s:r(100*s),v:r(100*l)})}function o(e,i,o){var a,s,l,c;if(e=(parseInt(e,10)||0)%360,i=parseInt(i,10)/100,o=parseInt(o,10)/100,i=n(0,t(i,1)),o=n(0,t(o,1)),0===i)return void(d=f=h=r(255*o));switch(a=e/60,s=o*i,l=s*(1-Math.abs(a%2-1)),c=o-s,Math.floor(a)){case 0:d=s,f=l,h=0;break;case 1:d=l,f=s,h=0;break;case 2:d=0,f=s,h=l;break;case 3:d=0,f=l,h=s;break;case 4:d=l,f=0,h=s;break;case 5:d=s,f=0,h=l;break;default:d=f=h=0}d=r(255*(d+c)),f=r(255*(f+c)),h=r(255*(h+c))}function a(){function e(e){return e=parseInt(e,10).toString(16),e.length>1?e:"0"+e}return"#"+e(d)+e(f)+e(h)}function s(){return{r:d,g:f,b:h}}function l(){return i(d,f,h)}function c(e){var t;return"object"==typeof e?"r"in e?(d=e.r,f=e.g,h=e.b):"v"in e&&o(e.h,e.s,e.v):(t=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(e))?(d=parseInt(t[1],10),f=parseInt(t[2],10),h=parseInt(t[3],10)):(t=/#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(e))?(d=parseInt(t[1],16),f=parseInt(t[2],16),h=parseInt(t[3],16)):(t=/#([0-F])([0-F])([0-F])/gi.exec(e))&&(d=parseInt(t[1]+t[1],16),f=parseInt(t[2]+t[2],16),h=parseInt(t[3]+t[3],16)),d=0>d?0:d>255?255:d,f=0>f?0:f>255?255:f,h=0>h?0:h>255?255:h,u}var u=this,d=0,f=0,h=0;e&&c(e),u.toRgb=s,u.toHsv=l,u.toHex=a,u.parse=c}var t=Math.min,n=Math.max,r=Math.round;return e}),r(wt,[De,we,pe,xt],function(e,t,n,r){return e.extend({Defaults:{classes:"widget colorpicker"},init:function(e){this._super(e)},postRender:function(){function e(e,t){var r=n.getPos(e),i,o;return i=t.pageX-r.x,o=t.pageY-r.y,i=Math.max(0,Math.min(i/e.clientWidth,1)),o=Math.max(0,Math.min(o/e.clientHeight,1)),{x:i,y:o}}function i(e,t){var i=(360-e.h)/360;n.css(d,{top:100*i+"%"}),t||n.css(h,{left:e.s+"%",top:100-e.v+"%"}),f.style.background=new r({s:100,v:100,h:e.h}).toHex(),s.color().parse({s:e.s,v:e.v,h:e.h})}function o(t){var n;n=e(f,t),c.s=100*n.x,c.v=100*(1-n.y),i(c),s.fire("change")}function a(t){var n;n=e(u,t),c=l.toHsv(),c.h=360*(1-n.y),i(c,!0),s.fire("change")}var s=this,l=s.color(),c,u,d,f,h;u=s.getEl("h"),d=s.getEl("hp"),f=s.getEl("sv"),h=s.getEl("svp"),s._repaint=function(){c=l.toHsv(),i(c)},s._super(),s._svdraghelper=new t(s._id+"-sv",{start:o,drag:o}),s._hdraghelper=new t(s._id+"-h",{start:a,drag:a}),s._repaint()},rgb:function(){return this.color().toRgb()},value:function(e){var t=this;return arguments.length?(t.color().parse(e),void(t._rendered&&t._repaint())):t.color().toHex()},color:function(){return this._color||(this._color=new r),this._color},renderHtml:function(){function e(){var e,t,n="",i,a;for(i="filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=",a=o.split(","),e=0,t=a.length-1;t>e;e++)n+='
    ';return n}var t=this,n=t._id,r=t.classPrefix,i,o="#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000",a="background: -ms-linear-gradient(top,"+o+");background: linear-gradient(to bottom,"+o+");";return i='
    '+e()+'
    ','
    '+i+"
    "}})}),r(Nt,[De],function(e){return e.extend({init:function(e){var t=this;e.delimiter||(e.delimiter="\xbb"),t._super(e),t.classes.add("path"),t.canFocus=!0,t.on("click",function(e){var n,r=e.target;(n=r.getAttribute("data-index"))&&t.fire("select",{value:t.row()[n],index:n})}),t.row(t.settings.row)},focus:function(){var e=this;return e.getEl().firstChild.focus(),e},row:function(e){return arguments.length?(this.state.set("row",e),this):this.state.get("row")},renderHtml:function(){var e=this;return'
    '+e._getDataPathHtml(e.state.get("row"))+"
    "},bindStates:function(){var e=this;return e.state.on("change:row",function(t){e.innerHtml(e._getDataPathHtml(t.value))}),e._super()},_getDataPathHtml:function(e){var t=this,n=e||[],r,i,o="",a=t.classPrefix;for(r=0,i=n.length;i>r;r++)o+=(r>0?'":"")+'
    '+n[r].name+"
    ";return o||(o='
    \xa0
    '),o}})}),r(Et,[Nt],function(e){return e.extend({postRender:function(){function e(e){if(1===e.nodeType){if("BR"==e.nodeName||e.getAttribute("data-mce-bogus"))return!0;if("bookmark"===e.getAttribute("data-mce-type"))return!0}return!1}var t=this,n=t.settings.editor;return n.settings.elementpath!==!1&&(t.on("select",function(e){n.focus(),n.selection.select(this.row()[e.index].element),n.nodeChanged()}),n.on("nodeChange",function(r){for(var i=[],o=r.parents,a=o.length;a--;)if(1==o[a].nodeType&&!e(o[a])){var s=n.fire("ResolveName",{name:o[a].nodeName.toLowerCase(),target:o[a]});if(s.isDefaultPrevented()||i.push({name:s.name,element:o[a]}),s.isPropagationStopped())break}t.row(i)})),t._super()}})}),r(_t,[xe],function(e){return e.extend({Defaults:{layout:"flex",align:"center",defaults:{flex:1}},renderHtml:function(){var e=this,t=e._layout,n=e.classPrefix;return e.classes.add("formitem"),t.preRender(e),'
    '+(e.settings.title?'
    '+e.settings.title+"
    ":"")+'
    '+(e.settings.html||"")+t.renderHtml(e)+"
    "}})}),r(St,[xe,_t,m],function(e,t,n){return e.extend({Defaults:{containerCls:"form",layout:"flex",direction:"column",align:"stretch",flex:1,padding:20,labelGap:30,spacing:10,callbacks:{submit:function(){this.submit()}}},preRender:function(){var e=this,r=e.items();e.settings.formItemDefaults||(e.settings.formItemDefaults={layout:"flex",autoResize:"overflow",defaults:{flex:1}}),r.each(function(r){var i,o=r.settings.label;o&&(i=new t(n.extend({items:{type:"label",id:r._id+"-l",text:o,flex:0,forId:r._id,disabled:r.disabled()}},e.settings.formItemDefaults)),i.type="formitem",r.aria("labelledby",r._id+"-l"),"undefined"==typeof r.settings.flex&&(r.settings.flex=1),e.replace(r,i),i.add(r))})},submit:function(){return this.fire("submit",{data:this.toJSON()})},postRender:function(){var e=this;e._super(),e.fromJSON(e.settings.data)},bindStates:function(){function e(){var e=0,n=[],r,i,o;if(t.settings.labelGapCalc!==!1)for(o="children"==t.settings.labelGapCalc?t.find("formitem"):t.items(),o.filter("formitem").each(function(t){var r=t.items()[0],i=r.getEl().clientWidth;e=i>e?i:e,n.push(r)}),i=t.settings.labelGap||0,r=n.length;r--;)n[r].settings.minWidth=e+i}var t=this;t._super(),t.on("show",e),e()}})}),r(kt,[St],function(e){return e.extend({Defaults:{containerCls:"fieldset",layout:"flex",direction:"column",align:"stretch",flex:1,padding:"25 15 5 15",labelGap:30,spacing:10,border:1},renderHtml:function(){var e=this,t=e._layout,n=e.classPrefix;return e.preRender(),t.preRender(e),'
    '+(e.settings.title?''+e.settings.title+"":"")+'
    '+(e.settings.html||"")+t.renderHtml(e)+"
    "}})}),r(Tt,[vt,m],function(e,t){return e.extend({init:function(e){var n=this,r=tinymce.activeEditor,i=r.settings,o,a,s;e.spellcheck=!1,s=i.file_picker_types||i.file_browser_callback_types,s&&(s=t.makeMap(s,/[, ]/)),s&&!s[e.filetype]||(a=i.file_picker_callback,!a||s&&!s[e.filetype]?(a=i.file_browser_callback,!a||s&&!s[e.filetype]||(o=function(){a(n.getEl("inp").id,n.value(),e.filetype,window)})):o=function(){var i=n.fire("beforecall").meta;i=t.extend({filetype:e.filetype},i),a.call(r,function(e,t){n.value(e).fire("change",{meta:t})},n.value(),i)}),o&&(e.icon="browse",e.onaction=o),n._super(e)}})}),r(Rt,[ht],function(e){return e.extend({recalc:function(e){var t=e.layoutRect(),n=e.paddingBox;e.items().filter(":visible").each(function(e){e.layoutRect({x:n.left,y:n.top,w:t.innerW-n.right-n.left,h:t.innerH-n.top-n.bottom}),e.recalc&&e.recalc()})}})}),r(At,[ht],function(e){return e.extend({recalc:function(e){var t,n,r,i,o,a,s,l,c,u,d,f,h,p,m,g,v=[],y,b,C,x,w,N,E,_,S,k,T,R,A,B,D,L,M,P,O,H,I,F,z=Math.max,U=Math.min;for(r=e.items().filter(":visible"),i=e.layoutRect(),o=e.paddingBox,a=e.settings,f=e.isRtl()?a.direction||"row-reversed":a.direction,s=a.align,l=e.isRtl()?a.pack||"end":a.pack,c=a.spacing||0,"row-reversed"!=f&&"column-reverse"!=f||(r=r.set(r.toArray().reverse()),f=f.split("-")[0]),"column"==f?(S="y",E="h",_="minH",k="maxH",R="innerH",T="top",A="deltaH",B="contentH",O="left",M="w",D="x",L="innerW", +P="minW",H="right",I="deltaW",F="contentW"):(S="x",E="w",_="minW",k="maxW",R="innerW",T="left",A="deltaW",B="contentW",O="top",M="h",D="y",L="innerH",P="minH",H="bottom",I="deltaH",F="contentH"),d=i[R]-o[T]-o[T],N=u=0,t=0,n=r.length;n>t;t++)h=r[t],p=h.layoutRect(),m=h.settings,g=m.flex,d-=n-1>t?c:0,g>0&&(u+=g,p[k]&&v.push(h),p.flex=g),d-=p[_],y=o[O]+p[P]+o[H],y>N&&(N=y);if(x={},0>d?x[_]=i[_]-d+i[A]:x[_]=i[R]-d+i[A],x[P]=N+i[I],x[B]=i[R]-d,x[F]=N,x.minW=U(x.minW,i.maxW),x.minH=U(x.minH,i.maxH),x.minW=z(x.minW,i.startMinWidth),x.minH=z(x.minH,i.startMinHeight),!i.autoResize||x.minW==i.minW&&x.minH==i.minH){for(C=d/u,t=0,n=v.length;n>t;t++)h=v[t],p=h.layoutRect(),b=p[k],y=p[_]+p.flex*C,y>b?(d-=p[k]-p[_],u-=p.flex,p.flex=0,p.maxFlexSize=b):p.maxFlexSize=0;for(C=d/u,w=o[T],x={},0===u&&("end"==l?w=d+o[T]:"center"==l?(w=Math.round(i[R]/2-(i[R]-d)/2)+o[T],0>w&&(w=o[T])):"justify"==l&&(w=o[T],c=Math.floor(d/(r.length-1)))),x[D]=o[O],t=0,n=r.length;n>t;t++)h=r[t],p=h.layoutRect(),y=p.maxFlexSize||p[_],"center"===s?x[D]=Math.round(i[L]/2-p[M]/2):"stretch"===s?(x[M]=z(p[P]||0,i[L]-o[O]-o[H]),x[D]=o[O]):"end"===s&&(x[D]=i[L]-p[M]-o.top),p.flex>0&&(y+=p.flex*C),x[E]=y,x[S]=w,h.layoutRect(x),h.recalc&&h.recalc(),w+=y+c}else if(x.w=x.minW,x.h=x.minH,e.layoutRect(x),this.recalc(e),null===e._lastRect){var W=e.parent();W&&(W._lastRect=null,W.recalc())}}})}),r(Bt,[ft],function(e){return e.extend({Defaults:{containerClass:"flow-layout",controlClass:"flow-layout-item",endClass:"break"},recalc:function(e){e.items().filter(":visible").each(function(e){e.recalc&&e.recalc()})},isNative:function(){return!0}})}),r(Dt,[ye,De,ke,m,w,it,d],function(e,t,n,r,i,o,a){function s(e){e.settings.ui_container&&(a.container=i.DOM.select(e.settings.ui_container)[0])}function l(t){t.on("ScriptsLoaded",function(){t.rtl&&(e.rtl=!0)})}function c(e){function t(t,n){return function(){var r=this;e.on("nodeChange",function(i){var o=e.formatter,a=null;u(i.parents,function(e){return u(t,function(t){return n?o.matchNode(e,n,{value:t.value})&&(a=t.value):o.matchNode(e,t.value)&&(a=t.value),a?!1:void 0}),a?!1:void 0}),r.value(a)})}}function r(e){e=e.replace(/;$/,"").split(";");for(var t=e.length;t--;)e[t]=e[t].split("=");return e}function i(){function t(e){var n=[];if(e)return u(e,function(e){var o={text:e.title,icon:e.icon};if(e.items)o.menu=t(e.items);else{var a=e.format||"custom"+r++;e.format||(e.name=a,i.push(e)),o.format=a,o.cmd=e.cmd}n.push(o)}),n}function n(){var n;return n=t(e.settings.style_formats_merge?e.settings.style_formats?o.concat(e.settings.style_formats):o:e.settings.style_formats||o)}var r=0,i=[],o=[{title:"Headings",items:[{title:"Heading 1",format:"h1"},{title:"Heading 2",format:"h2"},{title:"Heading 3",format:"h3"},{title:"Heading 4",format:"h4"},{title:"Heading 5",format:"h5"},{title:"Heading 6",format:"h6"}]},{title:"Inline",items:[{title:"Bold",icon:"bold",format:"bold"},{title:"Italic",icon:"italic",format:"italic"},{title:"Underline",icon:"underline",format:"underline"},{title:"Strikethrough",icon:"strikethrough",format:"strikethrough"},{title:"Superscript",icon:"superscript",format:"superscript"},{title:"Subscript",icon:"subscript",format:"subscript"},{title:"Code",icon:"code",format:"code"}]},{title:"Blocks",items:[{title:"Paragraph",format:"p"},{title:"Blockquote",format:"blockquote"},{title:"Div",format:"div"},{title:"Pre",format:"pre"}]},{title:"Alignment",items:[{title:"Left",icon:"alignleft",format:"alignleft"},{title:"Center",icon:"aligncenter",format:"aligncenter"},{title:"Right",icon:"alignright",format:"alignright"},{title:"Justify",icon:"alignjustify",format:"alignjustify"}]}];return e.on("init",function(){u(i,function(t){e.formatter.register(t.name,t)})}),{type:"menu",items:n(),onPostRender:function(t){e.fire("renderFormatsMenu",{control:t.control})},itemDefaults:{preview:!0,textStyle:function(){return this.settings.format?e.formatter.getCssText(this.settings.format):void 0},onPostRender:function(){var t=this;t.parent().on("show",function(){var n,r;n=t.settings.format,n&&(t.disabled(!e.formatter.canApply(n)),t.active(e.formatter.match(n))),r=t.settings.cmd,r&&t.active(e.queryCommandState(r))})},onclick:function(){this.settings.format&&l(this.settings.format),this.settings.cmd&&e.execCommand(this.settings.cmd)}}}}function o(t){return function(){var n=this;e.formatter?e.formatter.formatChanged(t,function(e){n.active(e)}):e.on("init",function(){e.formatter.formatChanged(t,function(e){n.active(e)})})}}function a(t){return function(){function n(){return e.undoManager?e.undoManager[t]():!1}var r=this;t="redo"==t?"hasRedo":"hasUndo",r.disabled(!n()),e.on("Undo Redo AddUndo TypingUndo ClearUndos SwitchMode",function(){r.disabled(e.readonly||!n())})}}function s(){var t=this;e.on("VisualAid",function(e){t.active(e.hasVisual)}),t.active(e.hasVisual)}function l(t){t.control&&(t=t.control.value()),t&&e.execCommand("mceToggleFormat",!1,t)}var c;c=i(),u({bold:"Bold",italic:"Italic",underline:"Underline",strikethrough:"Strikethrough",subscript:"Subscript",superscript:"Superscript"},function(t,n){e.addButton(n,{tooltip:t,onPostRender:o(n),onclick:function(){l(n)}})}),u({outdent:["Decrease indent","Outdent"],indent:["Increase indent","Indent"],cut:["Cut","Cut"],copy:["Copy","Copy"],paste:["Paste","Paste"],help:["Help","mceHelp"],selectall:["Select all","SelectAll"],removeformat:["Clear formatting","RemoveFormat"],visualaid:["Visual aids","mceToggleVisualAid"],newdocument:["New document","mceNewDocument"]},function(t,n){e.addButton(n,{tooltip:t[0],cmd:t[1]})}),u({blockquote:["Blockquote","mceBlockQuote"],numlist:["Numbered list","InsertOrderedList"],bullist:["Bullet list","InsertUnorderedList"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],alignleft:["Align left","JustifyLeft"],aligncenter:["Align center","JustifyCenter"],alignright:["Align right","JustifyRight"],alignjustify:["Justify","JustifyFull"],alignnone:["No alignment","JustifyNone"]},function(t,n){e.addButton(n,{tooltip:t[0],cmd:t[1],onPostRender:o(n)})}),e.addButton("undo",{tooltip:"Undo",onPostRender:a("undo"),cmd:"undo"}),e.addButton("redo",{tooltip:"Redo",onPostRender:a("redo"),cmd:"redo"}),e.addMenuItem("newdocument",{text:"New document",icon:"newdocument",cmd:"mceNewDocument"}),e.addMenuItem("undo",{text:"Undo",icon:"undo",shortcut:"Meta+Z",onPostRender:a("undo"),cmd:"undo"}),e.addMenuItem("redo",{text:"Redo",icon:"redo",shortcut:"Meta+Y",onPostRender:a("redo"),cmd:"redo"}),e.addMenuItem("visualaid",{text:"Visual aids",selectable:!0,onPostRender:s,cmd:"mceToggleVisualAid"}),e.addButton("remove",{tooltip:"Remove",icon:"remove",cmd:"Delete"}),u({cut:["Cut","Cut","Meta+X"],copy:["Copy","Copy","Meta+C"],paste:["Paste","Paste","Meta+V"],selectall:["Select all","SelectAll","Meta+A"],bold:["Bold","Bold","Meta+B"],italic:["Italic","Italic","Meta+I"],underline:["Underline","Underline"],strikethrough:["Strikethrough","Strikethrough"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],removeformat:["Clear formatting","RemoveFormat"]},function(t,n){e.addMenuItem(n,{text:t[0],icon:n,shortcut:t[2],cmd:t[1]})}),e.on("mousedown",function(){n.hideAll()}),e.addButton("styleselect",{type:"menubutton",text:"Formats",menu:c}),e.addButton("formatselect",function(){var n=[],i=r(e.settings.block_formats||"Paragraph=p;Heading 1=h1;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6;Preformatted=pre");return u(i,function(t){n.push({text:t[0],value:t[1],textStyle:function(){return e.formatter.getCssText(t[1])}})}),{type:"listbox",text:i[0][0],values:n,fixedWidth:!0,onselect:l,onPostRender:t(n)}}),e.addButton("fontselect",function(){var n="Andale Mono=andale mono,monospace;Arial=arial,helvetica,sans-serif;Arial Black=arial black,sans-serif;Book Antiqua=book antiqua,palatino,serif;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier,monospace;Georgia=georgia,palatino,serif;Helvetica=helvetica,arial,sans-serif;Impact=impact,sans-serif;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco,monospace;Times New Roman=times new roman,times,serif;Trebuchet MS=trebuchet ms,geneva,sans-serif;Verdana=verdana,geneva,sans-serif;Webdings=webdings;Wingdings=wingdings,zapf dingbats",i=[],o=r(e.settings.font_formats||n);return u(o,function(e){i.push({text:{raw:e[0]},value:e[1],textStyle:-1==e[1].indexOf("dings")?"font-family:"+e[1]:""})}),{type:"listbox",text:"Font Family",tooltip:"Font Family",values:i,fixedWidth:!0,onPostRender:t(i,"fontname"),onselect:function(t){t.control.settings.value&&e.execCommand("FontName",!1,t.control.settings.value)}}}),e.addButton("fontsizeselect",function(){var n=[],r="8pt 10pt 12pt 14pt 18pt 24pt 36pt",i=e.settings.fontsize_formats||r;return u(i.split(" "),function(e){var t=e,r=e,i=e.split("=");i.length>1&&(t=i[0],r=i[1]),n.push({text:t,value:r})}),{type:"listbox",text:"Font Sizes",tooltip:"Font Sizes",values:n,fixedWidth:!0,onPostRender:t(n,"fontsize"),onclick:function(t){t.control.settings.value&&e.execCommand("FontSize",!1,t.control.settings.value)}}}),e.addMenuItem("formats",{text:"Formats",menu:c})}var u=r.each;o.on("AddEditor",function(e){var t=e.editor;l(t),c(t),s(t)}),e.translate=function(e){return o.translate(e)},t.tooltips=!a.iOS}),r(Lt,[ht],function(e){return e.extend({recalc:function(e){var t,n,r,i,o,a,s,l,c,u,d,f,h,p,m,g,v,y,b,C,x,w,N,E=[],_=[],S,k,T,R,A,B;t=e.settings,i=e.items().filter(":visible"),o=e.layoutRect(),r=t.columns||Math.ceil(Math.sqrt(i.length)),n=Math.ceil(i.length/r),y=t.spacingH||t.spacing||0,b=t.spacingV||t.spacing||0,C=t.alignH||t.align,x=t.alignV||t.align,g=e.paddingBox,A="reverseRows"in t?t.reverseRows:e.isRtl(),C&&"string"==typeof C&&(C=[C]),x&&"string"==typeof x&&(x=[x]);for(d=0;r>d;d++)E.push(0);for(f=0;n>f;f++)_.push(0);for(f=0;n>f;f++)for(d=0;r>d&&(u=i[f*r+d],u);d++)c=u.layoutRect(),S=c.minW,k=c.minH,E[d]=S>E[d]?S:E[d],_[f]=k>_[f]?k:_[f];for(T=o.innerW-g.left-g.right,w=0,d=0;r>d;d++)w+=E[d]+(d>0?y:0),T-=(d>0?y:0)+E[d];for(R=o.innerH-g.top-g.bottom,N=0,f=0;n>f;f++)N+=_[f]+(f>0?b:0),R-=(f>0?b:0)+_[f];if(w+=g.left+g.right,N+=g.top+g.bottom,l={},l.minW=w+(o.w-o.innerW),l.minH=N+(o.h-o.innerH),l.contentW=l.minW-o.deltaW,l.contentH=l.minH-o.deltaH,l.minW=Math.min(l.minW,o.maxW),l.minH=Math.min(l.minH,o.maxH),l.minW=Math.max(l.minW,o.startMinWidth),l.minH=Math.max(l.minH,o.startMinHeight),!o.autoResize||l.minW==o.minW&&l.minH==o.minH){o.autoResize&&(l=e.layoutRect(l),l.contentW=l.minW-o.deltaW,l.contentH=l.minH-o.deltaH);var D;D="start"==t.packV?0:R>0?Math.floor(R/n):0;var L=0,M=t.flexWidths;if(M)for(d=0;dd;d++)E[d]+=M?M[d]*P:P;for(p=g.top,f=0;n>f;f++){for(h=g.left,s=_[f]+D,d=0;r>d&&(B=A?f*r+r-1-d:f*r+d,u=i[B],u);d++)m=u.settings,c=u.layoutRect(),a=Math.max(E[d],c.startMinWidth),c.x=h,c.y=p,v=m.alignH||(C?C[d]||C[0]:null),"center"==v?c.x=h+a/2-c.w/2:"right"==v?c.x=h+a-c.w:"stretch"==v&&(c.w=a),v=m.alignV||(x?x[d]||x[0]:null),"center"==v?c.y=p+s/2-c.h/2:"bottom"==v?c.y=p+s-c.h:"stretch"==v&&(c.h=s),u.layoutRect(c),h+=a+y,u.recalc&&u.recalc();p+=s+b}}else if(l.w=l.minW,l.h=l.minH,e.layoutRect(l),this.recalc(e),null===e._lastRect){var O=e.parent();O&&(O._lastRect=null,O.recalc())}}})}),r(Mt,[De,u],function(e,t){return e.extend({renderHtml:function(){var e=this;return e.classes.add("iframe"),e.canFocus=!1,''},src:function(e){this.getEl().src=e},html:function(e,n){var r=this,i=this.getEl().contentWindow.document.body;return i?(i.innerHTML=e,n&&n()):t.setTimeout(function(){r.html(e)}),this}})}),r(Pt,[De],function(e){return e.extend({init:function(e){var t=this;t._super(e),t.classes.add("widget").add("infobox"),t.canFocus=!1},severity:function(e){this.classes.remove("error"),this.classes.remove("warning"),this.classes.remove("success"),this.classes.add(e)},help:function(e){this.state.set("help",e)},renderHtml:function(){var e=this,t=e.classPrefix;return'
    '+e.encode(e.state.get("text"))+'
    '},bindStates:function(){var e=this;return e.state.on("change:text",function(t){e.getEl("body").firstChild.data=e.encode(t.value),e.state.get("rendered")&&e.updateLayoutRect()}),e.state.on("change:help",function(t){e.classes.toggle("has-help",t.value),e.state.get("rendered")&&e.updateLayoutRect()}),e._super()}})}),r(Ot,[De,pe],function(e,t){return e.extend({init:function(e){var t=this;t._super(e),t.classes.add("widget").add("label"),t.canFocus=!1,e.multiline&&t.classes.add("autoscroll"),e.strong&&t.classes.add("strong")},initLayoutRect:function(){var e=this,n=e._super();if(e.settings.multiline){var r=t.getSize(e.getEl());r.width>n.maxW&&(n.minW=n.maxW,e.classes.add("multiline")),e.getEl().style.width=n.minW+"px",n.startMinH=n.h=n.minH=Math.min(n.maxH,t.getSize(e.getEl()).height)}return n},repaint:function(){var e=this;return e.settings.multiline||(e.getEl().style.lineHeight=e.layoutRect().h+"px"),e._super()},severity:function(e){this.classes.remove("error"),this.classes.remove("warning"),this.classes.remove("success"),this.classes.add(e)},renderHtml:function(){var e=this,t,n,r=e.settings.forId;return!r&&(n=e.settings.forName)&&(t=e.getRoot().find("#"+n)[0],t&&(r=t._id)),r?'":''+e.encode(e.state.get("text"))+""},bindStates:function(){var e=this;return e.state.on("change:text",function(t){e.innerHtml(e.encode(t.value)),e.state.get("rendered")&&e.updateLayoutRect()}),e._super()}})}),r(Ht,[xe],function(e){return e.extend({Defaults:{role:"toolbar",layout:"flow"},init:function(e){var t=this;t._super(e),t.classes.add("toolbar")},postRender:function(){var e=this;return e.items().each(function(e){e.classes.add("toolbar-item")}),e._super()}})}),r(It,[Ht],function(e){return e.extend({Defaults:{role:"menubar",containerCls:"menubar",ariaRoot:!0,defaults:{type:"menubutton"}}})}),r(Ft,[pt,be,It],function(e,t,n){function r(e,t){for(;e;){if(t===e)return!0;e=e.parentNode}return!1}var i=e.extend({init:function(e){var t=this;t._renderOpen=!0,t._super(e),e=t.settings,t.classes.add("menubtn"),e.fixedWidth&&t.classes.add("fixed-width"),t.aria("haspopup",!0),t.state.set("menu",e.menu||t.render())},showMenu:function(){var e=this,n;return e.menu&&e.menu.visible()?e.hideMenu():(e.menu||(n=e.state.get("menu")||[],n.length?n={type:"menu",items:n}:n.type=n.type||"menu",n.renderTo?e.menu=n.parent(e).show().renderTo():e.menu=t.create(n).parent(e).renderTo(),e.fire("createmenu"),e.menu.reflow(),e.menu.on("cancel",function(t){t.control.parent()===e.menu&&(t.stopPropagation(),e.focus(),e.hideMenu())}),e.menu.on("select",function(){e.focus()}),e.menu.on("show hide",function(t){t.control==e.menu&&e.activeMenu("show"==t.type),e.aria("expanded","show"==t.type)}).fire("show")),e.menu.show(),e.menu.layoutRect({w:e.layoutRect().w}),void e.menu.moveRel(e.getEl(),e.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"]))},hideMenu:function(){var e=this;e.menu&&(e.menu.items().each(function(e){e.hideMenu&&e.hideMenu()}),e.menu.hide())},activeMenu:function(e){this.classes.toggle("active",e)},renderHtml:function(){var e=this,t=e._id,r=e.classPrefix,i=e.settings.icon,o,a=e.state.get("text"),s="";return o=e.settings.image,o?(i="none","string"!=typeof o&&(o=window.getSelection?o[0]:o[1]),o=" style=\"background-image: url('"+o+"')\""):o="",a&&(e.classes.add("btn-has-text"),s=''+e.encode(a)+""),i=e.settings.icon?r+"ico "+r+"i-"+i:"",e.aria("role",e.parent()instanceof n?"menuitem":"button"),'
    '},postRender:function(){var e=this;return e.on("click",function(t){t.control===e&&r(t.target,e.getEl())&&(e.showMenu(),t.aria&&e.menu.items()[0].focus())}),e.on("mouseenter",function(t){var n=t.control,r=e.parent(),o;n&&r&&n instanceof i&&n.parent()==r&&(r.items().filter("MenuButton").each(function(e){e.hideMenu&&e!=n&&(e.menu&&e.menu.visible()&&(o=!0),e.hideMenu())}),o&&(n.focus(),n.showMenu()))}),e._super()},bindStates:function(){var e=this;return e.state.on("change:menu",function(){e.menu&&e.menu.remove(),e.menu=null}),e._super()},remove:function(){this._super(),this.menu&&this.menu.remove()}});return i}),r(zt,[De,be,d,u],function(e,t,n,r){return e.extend({Defaults:{border:0,role:"menuitem"},init:function(e){var t=this,n;t._super(e),e=t.settings,t.classes.add("menu-item"),e.menu&&t.classes.add("menu-item-expand"),e.preview&&t.classes.add("menu-item-preview"),n=t.state.get("text"),"-"!==n&&"|"!==n||(t.classes.add("menu-item-sep"),t.aria("role","separator"),t.state.set("text","-")),e.selectable&&(t.aria("role","menuitemcheckbox"),t.classes.add("menu-item-checkbox"),e.icon="selected"),e.preview||e.selectable||t.classes.add("menu-item-normal"),t.on("mousedown",function(e){e.preventDefault()}),e.menu&&!e.ariaHideMenu&&t.aria("haspopup",!0)},hasMenus:function(){return!!this.settings.menu},showMenu:function(){var e=this,n=e.settings,r,i=e.parent();if(i.items().each(function(t){t!==e&&t.hideMenu()}),n.menu){r=e.menu,r?r.show():(r=n.menu,r.length?r={type:"menu",items:r}:r.type=r.type||"menu",i.settings.itemDefaults&&(r.itemDefaults=i.settings.itemDefaults),r=e.menu=t.create(r).parent(e).renderTo(),r.reflow(),r.on("cancel",function(t){t.stopPropagation(),e.focus(),r.hide()}),r.on("show hide",function(e){e.control.items().each(function(e){e.active(e.settings.selected)})}).fire("show"),r.on("hide",function(t){t.control===r&&e.classes.remove("selected")}),r.submenu=!0),r._parentMenu=i,r.classes.add("menu-sub");var o=r.testMoveRel(e.getEl(),e.isRtl()?["tl-tr","bl-br","tr-tl","br-bl"]:["tr-tl","br-bl","tl-tr","bl-br"]);r.moveRel(e.getEl(),o),r.rel=o,o="menu-sub-"+o,r.classes.remove(r._lastRel).add(o),r._lastRel=o,e.classes.add("selected"),e.aria("expanded",!0)}},hideMenu:function(){var e=this;return e.menu&&(e.menu.items().each(function(e){e.hideMenu&&e.hideMenu()}),e.menu.hide(),e.aria("expanded",!1)),e},renderHtml:function(){function e(e){var t,r,i={};for(i=n.mac?{alt:"⌥",ctrl:"⌘",shift:"⇧",meta:"⌘"}:{meta:"Ctrl"},e=e.split("+"),t=0;t'+("-"!==a?'\xa0":"")+("-"!==a?''+a+"":"")+(c?'
    '+c+"
    ":"")+(i.menu?'
    ':"")+""},postRender:function(){var e=this,t=e.settings,n=t.textStyle;if("function"==typeof n&&(n=n.call(this)),n){var i=e.getEl("text");i&&i.setAttribute("style",n)}return e.on("mouseenter click",function(n){n.control===e&&(t.menu||"click"!==n.type?(e.showMenu(),n.aria&&e.menu.focus(!0)):(e.fire("select"),r.requestAnimationFrame(function(){e.parent().hideAll()})))}),e._super(),e},hover:function(){var e=this;return e.parent().items().each(function(e){e.classes.remove("selected")}),e.classes.toggle("selected",!0),e},active:function(e){return"undefined"!=typeof e&&this.aria("checked",e),this._super(e)},remove:function(){this._super(),this.menu&&this.menu.remove()}})}),r(Ut,[g,ye,u],function(e,t,n){return function(r,i){var o=this,a,s=t.classPrefix,l;o.show=function(t,c){function u(){a&&(e(r).append('
    '),c&&c())}return o.hide(),a=!0,t?l=n.setTimeout(u,t):u(),o},o.hide=function(){var e=r.lastChild;return n.clearTimeout(l),e&&-1!=e.className.indexOf("throbber")&&e.parentNode.removeChild(e),a=!1,o}}}),r(Wt,[ke,zt,Ut,m],function(e,t,n,r){return e.extend({Defaults:{defaultType:"menuitem",border:1,layout:"stack",role:"application",bodyRole:"menu",ariaRoot:!0},init:function(e){var t=this;if(e.autohide=!0,e.constrainToViewport=!0,"function"==typeof e.items&&(e.itemsFactory=e.items,e.items=[]),e.itemDefaults)for(var n=e.items,i=n.length;i--;)n[i]=r.extend({},e.itemDefaults,n[i]);t._super(e),t.classes.add("menu")},repaint:function(){return this.classes.toggle("menu-align",!0),this._super(),this.getEl().style.height="",this.getEl("body").style.height="",this},cancel:function(){var e=this;e.hideAll(),e.fire("select")},load:function(){function e(){t.throbber&&(t.throbber.hide(),t.throbber=null)}var t=this,r,i;i=t.settings.itemsFactory,i&&(t.throbber||(t.throbber=new n(t.getEl("body"),!0),0===t.items().length?(t.throbber.show(),t.fire("loading")):t.throbber.show(100,function(){t.items().remove(),t.fire("loading")}),t.on("hide close",e)),t.requestTime=r=(new Date).getTime(),t.settings.itemsFactory(function(n){return 0===n.length?void t.hide():void(t.requestTime===r&&(t.getEl().style.width="",t.getEl("body").style.width="",e(),t.items().remove(),t.getEl("body").innerHTML="",t.add(n),t.renderNew(),t.fire("loaded")))}))},hideAll:function(){var e=this;return this.find("menuitem").exec("hideMenu"),e._super()},preRender:function(){var e=this;return e.items().each(function(t){var n=t.settings;return n.icon||n.image||n.selectable?(e._hasIcons=!0,!1):void 0}),e.settings.itemsFactory&&e.on("postrender",function(){e.settings.itemsFactory&&e.load()}),e._super()}})}),r(Vt,[Ft,Wt],function(e,t){return e.extend({init:function(e){function t(r){for(var a=0;a0&&(o=r[0].text,n.state.set("value",r[0].value)),n.state.set("menu",r)),n.state.set("text",e.text||o),n.classes.add("listbox"),n.on("select",function(t){var r=t.control;a&&(t.lastControl=a),e.multiple?r.active(!r.active()):n.value(t.control.value()),a=r})},bindStates:function(){function e(e,n){e instanceof t&&e.items().each(function(e){e.hasMenus()||e.active(e.value()===n)})}function n(e,t){var r;if(e)for(var i=0;i'},postRender:function(){var e=this;e._super(),e.resizeDragHelper=new t(this._id,{start:function(){e.fire("ResizeStart")},drag:function(t){"both"!=e.settings.direction&&(t.deltaX=0),e.fire("Resize",t)},stop:function(){e.fire("ResizeEnd")}})},remove:function(){return this.resizeDragHelper&&this.resizeDragHelper.destroy(),this._super()}})}),r(jt,[De],function(e){function t(e){var t="";if(e)for(var n=0;n'+e[n]+"";return t}return e.extend({Defaults:{classes:"selectbox",role:"selectbox",options:[]},init:function(e){var t=this;t._super(e),t.settings.size&&(t.size=t.settings.size),t.settings.options&&(t._options=t.settings.options),t.on("keydown",function(e){var n;13==e.keyCode&&(e.preventDefault(),t.parents().reverse().each(function(e){return e.toJSON?(n=e,!1):void 0}),t.fire("submit",{data:n.toJSON()}))})},options:function(e){return arguments.length?(this.state.set("options",e),this):this.state.get("options")},renderHtml:function(){var e=this,n,r="";return n=t(e._options),e.size&&(r=' size = "'+e.size+'"'),'"},bindStates:function(){var e=this;return e.state.on("change:options",function(n){e.getEl().innerHTML=t(n.value)}),e._super()}})}),r(Yt,[De,we,pe],function(e,t,n){function r(e,t,n){return t>e&&(e=t),e>n&&(e=n),e}function i(e,t,n){e.setAttribute("aria-"+t,n)}function o(e,t){var r,o,a,s,l,c;"v"==e.settings.orientation?(s="top",a="height",o="h"):(s="left",a="width",o="w"),c=e.getEl("handle"),r=(e.layoutRect()[o]||100)-n.getSize(c)[a],l=r*((t-e._minValue)/(e._maxValue-e._minValue))+"px",c.style[s]=l,c.style.height=e.layoutRect().h+"px",i(c,"valuenow",t),i(c,"valuetext",""+e.settings.previewFilter(t)),i(c,"valuemin",e._minValue),i(c,"valuemax",e._maxValue)}return e.extend({init:function(e){var t=this;e.previewFilter||(e.previewFilter=function(e){return Math.round(100*e)/100}),t._super(e),t.classes.add("slider"),"v"==e.orientation&&t.classes.add("vertical"),t._minValue=e.minValue||0,t._maxValue=e.maxValue||100,t._initValue=t.state.get("value")},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix;return'
    '},reset:function(){this.value(this._initValue).repaint()},postRender:function(){function e(e,t,n){return(n+e)/(t-e)}function i(e,t,n){return n*(t-e)-e}function o(t,n){function o(o){var a;a=s.value(),a=i(t,n,e(t,n,a)+.05*o),a=r(a,t,n),s.value(a),s.fire("dragstart",{value:a}),s.fire("drag",{value:a}),s.fire("dragend",{value:a})}s.on("keydown",function(e){switch(e.keyCode){case 37:case 38:o(-1);break;case 39:case 40:o(1)}})}function a(e,i,o){var a,l,c,p,m;s._dragHelper=new t(s._id,{handle:s._id+"-handle",start:function(e){a=e[u],l=parseInt(s.getEl("handle").style[d],10),c=(s.layoutRect()[h]||100)-n.getSize(o)[f],s.fire("dragstart",{value:m})},drag:function(t){var n=t[u]-a;p=r(l+n,0,c),o.style[d]=p+"px",m=e+p/c*(i-e),s.value(m),s.tooltip().text(""+s.settings.previewFilter(m)).show().moveRel(o,"bc tc"),s.fire("drag",{value:m})},stop:function(){s.tooltip().hide(),s.fire("dragend",{value:m})}})}var s=this,l,c,u,d,f,h;l=s._minValue,c=s._maxValue,"v"==s.settings.orientation?(u="screenY",d="top",f="height",h="h"):(u="screenX",d="left",f="width",h="w"),s._super(),o(l,c,s.getEl("handle")),a(l,c,s.getEl("handle"))},repaint:function(){this._super(),o(this,this.value())},bindStates:function(){var e=this;return e.state.on("change:value",function(t){o(e,t.value)}),e._super()}})}),r(Xt,[De],function(e){return e.extend({renderHtml:function(){var e=this;return e.classes.add("spacer"),e.canFocus=!1,'
    '}})}),r(Kt,[Ft,pe,g],function(e,t,n){return e.extend({Defaults:{classes:"widget btn splitbtn",role:"button"},repaint:function(){var e=this,r=e.getEl(),i=e.layoutRect(),o,a;return e._super(),o=r.firstChild,a=r.lastChild,n(o).css({width:i.w-t.getSize(a).width,height:i.h-2}),n(a).css({height:i.h-2}),e},activeMenu:function(e){var t=this;n(t.getEl().lastChild).toggleClass(t.classPrefix+"active",e)},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r,i=e.state.get("icon"),o=e.state.get("text"),a="";return r=e.settings.image,r?(i="none","string"!=typeof r&&(r=window.getSelection?r[0]:r[1]),r=" style=\"background-image: url('"+r+"')\""):r="",i=e.settings.icon?n+"ico "+n+"i-"+i:"",o&&(e.classes.add("btn-has-text"),a=''+e.encode(o)+""),'
    '},postRender:function(){var e=this,t=e.settings.onclick;return e.on("click",function(e){var n=e.target;if(e.control==this)for(;n;){if(e.aria&&"down"!=e.aria.key||"BUTTON"==n.nodeName&&-1==n.className.indexOf("open"))return e.stopImmediatePropagation(),void(t&&t.call(this,e));n=n.parentNode}}),delete e.settings.onclick,e._super()}})}),r(Gt,[Bt],function(e){return e.extend({Defaults:{containerClass:"stack-layout",controlClass:"stack-layout-item",endClass:"break"},isNative:function(){return!0}})}),r(Jt,[Ee,g,pe],function(e,t,n){return e.extend({Defaults:{layout:"absolute",defaults:{type:"panel"}},activateTab:function(e){var n;this.activeTabId&&(n=this.getEl(this.activeTabId),t(n).removeClass(this.classPrefix+"active"),n.setAttribute("aria-selected","false")),this.activeTabId="t"+e,n=this.getEl("t"+e),n.setAttribute("aria-selected","true"),t(n).addClass(this.classPrefix+"active"),this.items()[e].show().fire("showtab"),this.reflow(),this.items().each(function(t,n){e!=n&&t.hide()})},renderHtml:function(){var e=this,t=e._layout,n="",r=e.classPrefix;return e.preRender(),t.preRender(e),e.items().each(function(t,i){var o=e._id+"-t"+i;t.aria("role","tabpanel"),t.aria("labelledby",o),n+='"}),'
    '+n+'
    '+t.renderHtml(e)+"
    "},postRender:function(){var e=this;e._super(),e.settings.activeTab=e.settings.activeTab||0,e.activateTab(e.settings.activeTab),this.on("click",function(t){var n=t.target.parentNode;if(t.target.parentNode.id==e._id+"-head")for(var r=n.childNodes.length;r--;)n.childNodes[r]==t.target&&e.activateTab(r)})},initLayoutRect:function(){var e=this,t,r,i;r=n.getSize(e.getEl("head")).width,r=0>r?0:r,i=0,e.items().each(function(e){r=Math.max(r,e.layoutRect().minW),i=Math.max(i,e.layoutRect().minH)}),e.items().each(function(e){e.settings.x=0,e.settings.y=0,e.settings.w=r,e.settings.h=i,e.layoutRect({x:0,y:0,w:r,h:i})});var o=n.getSize(e.getEl("head")).height;return e.settings.minWidth=r,e.settings.minHeight=i+o,t=e._super(),t.deltaH+=o,t.innerH=t.h-t.deltaH,t}})}),r(Qt,[De,m,pe],function(e,t,n){return e.extend({init:function(e){var t=this;t._super(e),t.classes.add("textbox"),e.multiline?t.classes.add("multiline"):(t.on("keydown",function(e){var n;13==e.keyCode&&(e.preventDefault(),t.parents().reverse().each(function(e){return e.toJSON?(n=e,!1):void 0}),t.fire("submit",{data:n.toJSON()}))}),t.on("keyup",function(e){t.state.set("value",e.target.value)}))},repaint:function(){var e=this,t,n,r,i,o=0,a;t=e.getEl().style,n=e._layoutRect,a=e._lastRepaintRect||{};var s=document;return!e.settings.multiline&&s.all&&(!s.documentMode||s.documentMode<=8)&&(t.lineHeight=n.h-o+"px"),r=e.borderBox,i=r.left+r.right+8,o=r.top+r.bottom+(e.settings.multiline?8:0),n.x!==a.x&&(t.left=n.x+"px",a.x=n.x),n.y!==a.y&&(t.top=n.y+"px",a.y=n.y),n.w!==a.w&&(t.width=n.w-i+"px",a.w=n.w),n.h!==a.h&&(t.height=n.h-o+"px",a.h=n.h),e._lastRepaintRect=a,e.fire("repaint",{},!1),e},renderHtml:function(){var e=this,r=e.settings,i,o;return i={id:e._id,hidefocus:"1"},t.each(["rows","spellcheck","maxLength","size","readonly","min","max","step","list","pattern","placeholder","required","multiple"],function(e){i[e]=r[e]}),e.disabled()&&(i.disabled="disabled"),r.subtype&&(i.type=r.subtype),o=n.create(r.multiline?"textarea":"input",i),o.value=e.state.get("value"),o.className=e.classes,o.outerHTML},value:function(e){return arguments.length?(this.state.set("value",e),this):(this.state.get("rendered")&&this.state.set("value",this.getEl().value),this.state.get("value"))},postRender:function(){var e=this;e.getEl().value=e.state.get("value"),e._super(),e.$el.on("change",function(t){ +e.state.set("value",t.target.value),e.fire("change",t)})},bindStates:function(){var e=this;return e.state.on("change:value",function(t){e.getEl().value!=t.value&&(e.getEl().value=t.value)}),e.state.on("change:disabled",function(t){e.getEl().disabled=t.value}),e._super()},remove:function(){this.$el.off(),this._super()}})}),r(Zt,[],function(){var e=this||window,t=function(){return e.tinymce};return"function"==typeof e.define&&(e.define.amd||e.define("ephox/tinymce",[],t)),{}}),a([l,c,u,d,f,h,m,g,v,y,C,w,N,E,T,A,B,D,L,M,P,O,I,F,j,Y,J,Q,oe,ae,se,le,ue,fe,he,ve,ye,be,Ce,xe,we,Ne,Ee,_e,Se,ke,Te,Re,Ae,Be,De,Le,Me,Pe,Ie,ze,tt,nt,rt,it,at,st,lt,ct,ut,dt,ft,ht,pt,mt,gt,vt,yt,bt,Ct,xt,wt,Nt,Et,_t,St,kt,Tt,Rt,At,Bt,Dt,Lt,Mt,Pt,Ot,Ht,It,Ft,zt,Ut,Wt,Vt,$t,qt,jt,Yt,Xt,Kt,Gt,Jt,Qt])}(this); +!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;g>i;++i)h[i]=d(e[i]);var j=f.apply(null,h);if(void 0===j)throw"module ["+b+"] returned undefined";c.instance=j},c=function(b,c,d){if("string"!=typeof b)throw"module id must be a string";if(void 0===c)throw"no dependencies for "+b;if(void 0===d)throw"no definition function for "+b;a[b]={deps:c,defn:d,instance:void 0}},d=function(c){var d=a[c];if(void 0===d)throw"module ["+c+"] was undefined";return void 0===d.instance&&b(c),d.instance},e=function(a,b){for(var c=a.length,e=new Array(c),f=0;c>f;++f)e.push(d(a[f]));b.apply(null,b)},f={};f.bolt={module:{api:{define:c,require:e,demand:d}}};var g=c,h=function(a,b){g(a,[],function(){return b})};h("1",tinymce.ThemeManager),h("2",tinymce.util.Delay),h("c",tinymce.util.Tools),h("d",tinymce.ui.Factory),h("e",tinymce.DOM),g("j",[],function(){var a=function(a){return function(b){return typeof b===a}},b=function(a){return Array.isArray(a)},c=function(a){return null===a},d=function(a){return function(d){return!c(d)&&!b(d)&&a(d)}};return{isString:a("string"),isNumber:a("number"),isBoolean:a("boolean"),isFunction:a("function"),isObject:d(a("object")),isNull:c,isArray:b}}),g("f",["c","d","j"],function(a,b,c){var d=function(a,b){return function(c,d){for(var e,f=d.parents.length;f--&&(e=d.parents[f].nodeName,"OL"!=e&&"UL"!=e););a.active(c&&e==b)}},e=function(a,b){var c=function(a,b){return{selector:a,handler:b}},e=function(a){b.active(a)},f=function(a){b.disabled(a)};return"bullist"==a?c("ul > li",d(b,"UL")):"numlist"==a?c("ol > li",d(b,"OL")):b.settings.stateSelector?c(b.settings.stateSelector,e):b.settings.disabledStateSelector?c(b.settings.disabledStateSelector,f):null},f=function(a,b,c){return function(){var d=e(b,c);null!==d&&a.selection.selectorChanged(d.selector,d.handler)}},g=function(a){return c.isArray(a)?a:c.isString(a)?a.split(/[ ,]/):[]},h=function(c,d,e){var h,i=[];if(e)return a.each(g(e),function(a){var d;"|"==a?h=null:b.has(a)?(a={type:a},i.push(a),h=null):c.buttons[a]&&(h||(h={type:"buttongroup",items:[]},i.push(h)),d=a,a=c.buttons[d],"function"==typeof a&&(a=a()),a.type=a.type||"button",a=b.create(a),a.on("postRender",f(c,d,a)),h.items.push(a))}),b.create({type:"toolbar",layout:"flow",name:d,items:i})};return{create:h}}),h("o",tinymce.util.Promise),g("p",[],function(){var a=0,b=function(){var a=function(){return Math.round(4294967295*Math.random()).toString(36)};return"s"+Date.now().toString(36)+a()+a()+a()},c=function(c){return c+a++ +b()};return{uuid:c}}),g("u",[],function(){var a=function(a,b){function c(c){var e,f,g;f=b[c?"startContainer":"endContainer"],g=b[c?"startOffset":"endOffset"],1==f.nodeType&&(e=a.create("span",{"data-mce-type":"bookmark"}),f.hasChildNodes()?(g=Math.min(g,f.childNodes.length-1),c?f.insertBefore(e,f.childNodes[g]):a.insertAfter(e,f.childNodes[g])):f.appendChild(e),f=e,g=0),d[c?"startContainer":"endContainer"]=f,d[c?"startOffset":"endOffset"]=g}var d={};return c(!0),b.collapsed||c(),d},b=function(a,b){function c(c){function d(a){for(var b=a.parentNode.firstChild,c=0;b;){if(b==a)return c;1==b.nodeType&&"bookmark"==b.getAttribute("data-mce-type")||c++,b=b.nextSibling}return-1}var e,f,g;e=g=b[c?"startContainer":"endContainer"],f=b[c?"startOffset":"endOffset"],e&&(1==e.nodeType&&(f=d(e),e=e.parentNode,a.remove(g)),b[c?"startContainer":"endContainer"]=e,b[c?"startOffset":"endOffset"]=f)}c(!0),c();var d=a.createRng();return d.setStart(b.startContainer,b.startOffset),b.endContainer&&d.setEnd(b.endContainer,b.endOffset),d};return{create:a,resolve:b}}),h("v",tinymce.dom.TreeWalker),h("w",tinymce.dom.RangeUtils),g("q",["u","c","v","w"],function(a,b,c,d){var e=function(a,b,d){var e,f,g=[];for(e=new c(b,a),f=b;f&&(1===f.nodeType&&g.push(f),f!==d);f=e.next());return g},f=function(c,d){var e,f,g;f=c.dom,g=c.selection,e=a.create(f,g.getRng()),b.each(d,function(a){c.dom.remove(a,!0)}),g.setRng(a.resolve(f,e))},g=function(a){return"A"===a.nodeName&&a.hasAttribute("href")},h=function(a,b){var c=a.getParent(b,g);return c?c:b},i=function(a){var c,f,i,j,k,l,m;return k=a.selection,l=a.dom,m=k.getRng(),c=h(l,d.getNode(m.startContainer,m.startOffset)),f=d.getNode(m.endContainer,m.endOffset),i=a.getBody(),j=b.grep(e(i,c,f),g)},j=function(a){f(a,i(a))};return{unlinkSelection:j}}),g("m",["p","q"],function(a,b){var c=function(a,b){var c,d,e;for(e='
    ").append(a("").attr("data-action","today").append(a("").addClass(d.icons.today)))),!d.sideBySide&&z()&&y()&&b.push(a("").append(a("").attr("data-action","togglePicker").append(a("").addClass(d.icons.time)))),d.showClear&&b.push(a("").append(a("").attr("data-action","clear").append(a("").addClass(d.icons.clear)))),d.showClose&&b.push(a("").append(a("").attr("data-action","close").append(a("").addClass(d.icons.close)))),a("").addClass("table-condensed").append(a("").append(a("").append(b)))},E=function(){var b=a("
    ").addClass("bootstrap-datetimepicker-widget dropdown-menu"),c=a("
    ").addClass("datepicker").append(A()),e=a("
    ").addClass("timepicker").append(C()),g=a("
      ").addClass("list-unstyled"),h=a("
    • ").addClass("picker-switch"+(d.collapse?" accordion-toggle":"")).append(D());return d.inline&&b.removeClass("dropdown-menu"),f&&b.addClass("usetwentyfour"),d.sideBySide&&z()&&y()?(b.addClass("timepicker-sbs"),b.append(a("
      ").addClass("row").append(c.addClass("col-sm-6")).append(e.addClass("col-sm-6"))),b.append(h),b):("top"===d.toolbarPlacement&&g.append(h),z()&&g.append(a("
    • ").addClass(d.collapse&&y()?"collapse in":"").append(c)),"default"===d.toolbarPlacement&&g.append(h),y()&&g.append(a("
    • ").addClass(d.collapse&&z()?"collapse":"").append(e)),"bottom"===d.toolbarPlacement&&g.append(h),b.append(g))},F=function(){var b,e={};return b=c.is("input")||d.inline?c.data():c.find("input").data(),b.dateOptions&&b.dateOptions instanceof Object&&(e=a.extend(!0,e,b.dateOptions)),a.each(d,function(a){var c="date"+a.charAt(0).toUpperCase()+a.slice(1);void 0!==b[c]&&(e[a]=b[c])}),e},G=function(){var b,e=(n||c).position(),f=(n||c).offset(),g=d.widgetPositioning.vertical,h=d.widgetPositioning.horizontal;if(d.widgetParent)b=d.widgetParent.append(o);else if(c.is("input"))b=c.parent().append(o);else{if(d.inline)return void(b=c.append(o));b=c,c.children().first().after(o)}if("auto"===g&&(g=f.top+1.5*o.height()>=a(window).height()+a(window).scrollTop()&&o.height()+c.outerHeight()a(window).width()?"right":"left"),"top"===g?o.addClass("top").removeClass("bottom"):o.addClass("bottom").removeClass("top"),"right"===h?o.addClass("pull-right"):o.removeClass("pull-right"),"relative"!==b.css("position")&&(b=b.parents().filter(function(){return"relative"===a(this).css("position")}).first()),0===b.length)throw new Error("datetimepicker component should be placed within a relative positioned container");o.css({top:"top"===g?"auto":e.top+c.outerHeight(),bottom:"top"===g?e.top+c.outerHeight():"auto",left:"left"===h?b.css("padding-left"):"auto",right:"left"===h?"auto":b.width()-c.outerWidth()})},H=function(a){"dp.change"===a.type&&(a.date&&a.date.isSame(a.oldDate)||!a.date&&!a.oldDate)||c.trigger(a)},I=function(a){o&&(a&&(i=Math.max(p,Math.min(2,i+a))),o.find(".datepicker > div").hide().filter(".datepicker-"+q[i].clsName).show())},J=function(){var b=a("
    "),c=l.clone().startOf("w");for(d.calendarWeeks===!0&&b.append(a(""),d.calendarWeeks&&e.append('"),i.push(e)),f="",c.isBefore(l,"M")&&(f+=" old"),c.isAfter(l,"M")&&(f+=" new"),c.isSame(k,"d")&&!m&&(f+=" active"),M(c,"d")||(f+=" disabled"),c.isSame(b(),"d")&&(f+=" today"),(0===c.day()||6===c.day())&&(f+=" weekend"),e.append('"),c.add(1,"d");g.find("tbody").empty().append(i),O(),P()}},R=function(){var b=o.find(".timepicker-hours table"),c=l.clone().startOf("d"),d=[],e=a("");for(l.hour()>11&&!f&&c.hour(12);c.isSame(l,"d")&&(f||l.hour()<12&&c.hour()<12||l.hour()>11);)c.hour()%4===0&&(e=a(""),d.push(e)),e.append('"),c.add(1,"h");b.empty().append(d)},S=function(){for(var b=o.find(".timepicker-minutes table"),c=l.clone().startOf("h"),e=[],f=a(""),g=1===d.stepping?5:d.stepping;l.isSame(c,"h");)c.minute()%(4*g)===0&&(f=a(""),e.push(f)),f.append('"),c.add(g,"m");b.empty().append(e)},T=function(){for(var b=o.find(".timepicker-seconds table"),c=l.clone().startOf("m"),d=[],e=a("");l.isSame(c,"m");)c.second()%20===0&&(e=a(""),d.push(e)),e.append('"),c.add(5,"s");b.empty().append(d)},U=function(){var a=o.find(".timepicker span[data-time-component]");f||o.find(".timepicker [data-action=togglePeriod]").text(k.format("A")),a.filter("[data-time-component=hours]").text(k.format(f?"HH":"hh")),a.filter("[data-time-component=minutes]").text(k.format("mm")),a.filter("[data-time-component=seconds]").text(k.format("ss")),R(),S(),T()},V=function(){o&&(Q(),U())},W=function(a){var b=m?null:k;return a?(a=a.clone().locale(d.locale),1!==d.stepping&&a.minutes(Math.round(a.minutes()/d.stepping)*d.stepping%60).seconds(0),void(M(a)?(k=a,l=k.clone(),e.val(k.format(g)),c.data("date",k.format(g)),V(),m=!1,H({type:"dp.change",date:k.clone(),oldDate:b})):(d.keepInvalid||e.val(m?"":k.format(g)),H({type:"dp.error",date:a})))):(m=!0,e.val(""),c.data("date",""),H({type:"dp.change",date:null,oldDate:b}),void V())},X=function(){var b=!1;return o?(o.find(".collapse").each(function(){var c=a(this).data("collapse");return c&&c.transitioning?(b=!0,!1):!0}),b?j:(n&&n.hasClass("btn")&&n.toggleClass("active"),o.hide(),a(window).off("resize",G),o.off("click","[data-action]"),o.off("mousedown",!1),o.remove(),o=!1,H({type:"dp.hide",date:k.clone()}),j)):j},Y=function(){W(null)},Z={next:function(){l.add(q[i].navStep,q[i].navFnc),Q()},previous:function(){l.subtract(q[i].navStep,q[i].navFnc),Q()},pickerSwitch:function(){I(1)},selectMonth:function(b){var c=a(b.target).closest("tbody").find("span").index(a(b.target));l.month(c),i===p?(W(k.clone().year(l.year()).month(l.month())),d.inline||X()):(I(-1),Q())},selectYear:function(b){var c=parseInt(a(b.target).text(),10)||0;l.year(c),i===p?(W(k.clone().year(l.year())),d.inline||X()):(I(-1),Q())},selectDay:function(b){var c=l.clone();a(b.target).is(".old")&&c.subtract(1,"M"),a(b.target).is(".new")&&c.add(1,"M"),W(c.date(parseInt(a(b.target).text(),10))),y()||d.keepOpen||d.inline||X()},incrementHours:function(){W(k.clone().add(1,"h"))},incrementMinutes:function(){W(k.clone().add(d.stepping,"m"))},incrementSeconds:function(){W(k.clone().add(1,"s"))},decrementHours:function(){W(k.clone().subtract(1,"h"))},decrementMinutes:function(){W(k.clone().subtract(d.stepping,"m"))},decrementSeconds:function(){W(k.clone().subtract(1,"s"))},togglePeriod:function(){W(k.clone().add(k.hours()>=12?-12:12,"h"))},togglePicker:function(b){var c,e=a(b.target),f=e.closest("ul"),g=f.find(".in"),h=f.find(".collapse:not(.in)");if(g&&g.length){if(c=g.data("collapse"),c&&c.transitioning)return;g.collapse?(g.collapse("hide"),h.collapse("show")):(g.removeClass("in"),h.addClass("in")),e.is("span")?e.toggleClass(d.icons.time+" "+d.icons.date):e.find("span").toggleClass(d.icons.time+" "+d.icons.date)}},showPicker:function(){o.find(".timepicker > div:not(.timepicker-picker)").hide(),o.find(".timepicker .timepicker-picker").show()},showHours:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-hours").show()},showMinutes:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-minutes").show()},showSeconds:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-seconds").show()},selectHour:function(b){var c=parseInt(a(b.target).text(),10);f||(k.hours()>=12?12!==c&&(c+=12):12===c&&(c=0)),W(k.clone().hours(c)),Z.showPicker.call(j)},selectMinute:function(b){W(k.clone().minutes(parseInt(a(b.target).text(),10))),Z.showPicker.call(j)},selectSecond:function(b){W(k.clone().seconds(parseInt(a(b.target).text(),10))),Z.showPicker.call(j)},clear:Y,today:function(){W(b())},close:X},$=function(b){return a(b.currentTarget).is(".disabled")?!1:(Z[a(b.currentTarget).data("action")].apply(j,arguments),!1)},_=function(){var c,f={year:function(a){return a.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(a){return a.date(1).hours(0).seconds(0).minutes(0)},day:function(a){return a.hours(0).seconds(0).minutes(0)},hour:function(a){return a.seconds(0).minutes(0)},minute:function(a){return a.seconds(0)}};return e.prop("disabled")||!d.ignoreReadonly&&e.prop("readonly")||o?j:(d.useCurrent&&m&&(e.is("input")&&0===e.val().trim().length||d.inline)&&(c=b(),"string"==typeof d.useCurrent&&(c=f[d.useCurrent](c)),W(c)),o=E(),J(),N(),o.find(".timepicker-hours").hide(),o.find(".timepicker-minutes").hide(),o.find(".timepicker-seconds").hide(),V(),I(),a(window).on("resize",G),o.on("click","[data-action]",$),o.on("mousedown",!1),n&&n.hasClass("btn")&&n.toggleClass("active"),o.show(),G(),e.is(":focus")||e.focus(),H({type:"dp.show"}),j)},ab=function(){return o?X():_()},bb=function(a){return a=b.isMoment(a)||a instanceof Date?b(a):b(a,h,d.useStrict),a.locale(d.locale),a},cb=function(a){var b,c,e,f,g=null,h=[],i={},k=a.which,l="p";w[k]=l;for(b in w)w.hasOwnProperty(b)&&w[b]===l&&(h.push(b),parseInt(b,10)!==k&&(i[b]=!0));for(b in d.keyBinds)if(d.keyBinds.hasOwnProperty(b)&&"function"==typeof d.keyBinds[b]&&(e=b.split(" "),e.length===h.length&&v[k]===e[e.length-1])){for(f=!0,c=e.length-2;c>=0;c--)if(!(v[e[c]]in i)){f=!1;break}if(f){g=d.keyBinds[b];break}}g&&(g.call(j,o),a.stopPropagation(),a.preventDefault())},db=function(a){w[a.which]="r",a.stopPropagation(),a.preventDefault()},eb=function(b){var c=a(b.target).val().trim(),d=c?bb(c):null;return W(d),b.stopImmediatePropagation(),!1},fb=function(){e.on({change:eb,blur:d.debug?"":X,keydown:cb,keyup:db}),c.is("input")?e.on({focus:_}):n&&(n.on("click",ab),n.on("mousedown",!1))},gb=function(){e.off({change:eb,blur:X,keydown:cb,keyup:db}),c.is("input")?e.off({focus:_}):n&&(n.off("click",ab),n.off("mousedown",!1))},hb=function(b){var c={};return a.each(b,function(){var a=bb(this);a.isValid()&&(c[a.format("YYYY-MM-DD")]=!0)}),Object.keys(c).length?c:!1},ib=function(){var a=d.format||"L LT";g=a.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){var b=k.localeData().longDateFormat(a)||a;return b.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){return k.localeData().longDateFormat(a)||a})}),h=d.extraFormats?d.extraFormats.slice():[],h.indexOf(a)<0&&h.indexOf(g)<0&&h.push(g),f=g.toLowerCase().indexOf("a")<1&&g.indexOf("h")<1,x("y")&&(p=2),x("M")&&(p=1),x("d")&&(p=0),i=Math.max(p,i),m||W(k)};if(j.destroy=function(){X(),gb(),c.removeData("DateTimePicker"),c.removeData("date")},j.toggle=ab,j.show=_,j.hide=X,j.disable=function(){return X(),n&&n.hasClass("btn")&&n.addClass("disabled"),e.prop("disabled",!0),j},j.enable=function(){return n&&n.hasClass("btn")&&n.removeClass("disabled"),e.prop("disabled",!1),j},j.ignoreReadonly=function(a){if(0===arguments.length)return d.ignoreReadonly;if("boolean"!=typeof a)throw new TypeError("ignoreReadonly () expects a boolean parameter");return d.ignoreReadonly=a,j},j.options=function(b){if(0===arguments.length)return a.extend(!0,{},d);if(!(b instanceof Object))throw new TypeError("options() options parameter should be an object");return a.extend(!0,d,b),a.each(d,function(a,b){if(void 0===j[a])throw new TypeError("option "+a+" is not recognized!");j[a](b)}),j},j.date=function(a){if(0===arguments.length)return m?null:k.clone();if(!(null===a||"string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");return W(null===a?null:bb(a)),j},j.format=function(a){if(0===arguments.length)return d.format;if("string"!=typeof a&&("boolean"!=typeof a||a!==!1))throw new TypeError("format() expects a sting or boolean:false parameter "+a);return d.format=a,g&&ib(),j},j.dayViewHeaderFormat=function(a){if(0===arguments.length)return d.dayViewHeaderFormat;if("string"!=typeof a)throw new TypeError("dayViewHeaderFormat() expects a string parameter");return d.dayViewHeaderFormat=a,j},j.extraFormats=function(a){if(0===arguments.length)return d.extraFormats;if(a!==!1&&!(a instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");return d.extraFormats=a,h&&ib(),j},j.disabledDates=function(b){if(0===arguments.length)return d.disabledDates?a.extend({},d.disabledDates):d.disabledDates;if(!b)return d.disabledDates=!1,V(),j;if(!(b instanceof Array))throw new TypeError("disabledDates() expects an array parameter");return d.disabledDates=hb(b),d.enabledDates=!1,V(),j},j.enabledDates=function(b){if(0===arguments.length)return d.enabledDates?a.extend({},d.enabledDates):d.enabledDates;if(!b)return d.enabledDates=!1,V(),j;if(!(b instanceof Array))throw new TypeError("enabledDates() expects an array parameter");return d.enabledDates=hb(b),d.disabledDates=!1,V(),j},j.daysOfWeekDisabled=function(a){if(0===arguments.length)return d.daysOfWeekDisabled.splice(0);if(!(a instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");return d.daysOfWeekDisabled=a.reduce(function(a,b){return b=parseInt(b,10),b>6||0>b||isNaN(b)?a:(-1===a.indexOf(b)&&a.push(b),a)},[]).sort(),V(),j},j.maxDate=function(a){if(0===arguments.length)return d.maxDate?d.maxDate.clone():d.maxDate;if("boolean"==typeof a&&a===!1)return d.maxDate=!1,V(),j;"string"==typeof a&&("now"===a||"moment"===a)&&(a=b());var c=bb(a);if(!c.isValid())throw new TypeError("maxDate() Could not parse date parameter: "+a);if(d.minDate&&c.isBefore(d.minDate))throw new TypeError("maxDate() date parameter is before options.minDate: "+c.format(g));return d.maxDate=c,d.maxDate.isBefore(a)&&W(d.maxDate),l.isAfter(c)&&(l=c.clone()),V(),j},j.minDate=function(a){if(0===arguments.length)return d.minDate?d.minDate.clone():d.minDate;if("boolean"==typeof a&&a===!1)return d.minDate=!1,V(),j;"string"==typeof a&&("now"===a||"moment"===a)&&(a=b());var c=bb(a);if(!c.isValid())throw new TypeError("minDate() Could not parse date parameter: "+a);if(d.maxDate&&c.isAfter(d.maxDate))throw new TypeError("minDate() date parameter is after options.maxDate: "+c.format(g));return d.minDate=c,d.minDate.isAfter(a)&&W(d.minDate),l.isBefore(c)&&(l=c.clone()),V(),j},j.defaultDate=function(a){if(0===arguments.length)return d.defaultDate?d.defaultDate.clone():d.defaultDate;if(!a)return d.defaultDate=!1,j;"string"==typeof a&&("now"===a||"moment"===a)&&(a=b());var c=bb(a);if(!c.isValid())throw new TypeError("defaultDate() Could not parse date parameter: "+a);if(!M(c))throw new TypeError("defaultDate() date passed is invalid according to component setup validations");return d.defaultDate=c,d.defaultDate&&""===e.val().trim()&&void 0===e.attr("placeholder")&&W(d.defaultDate),j},j.locale=function(a){if(0===arguments.length)return d.locale;if(!b.localeData(a))throw new TypeError("locale() locale "+a+" is not loaded from moment locales!");return d.locale=a,k.locale(d.locale),l.locale(d.locale),g&&ib(),o&&(X(),_()),j},j.stepping=function(a){return 0===arguments.length?d.stepping:(a=parseInt(a,10),(isNaN(a)||1>a)&&(a=1),d.stepping=a,j)},j.useCurrent=function(a){var b=["year","month","day","hour","minute"];if(0===arguments.length)return d.useCurrent;if("boolean"!=typeof a&&"string"!=typeof a)throw new TypeError("useCurrent() expects a boolean or string parameter");if("string"==typeof a&&-1===b.indexOf(a.toLowerCase()))throw new TypeError("useCurrent() expects a string parameter of "+b.join(", "));return d.useCurrent=a,j},j.collapse=function(a){if(0===arguments.length)return d.collapse;if("boolean"!=typeof a)throw new TypeError("collapse() expects a boolean parameter");return d.collapse===a?j:(d.collapse=a,o&&(X(),_()),j)},j.icons=function(b){if(0===arguments.length)return a.extend({},d.icons);if(!(b instanceof Object))throw new TypeError("icons() expects parameter to be an Object");return a.extend(d.icons,b),o&&(X(),_()),j},j.useStrict=function(a){if(0===arguments.length)return d.useStrict;if("boolean"!=typeof a)throw new TypeError("useStrict() expects a boolean parameter");return d.useStrict=a,j},j.sideBySide=function(a){if(0===arguments.length)return d.sideBySide;if("boolean"!=typeof a)throw new TypeError("sideBySide() expects a boolean parameter");return d.sideBySide=a,o&&(X(),_()),j},j.viewMode=function(a){if(0===arguments.length)return d.viewMode;if("string"!=typeof a)throw new TypeError("viewMode() expects a string parameter");if(-1===r.indexOf(a))throw new TypeError("viewMode() parameter must be one of ("+r.join(", ")+") value");return d.viewMode=a,i=Math.max(r.indexOf(a),p),I(),j},j.toolbarPlacement=function(a){if(0===arguments.length)return d.toolbarPlacement;if("string"!=typeof a)throw new TypeError("toolbarPlacement() expects a string parameter");if(-1===u.indexOf(a))throw new TypeError("toolbarPlacement() parameter must be one of ("+u.join(", ")+") value");return d.toolbarPlacement=a,o&&(X(),_()),j},j.widgetPositioning=function(b){if(0===arguments.length)return a.extend({},d.widgetPositioning);if("[object Object]"!=={}.toString.call(b))throw new TypeError("widgetPositioning() expects an object variable");if(b.horizontal){if("string"!=typeof b.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(b.horizontal=b.horizontal.toLowerCase(),-1===t.indexOf(b.horizontal))throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+t.join(", ")+")");d.widgetPositioning.horizontal=b.horizontal}if(b.vertical){if("string"!=typeof b.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(b.vertical=b.vertical.toLowerCase(),-1===s.indexOf(b.vertical))throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+s.join(", ")+")");d.widgetPositioning.vertical=b.vertical}return V(),j},j.calendarWeeks=function(a){if(0===arguments.length)return d.calendarWeeks;if("boolean"!=typeof a)throw new TypeError("calendarWeeks() expects parameter to be a boolean value");return d.calendarWeeks=a,V(),j},j.showTodayButton=function(a){if(0===arguments.length)return d.showTodayButton;if("boolean"!=typeof a)throw new TypeError("showTodayButton() expects a boolean parameter");return d.showTodayButton=a,o&&(X(),_()),j},j.showClear=function(a){if(0===arguments.length)return d.showClear;if("boolean"!=typeof a)throw new TypeError("showClear() expects a boolean parameter");return d.showClear=a,o&&(X(),_()),j},j.widgetParent=function(b){if(0===arguments.length)return d.widgetParent;if("string"==typeof b&&(b=a(b)),null!==b&&"string"!=typeof b&&!(b instanceof a))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");return d.widgetParent=b,o&&(X(),_()),j},j.keepOpen=function(a){if(0===arguments.length)return d.keepOpen;if("boolean"!=typeof a)throw new TypeError("keepOpen() expects a boolean parameter");return d.keepOpen=a,j},j.inline=function(a){if(0===arguments.length)return d.inline;if("boolean"!=typeof a)throw new TypeError("inline() expects a boolean parameter");return d.inline=a,j},j.clear=function(){return Y(),j},j.keyBinds=function(a){return d.keyBinds=a,j},j.debug=function(a){if("boolean"!=typeof a)throw new TypeError("debug() expects a boolean parameter");return d.debug=a,j},j.showClose=function(a){if(0===arguments.length)return d.showClose;if("boolean"!=typeof a)throw new TypeError("showClose() expects a boolean parameter");return d.showClose=a,j},j.keepInvalid=function(a){if(0===arguments.length)return d.keepInvalid;if("boolean"!=typeof a)throw new TypeError("keepInvalid() expects a boolean parameter");return d.keepInvalid=a,j},j.datepickerInput=function(a){if(0===arguments.length)return d.datepickerInput;if("string"!=typeof a)throw new TypeError("datepickerInput() expects a string parameter");return d.datepickerInput=a,j},c.is("input"))e=c;else if(e=c.find(d.datepickerInput),0===e.size())e=c.find("input");else if(!e.is("input"))throw new Error('CSS class "'+d.datepickerInput+'" cannot be applied to non input element');if(c.hasClass("input-group")&&(n=c.find(0===c.find(".datepickerbutton").size()?'[class^="input-group-"]':".datepickerbutton")),!d.inline&&!e.is("input"))throw new Error("Could not initialize DateTimePicker without an input element");return a.extend(!0,d,F()),j.options(d),ib(),fb(),e.prop("disabled")&&j.disable(),e.is("input")&&0!==e.val().trim().length?W(bb(e.val().trim())):d.defaultDate&&void 0===e.attr("placeholder")&&W(d.defaultDate),d.inline&&_(),j};a.fn.datetimepicker=function(b){return this.each(function(){var d=a(this);d.data("DateTimePicker")||(b=a.extend(!0,{},a.fn.datetimepicker.defaults,b),d.data("DateTimePicker",c(d,b)))})},a.fn.datetimepicker.defaults={format:!1,dayViewHeaderFormat:"MMMM YYYY",extraFormats:!1,stepping:1,minDate:!1,maxDate:!1,useCurrent:!0,collapse:!0,locale:b.locale(),defaultDate:!1,disabledDates:!1,enabledDates:!1,icons:{time:"glyphicon glyphicon-time",date:"glyphicon glyphicon-calendar",up:"glyphicon glyphicon-chevron-up",down:"glyphicon glyphicon-chevron-down",previous:"glyphicon glyphicon-chevron-left",next:"glyphicon glyphicon-chevron-right",today:"glyphicon glyphicon-screenshot",clear:"glyphicon glyphicon-trash",close:"glyphicon glyphicon-remove"},useStrict:!1,sideBySide:!1,daysOfWeekDisabled:[],calendarWeeks:!1,viewMode:"days",toolbarPlacement:"default",showTodayButton:!1,showClear:!1,showClose:!1,widgetPositioning:{horizontal:"auto",vertical:"auto"},widgetParent:null,ignoreReadonly:!1,keepOpen:!1,inline:!1,keepInvalid:!1,datepickerInput:".datepickerinput",keyBinds:{up:function(a){if(a){var c=this.date()||b();this.date(a.find(".datepicker").is(":visible")?c.clone().subtract(7,"d"):c.clone().add(1,"m"))}},down:function(a){if(!a)return void this.show();var c=this.date()||b();this.date(a.find(".datepicker").is(":visible")?c.clone().add(7,"d"):c.clone().subtract(1,"m"))},"control up":function(a){if(a){var c=this.date()||b();this.date(a.find(".datepicker").is(":visible")?c.clone().subtract(1,"y"):c.clone().add(1,"h"))}},"control down":function(a){if(a){var c=this.date()||b();this.date(a.find(".datepicker").is(":visible")?c.clone().add(1,"y"):c.clone().subtract(1,"h"))}},left:function(a){if(a){var c=this.date()||b();a.find(".datepicker").is(":visible")&&this.date(c.clone().subtract(1,"d"))}},right:function(a){if(a){var c=this.date()||b();a.find(".datepicker").is(":visible")&&this.date(c.clone().add(1,"d"))}},pageUp:function(a){if(a){var c=this.date()||b();a.find(".datepicker").is(":visible")&&this.date(c.clone().subtract(1,"M"))}},pageDown:function(a){if(a){var c=this.date()||b();a.find(".datepicker").is(":visible")&&this.date(c.clone().add(1,"M"))}},enter:function(){this.hide()},escape:function(){this.hide()},"control space":function(a){a.find(".timepicker").is(":visible")&&a.find('.btn[data-action="togglePeriod"]').click()},t:function(){this.date(b())},"delete":function(){this.clear()}},debug:!1}}); \ No newline at end of file diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/build/nuget/Bootstrap.v3.Datetimepicker.4.0.0.nupkg b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/nuget/Bootstrap.v3.Datetimepicker.4.0.0.nupkg new file mode 100755 index 0000000..f38538a Binary files /dev/null and b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/nuget/Bootstrap.v3.Datetimepicker.4.0.0.nupkg differ diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/build/nuget/Bootstrap.v3.Datetimepicker.CSS.4.0.0.nupkg b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/nuget/Bootstrap.v3.Datetimepicker.CSS.4.0.0.nupkg new file mode 100755 index 0000000..d10e267 Binary files /dev/null and b/www/bower_components/eonasdan-bootstrap-datetimepicker/build/nuget/Bootstrap.v3.Datetimepicker.CSS.4.0.0.nupkg differ diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/component.json b/www/bower_components/eonasdan-bootstrap-datetimepicker/component.json new file mode 100755 index 0000000..c39ee58 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/component.json @@ -0,0 +1,9 @@ +{ + "name": "bootstrap-datetimepicker", + "version": "4.7.14", + "main": ["build/css/bootstrap-datetimepicker.min.css","build/js/bootstrap-datetimepicker.min.js","src/less/_bootstrap-datetimepicker.less","src/less/bootstrap-datetimepicker-build.less","src/js/bootstrap-datetimepicker.js"], + "dependencies": { + "jquery" : ">=1.8.3", + "moment": ">=2.8.0" + } +} diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/composer.json b/www/bower_components/eonasdan-bootstrap-datetimepicker/composer.json new file mode 100755 index 0000000..85655b7 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/composer.json @@ -0,0 +1,31 @@ +{ + "name": "eonasdan/bootstrap-datetimepicker", + "type": "component", + "version": "4.7.14", + "description": "Date/time picker widget based on twitter bootstrap", + "keywords": [ + "bootstrap", + "datetimepicker" + ], + "homepage": "http://eonasdan.github.io/bootstrap-datetimepicker/", + "license": "MIT", + "require": { + "robloach/component-installer": "*", + "components/jquery": ">=1.9.1", + "moment/moment": ">=2.8" + }, + "extra": { + "component": { + "scripts": [ + "src/js/bootstrap-datetimepicker.js" + ], + "files": [ + "build/js/bootstrap-datetimepicker.min.js", + "build/css/bootstrap-datetimepicker.css", + "build/css/bootstrap-datetimepicker.min.css", + "src/less/_bootstrap-datetimepicker.less", + "src/less/bootstrap-datetimepicker-build.less" + ] + } + } +} diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/package.json b/www/bower_components/eonasdan-bootstrap-datetimepicker/package.json new file mode 100755 index 0000000..52c5db4 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/package.json @@ -0,0 +1,35 @@ +{ + "name": "bootstrap-datetimepicker", + "main": "src/js/bootstrap-datetimepicker.js", + "version": "4.7.14", + "repository": { + "type": "git", + "url": "https://github.com/eonasdan/bootstrap-datetimepicker.git" + }, + "bugs": { + "url": "https://github.com/eonasdan/bootstrap-datetimepicker/issues" + }, + "keywords": [ + "twitter-bootstrap", + "bootstrap", + "datepicker", + "datetimepicker", + "timepicker", + "moment" + ], + "dependencies": { + "moment": "~2.8.2", + "bootstrap": "^3.0", + "jquery": "latest" + }, + "devDependencies": { + "grunt": "latest", + "grunt-contrib-jasmine": "^0.7.0", + "grunt-contrib-jshint": "latest", + "grunt-contrib-less": "latest", + "grunt-contrib-uglify": "latest", + "grunt-jscs": "latest", + "grunt-string-replace": "latest", + "load-grunt-tasks": "latest" + } +} diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js new file mode 100755 index 0000000..4df14e4 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js @@ -0,0 +1,2051 @@ +/*! version : 4.7.14 + ========================================================= + bootstrap-datetimejs + https://github.com/Eonasdan/bootstrap-datetimepicker + Copyright (c) 2015 Jonathan Peterson + ========================================================= + */ +/* + The MIT License (MIT) + + Copyright (c) 2015 Jonathan Peterson + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ +/*global define:false */ +/*global exports:false */ +/*global require:false */ +/*global jQuery:false */ +/*global moment:false */ +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // AMD is used - Register as an anonymous module. + define(['jquery', 'moment'], factory); + } else if (typeof exports === 'object') { + factory(require('jquery'), require('moment')); + } else { + // Neither AMD nor CommonJS used. Use global variables. + if (typeof jQuery === 'undefined') { + throw 'bootstrap-datetimepicker requires jQuery to be loaded first'; + } + if (typeof moment === 'undefined') { + throw 'bootstrap-datetimepicker requires Moment.js to be loaded first'; + } + factory(jQuery, moment); + } +}(function ($, moment) { + 'use strict'; + if (!moment) { + throw new Error('bootstrap-datetimepicker requires Moment.js to be loaded first'); + } + + var dateTimePicker = function (element, options) { + var picker = {}, + date = moment().startOf('d'), + viewDate = date.clone(), + unset = true, + input, + component = false, + widget = false, + use24Hours, + minViewModeNumber = 0, + actualFormat, + parseFormats, + currentViewMode, + datePickerModes = [ + { + clsName: 'days', + navFnc: 'M', + navStep: 1 + }, + { + clsName: 'months', + navFnc: 'y', + navStep: 1 + }, + { + clsName: 'years', + navFnc: 'y', + navStep: 10 + } + ], + viewModes = ['days', 'months', 'years'], + verticalModes = ['top', 'bottom', 'auto'], + horizontalModes = ['left', 'right', 'auto'], + toolbarPlacements = ['default', 'top', 'bottom'], + keyMap = { + 'up': 38, + 38: 'up', + 'down': 40, + 40: 'down', + 'left': 37, + 37: 'left', + 'right': 39, + 39: 'right', + 'tab': 9, + 9: 'tab', + 'escape': 27, + 27: 'escape', + 'enter': 13, + 13: 'enter', + 'pageUp': 33, + 33: 'pageUp', + 'pageDown': 34, + 34: 'pageDown', + 'shift': 16, + 16: 'shift', + 'control': 17, + 17: 'control', + 'space': 32, + 32: 'space', + 't': 84, + 84: 't', + 'delete': 46, + 46: 'delete' + }, + keyState = {}, + + /******************************************************************************** + * + * Private functions + * + ********************************************************************************/ + isEnabled = function (granularity) { + if (typeof granularity !== 'string' || granularity.length > 1) { + throw new TypeError('isEnabled expects a single character string parameter'); + } + switch (granularity) { + case 'y': + return actualFormat.indexOf('Y') !== -1; + case 'M': + return actualFormat.indexOf('M') !== -1; + case 'd': + return actualFormat.toLowerCase().indexOf('d') !== -1; + case 'h': + case 'H': + return actualFormat.toLowerCase().indexOf('h') !== -1; + case 'm': + return actualFormat.indexOf('m') !== -1; + case 's': + return actualFormat.indexOf('s') !== -1; + default: + return false; + } + }, + + hasTime = function () { + return (isEnabled('h') || isEnabled('m') || isEnabled('s')); + }, + + hasDate = function () { + return (isEnabled('y') || isEnabled('M') || isEnabled('d')); + }, + + getDatePickerTemplate = function () { + var headTemplate = $('') + .append($('') + .append($('') + .append($('') + .append($('"}var d,e,f,g,h,k,l,m,n=this,o=n._id,p=0;for(m=n.settings.origin,d=c(m),d.push({text:tinymce.translate("No color"),color:"transparent"}),f='
    ").addClass("cw").text("#"));c.isBefore(l.clone().endOf("w"));)b.append(a("").addClass("dow").text(c.format("dd"))),c.add(1,"d");o.find(".datepicker-days thead").append(b)},K=function(a){return d.disabledDates[a.format("YYYY-MM-DD")]===!0},L=function(a){return d.enabledDates[a.format("YYYY-MM-DD")]===!0},M=function(a,b){return a.isValid()?d.disabledDates&&K(a)&&"M"!==b?!1:d.enabledDates&&!L(a)&&"M"!==b?!1:d.minDate&&a.isBefore(d.minDate,b)?!1:d.maxDate&&a.isAfter(d.maxDate,b)?!1:"d"===b&&-1!==d.daysOfWeekDisabled.indexOf(a.day())?!1:!0:!1},N=function(){for(var b=[],c=l.clone().startOf("y").hour(12);c.isSame(l,"y");)b.push(a("").attr("data-action","selectMonth").addClass("month").text(c.format("MMM"))),c.add(1,"M");o.find(".datepicker-months td").empty().append(b)},O=function(){var b=o.find(".datepicker-months"),c=b.find("th"),d=b.find("tbody").find("span");b.find(".disabled").removeClass("disabled"),M(l.clone().subtract(1,"y"),"y")||c.eq(0).addClass("disabled"),c.eq(1).text(l.year()),M(l.clone().add(1,"y"),"y")||c.eq(2).addClass("disabled"),d.removeClass("active"),k.isSame(l,"y")&&d.eq(k.month()).addClass("active"),d.each(function(b){M(l.clone().month(b),"M")||a(this).addClass("disabled")})},P=function(){var a=o.find(".datepicker-years"),b=a.find("th"),c=l.clone().subtract(5,"y"),e=l.clone().add(6,"y"),f="";for(a.find(".disabled").removeClass("disabled"),d.minDate&&d.minDate.isAfter(c,"y")&&b.eq(0).addClass("disabled"),b.eq(1).text(c.year()+"-"+e.year()),d.maxDate&&d.maxDate.isBefore(e,"y")&&b.eq(2).addClass("disabled");!c.isAfter(e,"y");)f+=''+c.year()+"",c.add(1,"y");a.find("td").html(f)},Q=function(){var c,e,f,g=o.find(".datepicker-days"),h=g.find("th"),i=[];if(z()){for(g.find(".disabled").removeClass("disabled"),h.eq(1).text(l.format(d.dayViewHeaderFormat)),M(l.clone().subtract(1,"M"),"M")||h.eq(0).addClass("disabled"),M(l.clone().add(1,"M"),"M")||h.eq(2).addClass("disabled"),c=l.clone().startOf("M").startOf("week");!l.clone().endOf("M").endOf("w").isBefore(c,"d");)0===c.weekday()&&(e=a("
    '+c.week()+"'+c.date()+"
    '+c.format(f?"HH":"hh")+"
    '+c.format("mm")+"
    '+c.format("ss")+"
    ').addClass('prev').attr('data-action', 'previous') + .append($('').addClass(options.icons.previous)) + ) + .append($('').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', (options.calendarWeeks ? '6' : '5'))) + .append($('').addClass('next').attr('data-action', 'next') + .append($('').addClass(options.icons.next)) + ) + ), + contTemplate = $('
    ').attr('colspan', (options.calendarWeeks ? '8' : '7'))) + ); + + return [ + $('
    ').addClass('datepicker-days') + .append($('').addClass('table-condensed') + .append(headTemplate) + .append($('')) + ), + $('
    ').addClass('datepicker-months') + .append($('
    ').addClass('table-condensed') + .append(headTemplate.clone()) + .append(contTemplate.clone()) + ), + $('
    ').addClass('datepicker-years') + .append($('
    ').addClass('table-condensed') + .append(headTemplate.clone()) + .append(contTemplate.clone()) + ) + ]; + }, + + getTimePickerMainTemplate = function () { + var topRow = $(''), + middleRow = $(''), + bottomRow = $(''); + + if (isEnabled('h')) { + topRow.append($('",d=0;a>d;d++)e+="";e+=""}return e+="
    ') + .append($('').attr({href: '#', tabindex: '-1'}).addClass('btn').attr('data-action', 'incrementHours') + .append($('').addClass(options.icons.up)))); + middleRow.append($('') + .append($('').addClass('timepicker-hour').attr('data-time-component', 'hours').attr('data-action', 'showHours'))); + bottomRow.append($('') + .append($('').attr({href: '#', tabindex: '-1'}).addClass('btn').attr('data-action', 'decrementHours') + .append($('').addClass(options.icons.down)))); + } + if (isEnabled('m')) { + if (isEnabled('h')) { + topRow.append($('').addClass('separator')); + middleRow.append($('').addClass('separator').html(':')); + bottomRow.append($('').addClass('separator')); + } + topRow.append($('') + .append($('').attr({href: '#', tabindex: '-1'}).addClass('btn').attr('data-action', 'incrementMinutes') + .append($('').addClass(options.icons.up)))); + middleRow.append($('') + .append($('').addClass('timepicker-minute').attr('data-time-component', 'minutes').attr('data-action', 'showMinutes'))); + bottomRow.append($('') + .append($('').attr({href: '#', tabindex: '-1'}).addClass('btn').attr('data-action', 'decrementMinutes') + .append($('').addClass(options.icons.down)))); + } + if (isEnabled('s')) { + if (isEnabled('m')) { + topRow.append($('').addClass('separator')); + middleRow.append($('').addClass('separator').html(':')); + bottomRow.append($('').addClass('separator')); + } + topRow.append($('') + .append($('').attr({href: '#', tabindex: '-1'}).addClass('btn').attr('data-action', 'incrementSeconds') + .append($('').addClass(options.icons.up)))); + middleRow.append($('') + .append($('').addClass('timepicker-second').attr('data-time-component', 'seconds').attr('data-action', 'showSeconds'))); + bottomRow.append($('') + .append($('').attr({href: '#', tabindex: '-1'}).addClass('btn').attr('data-action', 'decrementSeconds') + .append($('').addClass(options.icons.down)))); + } + + if (!use24Hours) { + topRow.append($('').addClass('separator')); + middleRow.append($('') + .append($('').addClass('separator')); + } + + return $('
    ').addClass('timepicker-picker') + .append($('').addClass('table-condensed') + .append([topRow, middleRow, bottomRow])); + }, + + getTimePickerTemplate = function () { + var hoursView = $('
    ').addClass('timepicker-hours') + .append($('
    ').addClass('table-condensed')), + minutesView = $('
    ').addClass('timepicker-minutes') + .append($('
    ').addClass('table-condensed')), + secondsView = $('
    ').addClass('timepicker-seconds') + .append($('
    ').addClass('table-condensed')), + ret = [getTimePickerMainTemplate()]; + + if (isEnabled('h')) { + ret.push(hoursView); + } + if (isEnabled('m')) { + ret.push(minutesView); + } + if (isEnabled('s')) { + ret.push(secondsView); + } + + return ret; + }, + + getToolbar = function () { + var row = []; + if (options.showTodayButton) { + row.push($(''}),a+=""}),a+="
    ').append($('').attr('data-action', 'today').append($('').addClass(options.icons.today)))); + } + if (!options.sideBySide && hasDate() && hasTime()) { + row.push($('').append($('').attr('data-action', 'togglePicker').append($('').addClass(options.icons.time)))); + } + if (options.showClear) { + row.push($('').append($('').attr('data-action', 'clear').append($('').addClass(options.icons.clear)))); + } + if (options.showClose) { + row.push($('').append($('').attr('data-action', 'close').append($('').addClass(options.icons.close)))); + } + return $('').addClass('table-condensed').append($('').append($('').append(row))); + }, + + getTemplate = function () { + var template = $('
    ').addClass('bootstrap-datetimepicker-widget dropdown-menu'), + dateView = $('
    ').addClass('datepicker').append(getDatePickerTemplate()), + timeView = $('
    ').addClass('timepicker').append(getTimePickerTemplate()), + content = $('
      ').addClass('list-unstyled'), + toolbar = $('
    • ').addClass('picker-switch' + (options.collapse ? ' accordion-toggle' : '')).append(getToolbar()); + + if (options.inline) { + template.removeClass('dropdown-menu'); + } + + if (use24Hours) { + template.addClass('usetwentyfour'); + } + if (options.sideBySide && hasDate() && hasTime()) { + template.addClass('timepicker-sbs'); + template.append( + $('
      ').addClass('row') + .append(dateView.addClass('col-sm-6')) + .append(timeView.addClass('col-sm-6')) + ); + template.append(toolbar); + return template; + } + + if (options.toolbarPlacement === 'top') { + content.append(toolbar); + } + if (hasDate()) { + content.append($('
    • ').addClass((options.collapse && hasTime() ? 'collapse in' : '')).append(dateView)); + } + if (options.toolbarPlacement === 'default') { + content.append(toolbar); + } + if (hasTime()) { + content.append($('
    • ').addClass((options.collapse && hasDate() ? 'collapse' : '')).append(timeView)); + } + if (options.toolbarPlacement === 'bottom') { + content.append(toolbar); + } + return template.append(content); + }, + + dataToOptions = function () { + var eData, + dataOptions = {}; + + if (element.is('input') || options.inline) { + eData = element.data(); + } else { + eData = element.find('input').data(); + } + + if (eData.dateOptions && eData.dateOptions instanceof Object) { + dataOptions = $.extend(true, dataOptions, eData.dateOptions); + } + + $.each(options, function (key) { + var attributeName = 'date' + key.charAt(0).toUpperCase() + key.slice(1); + if (eData[attributeName] !== undefined) { + dataOptions[key] = eData[attributeName]; + } + }); + return dataOptions; + }, + + place = function () { + var position = (component || element).position(), + offset = (component || element).offset(), + vertical = options.widgetPositioning.vertical, + horizontal = options.widgetPositioning.horizontal, + parent; + + if (options.widgetParent) { + parent = options.widgetParent.append(widget); + } else if (element.is('input')) { + parent = element.parent().append(widget); + } else if (options.inline) { + parent = element.append(widget); + return; + } else { + parent = element; + element.children().first().after(widget); + } + + // Top and bottom logic + if (vertical === 'auto') { + if (offset.top + widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() && + widget.height() + element.outerHeight() < offset.top) { + vertical = 'top'; + } else { + vertical = 'bottom'; + } + } + + // Left and right logic + if (horizontal === 'auto') { + if (parent.width() < offset.left + widget.outerWidth() / 2 && + offset.left + widget.outerWidth() > $(window).width()) { + horizontal = 'right'; + } else { + horizontal = 'left'; + } + } + + if (vertical === 'top') { + widget.addClass('top').removeClass('bottom'); + } else { + widget.addClass('bottom').removeClass('top'); + } + + if (horizontal === 'right') { + widget.addClass('pull-right'); + } else { + widget.removeClass('pull-right'); + } + + // find the first parent element that has a relative css positioning + if (parent.css('position') !== 'relative') { + parent = parent.parents().filter(function () { + return $(this).css('position') === 'relative'; + }).first(); + } + + if (parent.length === 0) { + throw new Error('datetimepicker component should be placed within a relative positioned container'); + } + + widget.css({ + top: vertical === 'top' ? 'auto' : position.top + element.outerHeight(), + bottom: vertical === 'top' ? position.top + element.outerHeight() : 'auto', + left: horizontal === 'left' ? parent.css('padding-left') : 'auto', + right: horizontal === 'left' ? 'auto' : parent.width() - element.outerWidth() + }); + }, + + notifyEvent = function (e) { + if (e.type === 'dp.change' && ((e.date && e.date.isSame(e.oldDate)) || (!e.date && !e.oldDate))) { + return; + } + element.trigger(e); + }, + + showMode = function (dir) { + if (!widget) { + return; + } + if (dir) { + currentViewMode = Math.max(minViewModeNumber, Math.min(2, currentViewMode + dir)); + } + widget.find('.datepicker > div').hide().filter('.datepicker-' + datePickerModes[currentViewMode].clsName).show(); + }, + + fillDow = function () { + var row = $('
    '), + currentDate = viewDate.clone().startOf('w'); + + if (options.calendarWeeks === true) { + row.append($(''); + if (options.calendarWeeks) { + row.append(''); + } + html.push(row); + } + clsName = ''; + if (currentDate.isBefore(viewDate, 'M')) { + clsName += ' old'; + } + if (currentDate.isAfter(viewDate, 'M')) { + clsName += ' new'; + } + if (currentDate.isSame(date, 'd') && !unset) { + clsName += ' active'; + } + if (!isValid(currentDate, 'd')) { + clsName += ' disabled'; + } + if (currentDate.isSame(moment(), 'd')) { + clsName += ' today'; + } + if (currentDate.day() === 0 || currentDate.day() === 6) { + clsName += ' weekend'; + } + row.append(''); + currentDate.add(1, 'd'); + } + + daysView.find('tbody').empty().append(html); + + updateMonths(); + + updateYears(); + }, + + fillHours = function () { + var table = widget.find('.timepicker-hours table'), + currentHour = viewDate.clone().startOf('d'), + html = [], + row = $(''); + + if (viewDate.hour() > 11 && !use24Hours) { + currentHour.hour(12); + } + while (currentHour.isSame(viewDate, 'd') && (use24Hours || (viewDate.hour() < 12 && currentHour.hour() < 12) || viewDate.hour() > 11)) { + if (currentHour.hour() % 4 === 0) { + row = $(''); + html.push(row); + } + row.append(''); + currentHour.add(1, 'h'); + } + table.empty().append(html); + }, + + fillMinutes = function () { + var table = widget.find('.timepicker-minutes table'), + currentMinute = viewDate.clone().startOf('h'), + html = [], + row = $(''), + step = options.stepping === 1 ? 5 : options.stepping; + + while (viewDate.isSame(currentMinute, 'h')) { + if (currentMinute.minute() % (step * 4) === 0) { + row = $(''); + html.push(row); + } + row.append(''); + currentMinute.add(step, 'm'); + } + table.empty().append(html); + }, + + fillSeconds = function () { + var table = widget.find('.timepicker-seconds table'), + currentSecond = viewDate.clone().startOf('m'), + html = [], + row = $(''); + + while (viewDate.isSame(currentSecond, 'm')) { + if (currentSecond.second() % 20 === 0) { + row = $(''); + html.push(row); + } + row.append(''); + currentSecond.add(5, 's'); + } + + table.empty().append(html); + }, + + fillTime = function () { + var timeComponents = widget.find('.timepicker span[data-time-component]'); + if (!use24Hours) { + widget.find('.timepicker [data-action=togglePeriod]').text(date.format('A')); + } + timeComponents.filter('[data-time-component=hours]').text(date.format(use24Hours ? 'HH' : 'hh')); + timeComponents.filter('[data-time-component=minutes]').text(date.format('mm')); + timeComponents.filter('[data-time-component=seconds]').text(date.format('ss')); + + fillHours(); + fillMinutes(); + fillSeconds(); + }, + + update = function () { + if (!widget) { + return; + } + fillDate(); + fillTime(); + }, + + setValue = function (targetMoment) { + var oldDate = unset ? null : date; + + // case of calling setValue(null or false) + if (!targetMoment) { + unset = true; + input.val(''); + element.data('date', ''); + notifyEvent({ + type: 'dp.change', + date: null, + oldDate: oldDate + }); + update(); + return; + } + + targetMoment = targetMoment.clone().locale(options.locale); + + if (options.stepping !== 1) { + targetMoment.minutes((Math.round(targetMoment.minutes() / options.stepping) * options.stepping) % 60).seconds(0); + } + + if (isValid(targetMoment)) { + date = targetMoment; + viewDate = date.clone(); + input.val(date.format(actualFormat)); + element.data('date', date.format(actualFormat)); + update(); + unset = false; + notifyEvent({ + type: 'dp.change', + date: date.clone(), + oldDate: oldDate + }); + } else { + if (!options.keepInvalid) { + input.val(unset ? '' : date.format(actualFormat)); + } + notifyEvent({ + type: 'dp.error', + date: targetMoment + }); + } + }, + + hide = function () { + var transitioning = false; + if (!widget) { + return picker; + } + // Ignore event if in the middle of a picker transition + widget.find('.collapse').each(function () { + var collapseData = $(this).data('collapse'); + if (collapseData && collapseData.transitioning) { + transitioning = true; + return false; + } + return true; + }); + if (transitioning) { + return picker; + } + if (component && component.hasClass('btn')) { + component.toggleClass('active'); + } + widget.hide(); + + $(window).off('resize', place); + widget.off('click', '[data-action]'); + widget.off('mousedown', false); + + widget.remove(); + widget = false; + + notifyEvent({ + type: 'dp.hide', + date: date.clone() + }); + return picker; + }, + + clear = function () { + setValue(null); + }, + + /******************************************************************************** + * + * Widget UI interaction functions + * + ********************************************************************************/ + actions = { + next: function () { + viewDate.add(datePickerModes[currentViewMode].navStep, datePickerModes[currentViewMode].navFnc); + fillDate(); + }, + + previous: function () { + viewDate.subtract(datePickerModes[currentViewMode].navStep, datePickerModes[currentViewMode].navFnc); + fillDate(); + }, + + pickerSwitch: function () { + showMode(1); + }, + + selectMonth: function (e) { + var month = $(e.target).closest('tbody').find('span').index($(e.target)); + viewDate.month(month); + if (currentViewMode === minViewModeNumber) { + setValue(date.clone().year(viewDate.year()).month(viewDate.month())); + if (!options.inline) { + hide(); + } + } else { + showMode(-1); + fillDate(); + } + }, + + selectYear: function (e) { + var year = parseInt($(e.target).text(), 10) || 0; + viewDate.year(year); + if (currentViewMode === minViewModeNumber) { + setValue(date.clone().year(viewDate.year())); + if (!options.inline) { + hide(); + } + } else { + showMode(-1); + fillDate(); + } + }, + + selectDay: function (e) { + var day = viewDate.clone(); + if ($(e.target).is('.old')) { + day.subtract(1, 'M'); + } + if ($(e.target).is('.new')) { + day.add(1, 'M'); + } + setValue(day.date(parseInt($(e.target).text(), 10))); + if (!hasTime() && !options.keepOpen && !options.inline) { + hide(); + } + }, + + incrementHours: function () { + setValue(date.clone().add(1, 'h')); + }, + + incrementMinutes: function () { + setValue(date.clone().add(options.stepping, 'm')); + }, + + incrementSeconds: function () { + setValue(date.clone().add(1, 's')); + }, + + decrementHours: function () { + setValue(date.clone().subtract(1, 'h')); + }, + + decrementMinutes: function () { + setValue(date.clone().subtract(options.stepping, 'm')); + }, + + decrementSeconds: function () { + setValue(date.clone().subtract(1, 's')); + }, + + togglePeriod: function () { + setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h')); + }, + + togglePicker: function (e) { + var $this = $(e.target), + $parent = $this.closest('ul'), + expanded = $parent.find('.in'), + closed = $parent.find('.collapse:not(.in)'), + collapseData; + + if (expanded && expanded.length) { + collapseData = expanded.data('collapse'); + if (collapseData && collapseData.transitioning) { + return; + } + if (expanded.collapse) { // if collapse plugin is available through bootstrap.js then use it + expanded.collapse('hide'); + closed.collapse('show'); + } else { // otherwise just toggle in class on the two views + expanded.removeClass('in'); + closed.addClass('in'); + } + if ($this.is('span')) { + $this.toggleClass(options.icons.time + ' ' + options.icons.date); + } else { + $this.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); + } + + // NOTE: uncomment if toggled state will be restored in show() + //if (component) { + // component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); + //} + } + }, + + showPicker: function () { + widget.find('.timepicker > div:not(.timepicker-picker)').hide(); + widget.find('.timepicker .timepicker-picker').show(); + }, + + showHours: function () { + widget.find('.timepicker .timepicker-picker').hide(); + widget.find('.timepicker .timepicker-hours').show(); + }, + + showMinutes: function () { + widget.find('.timepicker .timepicker-picker').hide(); + widget.find('.timepicker .timepicker-minutes').show(); + }, + + showSeconds: function () { + widget.find('.timepicker .timepicker-picker').hide(); + widget.find('.timepicker .timepicker-seconds').show(); + }, + + selectHour: function (e) { + var hour = parseInt($(e.target).text(), 10); + + if (!use24Hours) { + if (date.hours() >= 12) { + if (hour !== 12) { + hour += 12; + } + } else { + if (hour === 12) { + hour = 0; + } + } + } + setValue(date.clone().hours(hour)); + actions.showPicker.call(picker); + }, + + selectMinute: function (e) { + setValue(date.clone().minutes(parseInt($(e.target).text(), 10))); + actions.showPicker.call(picker); + }, + + selectSecond: function (e) { + setValue(date.clone().seconds(parseInt($(e.target).text(), 10))); + actions.showPicker.call(picker); + }, + + clear: clear, + + today: function () { + setValue(moment()); + }, + + close: hide + }, + + doAction = function (e) { + if ($(e.currentTarget).is('.disabled')) { + return false; + } + actions[$(e.currentTarget).data('action')].apply(picker, arguments); + return false; + }, + + show = function () { + var currentMoment, + useCurrentGranularity = { + 'year': function (m) { + return m.month(0).date(1).hours(0).seconds(0).minutes(0); + }, + 'month': function (m) { + return m.date(1).hours(0).seconds(0).minutes(0); + }, + 'day': function (m) { + return m.hours(0).seconds(0).minutes(0); + }, + 'hour': function (m) { + return m.seconds(0).minutes(0); + }, + 'minute': function (m) { + return m.seconds(0); + } + }; + + if (input.prop('disabled') || (!options.ignoreReadonly && input.prop('readonly')) || widget) { + return picker; + } + if (options.useCurrent && unset && ((input.is('input') && input.val().trim().length === 0) || options.inline)) { + currentMoment = moment(); + if (typeof options.useCurrent === 'string') { + currentMoment = useCurrentGranularity[options.useCurrent](currentMoment); + } + setValue(currentMoment); + } + + widget = getTemplate(); + + fillDow(); + fillMonths(); + + widget.find('.timepicker-hours').hide(); + widget.find('.timepicker-minutes').hide(); + widget.find('.timepicker-seconds').hide(); + + update(); + showMode(); + + $(window).on('resize', place); + widget.on('click', '[data-action]', doAction); // this handles clicks on the widget + widget.on('mousedown', false); + + if (component && component.hasClass('btn')) { + component.toggleClass('active'); + } + widget.show(); + place(); + + if (!input.is(':focus')) { + input.focus(); + } + + notifyEvent({ + type: 'dp.show' + }); + return picker; + }, + + toggle = function () { + return (widget ? hide() : show()); + }, + + parseInputDate = function (inputDate) { + if (moment.isMoment(inputDate) || inputDate instanceof Date) { + inputDate = moment(inputDate); + } else { + inputDate = moment(inputDate, parseFormats, options.useStrict); + } + inputDate.locale(options.locale); + return inputDate; + }, + + keydown = function (e) { + //if (e.keyCode === 27 && widget) { // allow escape to hide picker + // hide(); + // return false; + //} + //if (e.keyCode === 40 && !widget) { // allow down to show picker + // show(); + // e.preventDefault(); + //} + //return true; + + var handler = null, + index, + index2, + pressedKeys = [], + pressedModifiers = {}, + currentKey = e.which, + keyBindKeys, + allModifiersPressed, + pressed = 'p'; + + keyState[currentKey] = pressed; + + for (index in keyState) { + if (keyState.hasOwnProperty(index) && keyState[index] === pressed) { + pressedKeys.push(index); + if (parseInt(index, 10) !== currentKey) { + pressedModifiers[index] = true; + } + } + } + + for (index in options.keyBinds) { + if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') { + keyBindKeys = index.split(' '); + if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) { + allModifiersPressed = true; + for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) { + if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) { + allModifiersPressed = false; + break; + } + } + if (allModifiersPressed) { + handler = options.keyBinds[index]; + break; + } + } + } + } + + if (handler) { + handler.call(picker, widget); + e.stopPropagation(); + e.preventDefault(); + } + }, + + keyup = function (e) { + keyState[e.which] = 'r'; + e.stopPropagation(); + e.preventDefault(); + }, + + change = function (e) { + var val = $(e.target).val().trim(), + parsedDate = val ? parseInputDate(val) : null; + setValue(parsedDate); + e.stopImmediatePropagation(); + return false; + }, + + attachDatePickerElementEvents = function () { + input.on({ + 'change': change, + 'blur': options.debug ? '' : hide, + 'keydown': keydown, + 'keyup': keyup + }); + + if (element.is('input')) { + input.on({ + 'focus': show + }); + } else if (component) { + component.on('click', toggle); + component.on('mousedown', false); + } + }, + + detachDatePickerElementEvents = function () { + input.off({ + 'change': change, + 'blur': hide, + 'keydown': keydown, + 'keyup': keyup + }); + + if (element.is('input')) { + input.off({ + 'focus': show + }); + } else if (component) { + component.off('click', toggle); + component.off('mousedown', false); + } + }, + + indexGivenDates = function (givenDatesArray) { + // Store given enabledDates and disabledDates as keys. + // This way we can check their existence in O(1) time instead of looping through whole array. + // (for example: options.enabledDates['2014-02-27'] === true) + var givenDatesIndexed = {}; + $.each(givenDatesArray, function () { + var dDate = parseInputDate(this); + if (dDate.isValid()) { + givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true; + } + }); + return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false; + }, + + initFormatting = function () { + var format = options.format || 'L LT'; + + actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput) { + var newinput = date.localeData().longDateFormat(formatInput) || formatInput; + return newinput.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput2) { //temp fix for #740 + return date.localeData().longDateFormat(formatInput2) || formatInput2; + }); + }); + + + parseFormats = options.extraFormats ? options.extraFormats.slice() : []; + if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) { + parseFormats.push(actualFormat); + } + + use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.indexOf('h') < 1); + + if (isEnabled('y')) { + minViewModeNumber = 2; + } + if (isEnabled('M')) { + minViewModeNumber = 1; + } + if (isEnabled('d')) { + minViewModeNumber = 0; + } + + currentViewMode = Math.max(minViewModeNumber, currentViewMode); + + if (!unset) { + setValue(date); + } + }; + + /******************************************************************************** + * + * Public API functions + * ===================== + * + * Important: Do not expose direct references to private objects or the options + * object to the outer world. Always return a clone when returning values or make + * a clone when setting a private variable. + * + ********************************************************************************/ + picker.destroy = function () { + hide(); + detachDatePickerElementEvents(); + element.removeData('DateTimePicker'); + element.removeData('date'); + }; + + picker.toggle = toggle; + + picker.show = show; + + picker.hide = hide; + + picker.disable = function () { + hide(); + if (component && component.hasClass('btn')) { + component.addClass('disabled'); + } + input.prop('disabled', true); + return picker; + }; + + picker.enable = function () { + if (component && component.hasClass('btn')) { + component.removeClass('disabled'); + } + input.prop('disabled', false); + return picker; + }; + + picker.ignoreReadonly = function (ignoreReadonly) { + if (arguments.length === 0) { + return options.ignoreReadonly; + } + if (typeof ignoreReadonly !== 'boolean') { + throw new TypeError('ignoreReadonly () expects a boolean parameter'); + } + options.ignoreReadonly = ignoreReadonly; + return picker; + }; + + picker.options = function (newOptions) { + if (arguments.length === 0) { + return $.extend(true, {}, options); + } + + if (!(newOptions instanceof Object)) { + throw new TypeError('options() options parameter should be an object'); + } + $.extend(true, options, newOptions); + $.each(options, function (key, value) { + if (picker[key] !== undefined) { + picker[key](value); + } else { + throw new TypeError('option ' + key + ' is not recognized!'); + } + }); + return picker; + }; + + picker.date = function (newDate) { + if (arguments.length === 0) { + if (unset) { + return null; + } + return date.clone(); + } + + if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { + throw new TypeError('date() parameter must be one of [null, string, moment or Date]'); + } + + setValue(newDate === null ? null : parseInputDate(newDate)); + return picker; + }; + + picker.format = function (newFormat) { + if (arguments.length === 0) { + return options.format; + } + + if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) { + throw new TypeError('format() expects a sting or boolean:false parameter ' + newFormat); + } + + options.format = newFormat; + if (actualFormat) { + initFormatting(); // reinit formatting + } + return picker; + }; + + picker.dayViewHeaderFormat = function (newFormat) { + if (arguments.length === 0) { + return options.dayViewHeaderFormat; + } + + if (typeof newFormat !== 'string') { + throw new TypeError('dayViewHeaderFormat() expects a string parameter'); + } + + options.dayViewHeaderFormat = newFormat; + return picker; + }; + + picker.extraFormats = function (formats) { + if (arguments.length === 0) { + return options.extraFormats; + } + + if (formats !== false && !(formats instanceof Array)) { + throw new TypeError('extraFormats() expects an array or false parameter'); + } + + options.extraFormats = formats; + if (parseFormats) { + initFormatting(); // reinit formatting + } + return picker; + }; + + picker.disabledDates = function (dates) { + if (arguments.length === 0) { + return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates); + } + + if (!dates) { + options.disabledDates = false; + update(); + return picker; + } + if (!(dates instanceof Array)) { + throw new TypeError('disabledDates() expects an array parameter'); + } + options.disabledDates = indexGivenDates(dates); + options.enabledDates = false; + update(); + return picker; + }; + + picker.enabledDates = function (dates) { + if (arguments.length === 0) { + return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates); + } + + if (!dates) { + options.enabledDates = false; + update(); + return picker; + } + if (!(dates instanceof Array)) { + throw new TypeError('enabledDates() expects an array parameter'); + } + options.enabledDates = indexGivenDates(dates); + options.disabledDates = false; + update(); + return picker; + }; + + picker.daysOfWeekDisabled = function (daysOfWeekDisabled) { + if (arguments.length === 0) { + return options.daysOfWeekDisabled.splice(0); + } + + if (!(daysOfWeekDisabled instanceof Array)) { + throw new TypeError('daysOfWeekDisabled() expects an array parameter'); + } + options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) { + currentValue = parseInt(currentValue, 10); + if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) { + return previousValue; + } + if (previousValue.indexOf(currentValue) === -1) { + previousValue.push(currentValue); + } + return previousValue; + }, []).sort(); + update(); + return picker; + }; + + picker.maxDate = function (maxDate) { + if (arguments.length === 0) { + return options.maxDate ? options.maxDate.clone() : options.maxDate; + } + + if ((typeof maxDate === 'boolean') && maxDate === false) { + options.maxDate = false; + update(); + return picker; + } + + if (typeof maxDate === 'string') { + if (maxDate === 'now' || maxDate === 'moment') { + maxDate = moment(); + } + } + + var parsedDate = parseInputDate(maxDate); + + if (!parsedDate.isValid()) { + throw new TypeError('maxDate() Could not parse date parameter: ' + maxDate); + } + if (options.minDate && parsedDate.isBefore(options.minDate)) { + throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat)); + } + options.maxDate = parsedDate; + if (options.maxDate.isBefore(maxDate)) { + setValue(options.maxDate); + } + if (viewDate.isAfter(parsedDate)) { + viewDate = parsedDate.clone(); + } + update(); + return picker; + }; + + picker.minDate = function (minDate) { + if (arguments.length === 0) { + return options.minDate ? options.minDate.clone() : options.minDate; + } + + if ((typeof minDate === 'boolean') && minDate === false) { + options.minDate = false; + update(); + return picker; + } + + if (typeof minDate === 'string') { + if (minDate === 'now' || minDate === 'moment') { + minDate = moment(); + } + } + + var parsedDate = parseInputDate(minDate); + + if (!parsedDate.isValid()) { + throw new TypeError('minDate() Could not parse date parameter: ' + minDate); + } + if (options.maxDate && parsedDate.isAfter(options.maxDate)) { + throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat)); + } + options.minDate = parsedDate; + if (options.minDate.isAfter(minDate)) { + setValue(options.minDate); + } + if (viewDate.isBefore(parsedDate)) { + viewDate = parsedDate.clone(); + } + update(); + return picker; + }; + + picker.defaultDate = function (defaultDate) { + if (arguments.length === 0) { + return options.defaultDate ? options.defaultDate.clone() : options.defaultDate; + } + if (!defaultDate) { + options.defaultDate = false; + return picker; + } + + if (typeof defaultDate === 'string') { + if (defaultDate === 'now' || defaultDate === 'moment') { + defaultDate = moment(); + } + } + + var parsedDate = parseInputDate(defaultDate); + if (!parsedDate.isValid()) { + throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate); + } + if (!isValid(parsedDate)) { + throw new TypeError('defaultDate() date passed is invalid according to component setup validations'); + } + + options.defaultDate = parsedDate; + + if (options.defaultDate && input.val().trim() === '' && input.attr('placeholder') === undefined) { + setValue(options.defaultDate); + } + return picker; + }; + + picker.locale = function (locale) { + if (arguments.length === 0) { + return options.locale; + } + + if (!moment.localeData(locale)) { + throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!'); + } + + options.locale = locale; + date.locale(options.locale); + viewDate.locale(options.locale); + + if (actualFormat) { + initFormatting(); // reinit formatting + } + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.stepping = function (stepping) { + if (arguments.length === 0) { + return options.stepping; + } + + stepping = parseInt(stepping, 10); + if (isNaN(stepping) || stepping < 1) { + stepping = 1; + } + options.stepping = stepping; + return picker; + }; + + picker.useCurrent = function (useCurrent) { + var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute']; + if (arguments.length === 0) { + return options.useCurrent; + } + + if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) { + throw new TypeError('useCurrent() expects a boolean or string parameter'); + } + if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) { + throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', ')); + } + options.useCurrent = useCurrent; + return picker; + }; + + picker.collapse = function (collapse) { + if (arguments.length === 0) { + return options.collapse; + } + + if (typeof collapse !== 'boolean') { + throw new TypeError('collapse() expects a boolean parameter'); + } + if (options.collapse === collapse) { + return picker; + } + options.collapse = collapse; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.icons = function (icons) { + if (arguments.length === 0) { + return $.extend({}, options.icons); + } + + if (!(icons instanceof Object)) { + throw new TypeError('icons() expects parameter to be an Object'); + } + $.extend(options.icons, icons); + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.useStrict = function (useStrict) { + if (arguments.length === 0) { + return options.useStrict; + } + + if (typeof useStrict !== 'boolean') { + throw new TypeError('useStrict() expects a boolean parameter'); + } + options.useStrict = useStrict; + return picker; + }; + + picker.sideBySide = function (sideBySide) { + if (arguments.length === 0) { + return options.sideBySide; + } + + if (typeof sideBySide !== 'boolean') { + throw new TypeError('sideBySide() expects a boolean parameter'); + } + options.sideBySide = sideBySide; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.viewMode = function (viewMode) { + if (arguments.length === 0) { + return options.viewMode; + } + + if (typeof viewMode !== 'string') { + throw new TypeError('viewMode() expects a string parameter'); + } + + if (viewModes.indexOf(viewMode) === -1) { + throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value'); + } + + options.viewMode = viewMode; + currentViewMode = Math.max(viewModes.indexOf(viewMode), minViewModeNumber); + + showMode(); + return picker; + }; + + picker.toolbarPlacement = function (toolbarPlacement) { + if (arguments.length === 0) { + return options.toolbarPlacement; + } + + if (typeof toolbarPlacement !== 'string') { + throw new TypeError('toolbarPlacement() expects a string parameter'); + } + if (toolbarPlacements.indexOf(toolbarPlacement) === -1) { + throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value'); + } + options.toolbarPlacement = toolbarPlacement; + + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.widgetPositioning = function (widgetPositioning) { + if (arguments.length === 0) { + return $.extend({}, options.widgetPositioning); + } + + if (({}).toString.call(widgetPositioning) !== '[object Object]') { + throw new TypeError('widgetPositioning() expects an object variable'); + } + if (widgetPositioning.horizontal) { + if (typeof widgetPositioning.horizontal !== 'string') { + throw new TypeError('widgetPositioning() horizontal variable must be a string'); + } + widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase(); + if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) { + throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')'); + } + options.widgetPositioning.horizontal = widgetPositioning.horizontal; + } + if (widgetPositioning.vertical) { + if (typeof widgetPositioning.vertical !== 'string') { + throw new TypeError('widgetPositioning() vertical variable must be a string'); + } + widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase(); + if (verticalModes.indexOf(widgetPositioning.vertical) === -1) { + throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')'); + } + options.widgetPositioning.vertical = widgetPositioning.vertical; + } + update(); + return picker; + }; + + picker.calendarWeeks = function (calendarWeeks) { + if (arguments.length === 0) { + return options.calendarWeeks; + } + + if (typeof calendarWeeks !== 'boolean') { + throw new TypeError('calendarWeeks() expects parameter to be a boolean value'); + } + + options.calendarWeeks = calendarWeeks; + update(); + return picker; + }; + + picker.showTodayButton = function (showTodayButton) { + if (arguments.length === 0) { + return options.showTodayButton; + } + + if (typeof showTodayButton !== 'boolean') { + throw new TypeError('showTodayButton() expects a boolean parameter'); + } + + options.showTodayButton = showTodayButton; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.showClear = function (showClear) { + if (arguments.length === 0) { + return options.showClear; + } + + if (typeof showClear !== 'boolean') { + throw new TypeError('showClear() expects a boolean parameter'); + } + + options.showClear = showClear; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.widgetParent = function (widgetParent) { + if (arguments.length === 0) { + return options.widgetParent; + } + + if (typeof widgetParent === 'string') { + widgetParent = $(widgetParent); + } + + if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof $))) { + throw new TypeError('widgetParent() expects a string or a jQuery object parameter'); + } + + options.widgetParent = widgetParent; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.keepOpen = function (keepOpen) { + if (arguments.length === 0) { + return options.keepOpen; + } + + if (typeof keepOpen !== 'boolean') { + throw new TypeError('keepOpen() expects a boolean parameter'); + } + + options.keepOpen = keepOpen; + return picker; + }; + + picker.inline = function (inline) { + if (arguments.length === 0) { + return options.inline; + } + + if (typeof inline !== 'boolean') { + throw new TypeError('inline() expects a boolean parameter'); + } + + options.inline = inline; + return picker; + }; + + picker.clear = function () { + clear(); + return picker; + }; + + picker.keyBinds = function (keyBinds) { + options.keyBinds = keyBinds; + return picker; + }; + + picker.debug = function (debug) { + if (typeof debug !== 'boolean') { + throw new TypeError('debug() expects a boolean parameter'); + } + + options.debug = debug; + return picker; + }; + + picker.showClose = function (showClose) { + if (arguments.length === 0) { + return options.showClose; + } + + if (typeof showClose !== 'boolean') { + throw new TypeError('showClose() expects a boolean parameter'); + } + + options.showClose = showClose; + return picker; + }; + + picker.keepInvalid = function (keepInvalid) { + if (arguments.length === 0) { + return options.keepInvalid; + } + + if (typeof keepInvalid !== 'boolean') { + throw new TypeError('keepInvalid() expects a boolean parameter'); + } + options.keepInvalid = keepInvalid; + return picker; + }; + + picker.datepickerInput = function (datepickerInput) { + if (arguments.length === 0) { + return options.datepickerInput; + } + + if (typeof datepickerInput !== 'string') { + throw new TypeError('datepickerInput() expects a string parameter'); + } + + options.datepickerInput = datepickerInput; + return picker; + }; + + // initializing element and component attributes + if (element.is('input')) { + input = element; + } else { + input = element.find(options.datepickerInput); + if (input.size() === 0) { + input = element.find('input'); + } else if (!input.is('input')) { + throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element'); + } + } + + if (element.hasClass('input-group')) { + // in case there is more then one 'input-group-addon' Issue #48 + if (element.find('.datepickerbutton').size() === 0) { + component = element.find('[class^="input-group-"]'); + } else { + component = element.find('.datepickerbutton'); + } + } + + if (!options.inline && !input.is('input')) { + throw new Error('Could not initialize DateTimePicker without an input element'); + } + + $.extend(true, options, dataToOptions()); + + picker.options(options); + + initFormatting(); + + attachDatePickerElementEvents(); + + if (input.prop('disabled')) { + picker.disable(); + } + if (input.is('input') && input.val().trim().length !== 0) { + setValue(parseInputDate(input.val().trim())); + } + else if (options.defaultDate && input.attr('placeholder') === undefined) { + setValue(options.defaultDate); + } + if (options.inline) { + show(); + } + return picker; + }; + + /******************************************************************************** + * + * jQuery plugin constructor and defaults object + * + ********************************************************************************/ + + $.fn.datetimepicker = function (options) { + return this.each(function () { + var $this = $(this); + if (!$this.data('DateTimePicker')) { + // create a private copy of the defaults object + options = $.extend(true, {}, $.fn.datetimepicker.defaults, options); + $this.data('DateTimePicker', dateTimePicker($this, options)); + } + }); + }; + + $.fn.datetimepicker.defaults = { + format: false, + dayViewHeaderFormat: 'MMMM YYYY', + extraFormats: false, + stepping: 1, + minDate: false, + maxDate: false, + useCurrent: true, + collapse: true, + locale: moment.locale(), + defaultDate: false, + disabledDates: false, + enabledDates: false, + icons: { + time: 'glyphicon glyphicon-time', + date: 'glyphicon glyphicon-calendar', + up: 'glyphicon glyphicon-chevron-up', + down: 'glyphicon glyphicon-chevron-down', + previous: 'glyphicon glyphicon-chevron-left', + next: 'glyphicon glyphicon-chevron-right', + today: 'glyphicon glyphicon-screenshot', + clear: 'glyphicon glyphicon-trash', + close: 'glyphicon glyphicon-remove' + }, + useStrict: false, + sideBySide: false, + daysOfWeekDisabled: [], + calendarWeeks: false, + viewMode: 'days', + toolbarPlacement: 'default', + showTodayButton: false, + showClear: false, + showClose: false, + widgetPositioning: { + horizontal: 'auto', + vertical: 'auto' + }, + widgetParent: null, + ignoreReadonly: false, + keepOpen: false, + inline: false, + keepInvalid: false, + datepickerInput: '.datepickerinput', + keyBinds: { + up: function (widget) { + if (!widget) { + return; + } + var d = this.date() || moment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().subtract(7, 'd')); + } else { + this.date(d.clone().add(1, 'm')); + } + }, + down: function (widget) { + if (!widget) { + this.show(); + return; + } + var d = this.date() || moment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().add(7, 'd')); + } else { + this.date(d.clone().subtract(1, 'm')); + } + }, + 'control up': function (widget) { + if (!widget) { + return; + } + var d = this.date() || moment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().subtract(1, 'y')); + } else { + this.date(d.clone().add(1, 'h')); + } + }, + 'control down': function (widget) { + if (!widget) { + return; + } + var d = this.date() || moment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().add(1, 'y')); + } else { + this.date(d.clone().subtract(1, 'h')); + } + }, + left: function (widget) { + if (!widget) { + return; + } + var d = this.date() || moment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().subtract(1, 'd')); + } + }, + right: function (widget) { + if (!widget) { + return; + } + var d = this.date() || moment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().add(1, 'd')); + } + }, + pageUp: function (widget) { + if (!widget) { + return; + } + var d = this.date() || moment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().subtract(1, 'M')); + } + }, + pageDown: function (widget) { + if (!widget) { + return; + } + var d = this.date() || moment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().add(1, 'M')); + } + }, + enter: function () { + this.hide(); + }, + escape: function () { + this.hide(); + }, + //tab: function (widget) { //this break the flow of the form. disabling for now + // var toggle = widget.find('.picker-switch a[data-action="togglePicker"]'); + // if(toggle.length > 0) toggle.click(); + //}, + 'control space': function (widget) { + if (widget.find('.timepicker').is(':visible')) { + widget.find('.btn[data-action="togglePeriod"]').click(); + } + }, + t: function () { + this.date(moment()); + }, + 'delete': function () { + this.clear(); + } + }, + debug: false + }; +})); diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/src/less/_bootstrap-datetimepicker.less b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/less/_bootstrap-datetimepicker.less new file mode 100755 index 0000000..6caa303 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/less/_bootstrap-datetimepicker.less @@ -0,0 +1,343 @@ +/*! + * Datetimepicker for Bootstrap 3 + * ! version : 4.7.14 + * https://github.com/Eonasdan/bootstrap-datetimepicker/ + */ +@bs-datetimepicker-timepicker-font-size: 1.2em; +@bs-datetimepicker-active-bg: @btn-primary-bg; +@bs-datetimepicker-active-color: @btn-primary-color; +@bs-datetimepicker-border-radius: @border-radius-base; +@bs-datetimepicker-btn-hover-bg: @gray-lighter; +@bs-datetimepicker-disabled-color: @gray-light; +@bs-datetimepicker-alternate-color: @gray-light; +@bs-datetimepicker-secondary-border-color: #ccc; +@bs-datetimepicker-secondary-border-color-rgba: rgba(0, 0, 0, 0.2); +@bs-datetimepicker-primary-border-color: white; +@bs-datetimepicker-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + +.bootstrap-datetimepicker-widget { + list-style: none; + + &.dropdown-menu { + margin: 2px 0; + padding: 4px; + width: 19em; + + &.timepicker-sbs { + @media (min-width: @screen-sm-min) { + width: 38em; + } + + @media (min-width: @screen-md-min) { + width: 38em; + } + + @media (min-width: @screen-lg-min) { + width: 38em; + } + } + + &:before, &:after { + content: ''; + display: inline-block; + position: absolute; + } + + &.bottom { + &:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid @bs-datetimepicker-secondary-border-color; + border-bottom-color: @bs-datetimepicker-secondary-border-color-rgba; + top: -7px; + left: 7px; + } + + &:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid @bs-datetimepicker-primary-border-color; + top: -6px; + left: 8px; + } + } + + &.top { + &:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid @bs-datetimepicker-secondary-border-color; + border-top-color: @bs-datetimepicker-secondary-border-color-rgba; + bottom: -7px; + left: 6px; + } + + &:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid @bs-datetimepicker-primary-border-color; + bottom: -6px; + left: 7px; + } + } + + &.pull-right { + &:before { + left: auto; + right: 6px; + } + + &:after { + left: auto; + right: 7px; + } + } + } + + .list-unstyled { + margin: 0; + } + + a[data-action] { + padding: 6px 0; + } + + a[data-action]:active { + box-shadow: none; + } + + .timepicker-hour, .timepicker-minute, .timepicker-second { + width: 54px; + font-weight: bold; + font-size: @bs-datetimepicker-timepicker-font-size; + margin: 0; + } + + button[data-action] { + padding: 6px; + } + + .btn[data-action="incrementHours"]::after { + .sr-only(); + content: "Increment Hours"; + } + + .btn[data-action="incrementMinutes"]::after { + .sr-only(); + content: "Increment Minutes"; + } + + .btn[data-action="decrementHours"]::after { + .sr-only(); + content: "Decrement Hours"; + } + + .btn[data-action="decrementMinutes"]::after { + .sr-only(); + content: "Decrement Minutes"; + } + + .btn[data-action="showHours"]::after { + .sr-only(); + content: "Show Hours"; + } + + .btn[data-action="showMinutes"]::after { + .sr-only(); + content: "Show Minutes"; + } + + .btn[data-action="togglePeriod"]::after { + .sr-only(); + content: "Toggle AM/PM"; + } + + .btn[data-action="clear"]::after { + .sr-only(); + content: "Clear the picker"; + } + + .btn[data-action="today"]::after { + .sr-only(); + content: "Set the date to today"; + } + + .picker-switch { + text-align: center; + + &::after { + .sr-only(); + content: "Toggle Date and Time Screens"; + } + + td { + padding: 0; + margin: 0; + height: auto; + width: auto; + line-height: inherit; + + span { + line-height: 2.5; + height: 2.5em; + width: 100%; + } + } + } + + table { + width: 100%; + margin: 0; + + + & td, + & th { + text-align: center; + border-radius: @bs-datetimepicker-border-radius; + } + + & th { + height: 20px; + line-height: 20px; + width: 20px; + + &.picker-switch { + width: 145px; + } + + &.disabled, + &.disabled:hover { + background: none; + color: @bs-datetimepicker-disabled-color; + cursor: not-allowed; + } + + &.prev::after { + .sr-only(); + content: "Previous Month"; + } + + &.next::after { + .sr-only(); + content: "Next Month"; + } + } + + & thead tr:first-child th { + cursor: pointer; + + &:hover { + background: @bs-datetimepicker-btn-hover-bg; + } + } + + & td { + height: 54px; + line-height: 54px; + width: 54px; + + &.cw { + font-size: .8em; + height: 20px; + line-height: 20px; + color: @bs-datetimepicker-alternate-color; + } + + &.day { + height: 20px; + line-height: 20px; + width: 20px; + } + + &.day:hover, + &.hour:hover, + &.minute:hover, + &.second:hover { + background: @bs-datetimepicker-btn-hover-bg; + cursor: pointer; + } + + &.old, + &.new { + color: @bs-datetimepicker-alternate-color; + } + + &.today { + position: relative; + + &:before { + content: ''; + display: inline-block; + border: 0 0 7px 7px solid transparent; + border-bottom-color: @bs-datetimepicker-active-bg; + border-top-color: @bs-datetimepicker-secondary-border-color-rgba; + position: absolute; + bottom: 4px; + right: 4px; + } + } + + &.active, + &.active:hover { + background-color: @bs-datetimepicker-active-bg; + color: @bs-datetimepicker-active-color; + text-shadow: @bs-datetimepicker-text-shadow; + } + + &.active.today:before { + border-bottom-color: #fff; + } + + &.disabled, + &.disabled:hover { + background: none; + color: @bs-datetimepicker-disabled-color; + cursor: not-allowed; + } + + span { + display: inline-block; + width: 54px; + height: 54px; + line-height: 54px; + margin: 2px 1.5px; + cursor: pointer; + border-radius: @bs-datetimepicker-border-radius; + + &:hover { + background: @bs-datetimepicker-btn-hover-bg; + } + + &.active { + background-color: @bs-datetimepicker-active-bg; + color: @bs-datetimepicker-active-color; + text-shadow: @bs-datetimepicker-text-shadow; + } + + &.old { + color: @bs-datetimepicker-alternate-color; + } + + &.disabled, + &.disabled:hover { + background: none; + color: @bs-datetimepicker-disabled-color; + cursor: not-allowed; + } + } + } + } + + &.usetwentyfour { + td.hour { + height: 27px; + line-height: 27px; + } + } +} + +.input-group.date { + & .input-group-addon { + cursor: pointer; + } +} diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/src/less/bootstrap-datetimepicker-build.less b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/less/bootstrap-datetimepicker-build.less new file mode 100755 index 0000000..c7a43cf --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/less/bootstrap-datetimepicker-build.less @@ -0,0 +1,17 @@ +// Import boostrap variables including default color palette and fonts +@import "../../node_modules/bootstrap/less/variables.less"; + +// Import datepicker component +@import "_bootstrap-datetimepicker.less"; + + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; +} \ No newline at end of file diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/Bootstrap.v3.Datetimepicker.CSS.nuspec b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/Bootstrap.v3.Datetimepicker.CSS.nuspec new file mode 100755 index 0000000..d8b86a9 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/Bootstrap.v3.Datetimepicker.CSS.nuspec @@ -0,0 +1,30 @@ + + + + Bootstrap.v3.Datetimepicker.CSS + 4.0.0 + Bootstrap 3 Datetimepicker CSS + Eonasdan + Eonasdan + https://github.com/Eonasdan/bootstrap-datetimepicker + false + A date/time picker component designed to work with Bootstrap 3 and Momentjs. + +For usage, installation and demos see Project Site on GitHub + +For LESS version install Bootstrap.v3.Datetimepicker + Check the change log on Github at https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Change-Log + bootstrap date time picker datetimepicker datepicker jquery + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/Bootstrap.v3.Datetimepicker.nuspec b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/Bootstrap.v3.Datetimepicker.nuspec new file mode 100755 index 0000000..a238feb --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/Bootstrap.v3.Datetimepicker.nuspec @@ -0,0 +1,30 @@ + + + + Bootstrap.v3.Datetimepicker + 4.0.0 + Bootstrap 3 Datetimepicker + Eonasdan + Eonasdan + https://github.com/Eonasdan/bootstrap-datetimepicker + false + A date/time picker component designed to work with Bootstrap 3 and Momentjs. + +For usage, installation and demos see Project Site on GitHub + +For CSS version install Bootstrap.v3.Datetimepicker.CSS + Check the change log on Github at https://github.com/Eonasdan/bootstrap-datetimepicker/wiki/Change-Log + bootstrap date time picker datetimepicker datepicker jquery + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/NuGet.exe b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/NuGet.exe new file mode 100755 index 0000000..c41a0d0 Binary files /dev/null and b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/NuGet.exe differ diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/install.ps1 b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/install.ps1 new file mode 100755 index 0000000..87c030f --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/src/nuget/install.ps1 @@ -0,0 +1,2 @@ +# install.ps1 +$DTE.ItemOperations.Navigate("http://eonasdan.github.io/bootstrap-datetimepicker/", $DTE.vsNavigateOptions.vsNavigateOptionsNewWindow) diff --git a/www/bower_components/eonasdan-bootstrap-datetimepicker/tasks/bump_version.js b/www/bower_components/eonasdan-bootstrap-datetimepicker/tasks/bump_version.js new file mode 100755 index 0000000..a4b8d57 --- /dev/null +++ b/www/bower_components/eonasdan-bootstrap-datetimepicker/tasks/bump_version.js @@ -0,0 +1,88 @@ +module.exports = function (grunt) { + grunt.registerTask('bump_version', function (version) { + if (!version || version.split('.').length !== 3) { + grunt.fail.fatal("malformed version. Use\n\n grunt bump_version:1.2.3") + } + + grunt.config('string-replace.bootstrap-datetimepicker-js', { + files: {'src/js/bootstrap-datetimepicker.js': 'src/js/bootstrap-datetimepicker.js'}, + options: { + replacements: [ + { + pattern: /\/\/! version : .*/, + replacement: '//! version : ' + version + } + ] + } + }); + + grunt.config('string-replace.bootstrap-datetimepicker-css', { + files: {'src/less/bootstrap-datetimepicker.less': 'src/less/bootstrap-datetimepicker.less'}, + options: { + replacements: [ + { + pattern: /\/\/! version : .*/, + replacement: '//! version : ' + version + } + ] + } + }); + + grunt.config('string-replace.package-json', { + files: {'package.json': 'package.json'}, + options: { + replacements: [ + { + pattern: /"version": .*/, + replacement: '"version": "' + version + '",' + } + ] + } + }); + + grunt.config('string-replace.bower-json', { + files: {'bower.json': 'bower.json'}, + options: { + replacements: [ + { + pattern: /"version": .*/, + replacement: '"version": "' + version + '",' + } + ] + } + }); + + grunt.config('string-replace.component-json', { + files: {'component.json': 'component.json'}, + options: { + replacements: [ + { + pattern: /"version": .*/, + replacement: '"version": "' + version + '",' + } + ] + } + }); + + grunt.config('string-replace.composer-json', { + files: {'composer.json': 'composer.json'}, + options: { + replacements: [ + { + pattern: /"version": .*/, + replacement: '"version": "' + version + '",' + } + ] + } + }); + + grunt.task.run([ + 'string-replace:bootstrap-datetimepicker-js', + 'string-replace:bootstrap-datetimepicker-css', + 'string-replace:package-json', + 'string-replace:bower-json', + 'string-replace:component-json', + 'string-replace:composer-json' + ]); + }); +}; diff --git a/www/bower_components/jquery/.bower.json b/www/bower_components/jquery/.bower.json new file mode 100755 index 0000000..ea4b7f6 --- /dev/null +++ b/www/bower_components/jquery/.bower.json @@ -0,0 +1,25 @@ +{ + "name": "jquery", + "main": "dist/jquery.js", + "license": "MIT", + "ignore": [ + "package.json" + ], + "keywords": [ + "jquery", + "javascript", + "browser", + "library" + ], + "homepage": "https://github.com/jquery/jquery-dist", + "version": "3.1.0", + "_release": "3.1.0", + "_resolution": { + "type": "version", + "tag": "3.1.0", + "commit": "6f02bc382c0529d3b4f68f6b2ad21876642dbbfe" + }, + "_source": "https://github.com/jquery/jquery-dist.git", + "_target": ">=1.7", + "_originalSource": "jquery" +} \ No newline at end of file diff --git a/www/bower_components/jquery/AUTHORS.txt b/www/bower_components/jquery/AUTHORS.txt new file mode 100644 index 0000000..ed15d3d --- /dev/null +++ b/www/bower_components/jquery/AUTHORS.txt @@ -0,0 +1,295 @@ +Authors ordered by first contribution. + +John Resig +Gilles van den Hoven +Michael Geary +Stefan Petre +Yehuda Katz +Corey Jewett +Klaus Hartl +Franck Marcia +Jörn Zaefferer +Paul Bakaus +Brandon Aaron +Mike Alsup +Dave Methvin +Ed Engelhardt +Sean Catchpole +Paul Mclanahan +David Serduke +Richard D. Worth +Scott González +Ariel Flesler +Jon Evans +TJ Holowaychuk +Michael Bensoussan +Robert Katić +Louis-Rémi Babé +Earle Castledine +Damian Janowski +Rich Dougherty +Kim Dalsgaard +Andrea Giammarchi +Mark Gibson +Karl Swedberg +Justin Meyer +Ben Alman +James Padolsey +David Petersen +Batiste Bieler +Alexander Farkas +Rick Waldron +Filipe Fortes +Neeraj Singh +Paul Irish +Iraê Carvalho +Matt Curry +Michael Monteleone +Noah Sloan +Tom Viner +Douglas Neiner +Adam J. Sontag +Dave Reed +Ralph Whitbeck +Carl Fürstenberg +Jacob Wright +J. Ryan Stinnett +unknown +temp01 +Heungsub Lee +Colin Snover +Ryan W Tenney +Pinhook +Ron Otten +Jephte Clain +Anton Matzneller +Alex Sexton +Dan Heberden +Henri Wiechers +Russell Holbrook +Julian Aubourg +Gianni Alessandro Chiappetta +Scott Jehl +James Burke +Jonas Pfenniger +Xavi Ramirez +Jared Grippe +Sylvester Keil +Brandon Sterne +Mathias Bynens +Timmy Willison +Corey Frang +Digitalxero +Anton Kovalyov +David Murdoch +Josh Varner +Charles McNulty +Jordan Boesch +Jess Thrysoee +Michael Murray +Lee Carpenter +Alexis Abril +Rob Morgan +John Firebaugh +Sam Bisbee +Gilmore Davidson +Brian Brennan +Xavier Montillet +Daniel Pihlstrom +Sahab Yazdani +avaly +Scott Hughes +Mike Sherov +Greg Hazel +Schalk Neethling +Denis Knauf +Timo Tijhof +Steen Nielsen +Anton Ryzhov +Shi Chuan +Berker Peksag +Toby Brain +Matt Mueller +Justin +Daniel Herman +Oleg Gaidarenko +Richard Gibson +Rafaël Blais Masson +cmc3cn <59194618@qq.com> +Joe Presbrey +Sindre Sorhus +Arne de Bree +Vladislav Zarakovsky +Andrew E Monat +Oskari +Joao Henrique de Andrade Bruni +tsinha +Matt Farmer +Trey Hunner +Jason Moon +Jeffery To +Kris Borchers +Vladimir Zhuravlev +Jacob Thornton +Chad Killingsworth +Nowres Rafid +David Benjamin +Uri Gilad +Chris Faulkner +Elijah Manor +Daniel Chatfield +Nikita Govorov +Wesley Walser +Mike Pennisi +Markus Staab +Dave Riddle +Callum Macrae +Benjamin Truyman +James Huston +Erick Ruiz de Chávez +David Bonner +Akintayo Akinwunmi +MORGAN +Ismail Khair +Carl Danley +Mike Petrovich +Greg Lavallee +Daniel Gálvez +Sai Lung Wong +Tom H Fuertes +Roland Eckl +Jay Merrifield +Allen J Schmidt Jr +Jonathan Sampson +Marcel Greter +Matthias Jäggli +David Fox +Yiming He +Devin Cooper +Paul Ramos +Rod Vagg +Bennett Sorbo +Sebastian Burkhard +Zachary Adam Kaplan +nanto_vi +nanto +Danil Somsikov +Ryunosuke SATO +Jean Boussier +Adam Coulombe +Andrew Plummer +Mark Raddatz +Isaac Z. Schlueter +Karl Sieburg +Pascal Borreli +Nguyen Phuc Lam +Dmitry Gusev +Michał Gołębiowski +Li Xudong +Steven Benner +Tom H Fuertes +Renato Oliveira dos Santos +ros3cin +Jason Bedard +Kyle Robinson Young +Chris Talkington +Eddie Monge +Terry Jones +Jason Merino +Jeremy Dunck +Chris Price +Guy Bedford +Amey Sakhadeo +Mike Sidorov +Anthony Ryan +Dominik D. Geyer +George Kats +Lihan Li +Ronny Springer +Chris Antaki +Marian Sollmann +njhamann +Ilya Kantor +David Hong +John Paul +Jakob Stoeck +Christopher Jones +Forbes Lindesay +S. Andrew Sheppard +Leonardo Balter +Roman Reiß +Benjy Cui +Rodrigo Rosenfeld Rosas +John Hoven +Philip Jägenstedt +Christian Kosmowski +Liang Peng +TJ VanToll +Senya Pugach +Aurelio De Rosa +Nazar Mokrynskyi +Amit Merchant +Jason Bedard +Arthur Verschaeve +Dan Hart +Bin Xin +David Corbacho +Veaceslav Grimalschi +Daniel Husar +Frederic Hemberger +Ben Toews +Aditya Raghavan +Victor Homyakov +Shivaji Varma +Nicolas HENRY +Anne-Gaelle Colom +George Mauer +Leonardo Braga +Stephen Edgar +Thomas Tortorini +Winston Howes +Jon Hester +Alexander O'Mara +Bastian Buchholz +Arthur Stolyar +Calvin Metcalf +Mu Haibao +Richard McDaniel +Chris Rebert +Gabriel Schulhof +Gilad Peleg +Martin Naumann +Marek Lewandowski +Bruno Pérel +Reed Loden +Daniel Nill +Yongwoo Jeon +Sean Henderson +Richard Kraaijenhagen +Connor Atherton +Gary Ye +Christian Grete +Liza Ramo +Julian Alexander Murillo +Joelle Fleurantin +Jae Sung Park +Jun Sun +Josh Soref +Henry Wong +Jon Dufresne +Martijn W. van der Lee +Devin Wilson +Steve Mao +Zack Hall +Bernhard M. Wiedemann +Todor Prikumov +Jha Naman +William Robinet +Alexander Lisianoi +Vitaliy Terziev +Joe Trumbull +Alexander K +Damian Senn +Ralin Chimev +Felipe Sateler +Christophe Tafani-Dereeper diff --git a/www/bower_components/jquery/LICENSE.txt b/www/bower_components/jquery/LICENSE.txt new file mode 100644 index 0000000..9d2b18c --- /dev/null +++ b/www/bower_components/jquery/LICENSE.txt @@ -0,0 +1,36 @@ +Copyright jQuery Foundation and other contributors, https://jquery.org/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. diff --git a/www/bower_components/jquery/MIT-LICENSE.txt b/www/bower_components/jquery/MIT-LICENSE.txt new file mode 100755 index 0000000..4d4a8e2 --- /dev/null +++ b/www/bower_components/jquery/MIT-LICENSE.txt @@ -0,0 +1,21 @@ +Copyright 2014 jQuery Foundation and other contributors +http://jquery.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/www/bower_components/jquery/README.md b/www/bower_components/jquery/README.md new file mode 100644 index 0000000..619df43 --- /dev/null +++ b/www/bower_components/jquery/README.md @@ -0,0 +1,65 @@ +# jQuery + +> jQuery is a fast, small, and feature-rich JavaScript library. + +For information on how to get started and how to use jQuery, please see [jQuery's documentation](http://api.jquery.com/). +For source files and issues, please visit the [jQuery repo](https://github.com/jquery/jquery). + +## Including jQuery + +Below are some of the most common ways to include jQuery. + +### Browser + +#### Script tag + +```html + +``` + +#### Babel + +[Babel](http://babeljs.io/) is a next generation JavaScript compiler. One of the features is the ability to use ES6/ES2015 modules now, even though browsers do not yet support this feature natively. + +```js +import $ from "jquery"; +``` + +#### Browserify/Webpack + +There are several ways to use [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). For more information on using these tools, please refer to the corresponding project's documention. In the script, including jQuery will usually look like this... + +```js +var $ = require("jquery"); +``` + +#### AMD (Asynchronous Module Definition) + +AMD is a module format built for the browser. For more information, we recommend [require.js' documentation](http://requirejs.org/docs/whyamd.html). + +```js +define(["jquery"], function($) { + +}); +``` + +### Node + +To include jQuery in [Node](nodejs.org), first install with npm. + +```sh +npm install jquery +``` + +For jQuery to work in Node, a window with a document is required. Since no such window exists natively in Node, one can be mocked by tools such as [jsdom](https://github.com/tmpvar/jsdom). This can be useful for testing purposes. + +```js +require("jsdom").env("", function(err, window) { + if (err) { + console.error(err); + return; + } + + var $ = require("jquery")(window); +}); +``` diff --git a/www/bower_components/jquery/bower.json b/www/bower_components/jquery/bower.json new file mode 100755 index 0000000..3ff8453 --- /dev/null +++ b/www/bower_components/jquery/bower.json @@ -0,0 +1,14 @@ +{ + "name": "jquery", + "main": "dist/jquery.js", + "license": "MIT", + "ignore": [ + "package.json" + ], + "keywords": [ + "jquery", + "javascript", + "browser", + "library" + ] +} \ No newline at end of file diff --git a/www/bower_components/jquery/dist/core.js b/www/bower_components/jquery/dist/core.js new file mode 100644 index 0000000..f8fea62 --- /dev/null +++ b/www/bower_components/jquery/dist/core.js @@ -0,0 +1,481 @@ +/* global Symbol */ +// Defining this global in .eslintrc would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + +define( [ + "./var/arr", + "./var/document", + "./var/getProto", + "./var/slice", + "./var/concat", + "./var/push", + "./var/indexOf", + "./var/class2type", + "./var/toString", + "./var/hasOwn", + "./var/fnToString", + "./var/ObjectFunctionString", + "./var/support", + "./core/DOMEval" +], function( arr, document, getProto, slice, concat, push, indexOf, + class2type, toString, hasOwn, fnToString, ObjectFunctionString, + support, DOMEval ) { + +"use strict"; + +var + version = "3.1.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); + }, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE <=9 - 11, Edge 12 - 13 + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} + +return jQuery; +} ); diff --git a/www/bower_components/jquery/dist/jquery.js b/www/bower_components/jquery/dist/jquery.js new file mode 100755 index 0000000..530037e --- /dev/null +++ b/www/bower_components/jquery/dist/jquery.js @@ -0,0 +1,10074 @@ +/*eslint-disable no-unused-vars*/ +/*! + * jQuery JavaScript Library v3.1.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2016-07-07T21:44Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + + + + function DOMEval( code, doc ) { + doc = doc || document; + + var script = doc.createElement( "script" ); + + script.text = code; + doc.head.appendChild( script ).parentNode.removeChild( script ); + } +/* global Symbol */ +// Defining this global in .eslintrc would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.1.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); + }, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE <=9 - 11, Edge 12 - 13 + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.0 + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-01-04 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + // Known :disabled false positives: + // IE: *[disabled]:not(button, input, select, textarea, optgroup, option, menuitem, fieldset) + // not IE: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Check form elements and option elements for explicit disabling + return "label" in elem && elem.disabled === disabled || + "form" in elem && elem.disabled === disabled || + + // Check non-disabled form elements for fieldset[disabled] ancestors + "form" in elem && elem.disabled === false && ( + // Support: IE6-11+ + // Ancestry is covered for us + elem.isDisabled === disabled || + + // Otherwise, assume any non- or other required elements. + thead: [ 1, "
    ').addClass('cw').text('#')); + } + + while (currentDate.isBefore(viewDate.clone().endOf('w'))) { + row.append($('').addClass('dow').text(currentDate.format('dd'))); + currentDate.add(1, 'd'); + } + widget.find('.datepicker-days thead').append(row); + }, + + isInDisabledDates = function (testDate) { + return options.disabledDates[testDate.format('YYYY-MM-DD')] === true; + }, + + isInEnabledDates = function (testDate) { + return options.enabledDates[testDate.format('YYYY-MM-DD')] === true; + }, + + isValid = function (targetMoment, granularity) { + if (!targetMoment.isValid()) { + return false; + } + if (options.disabledDates && isInDisabledDates(targetMoment) && granularity !== 'M') { + return false; + } + if (options.enabledDates && !isInEnabledDates(targetMoment) && granularity !== 'M') { + return false; + } + if (options.minDate && targetMoment.isBefore(options.minDate, granularity)) { + return false; + } + if (options.maxDate && targetMoment.isAfter(options.maxDate, granularity)) { + return false; + } + if (granularity === 'd' && options.daysOfWeekDisabled.indexOf(targetMoment.day()) !== -1) { //widget && widget.find('.datepicker-days').length > 0 + return false; + } + return true; + }, + + fillMonths = function () { + var spans = [], + monthsShort = viewDate.clone().startOf('y').hour(12); // hour is changed to avoid DST issues in some browsers + while (monthsShort.isSame(viewDate, 'y')) { + spans.push($('').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM'))); + monthsShort.add(1, 'M'); + } + widget.find('.datepicker-months td').empty().append(spans); + }, + + updateMonths = function () { + var monthsView = widget.find('.datepicker-months'), + monthsViewHeader = monthsView.find('th'), + months = monthsView.find('tbody').find('span'); + + monthsView.find('.disabled').removeClass('disabled'); + + if (!isValid(viewDate.clone().subtract(1, 'y'), 'y')) { + monthsViewHeader.eq(0).addClass('disabled'); + } + + monthsViewHeader.eq(1).text(viewDate.year()); + + if (!isValid(viewDate.clone().add(1, 'y'), 'y')) { + monthsViewHeader.eq(2).addClass('disabled'); + } + + months.removeClass('active'); + if (date.isSame(viewDate, 'y')) { + months.eq(date.month()).addClass('active'); + } + + months.each(function (index) { + if (!isValid(viewDate.clone().month(index), 'M')) { + $(this).addClass('disabled'); + } + }); + }, + + updateYears = function () { + var yearsView = widget.find('.datepicker-years'), + yearsViewHeader = yearsView.find('th'), + startYear = viewDate.clone().subtract(5, 'y'), + endYear = viewDate.clone().add(6, 'y'), + html = ''; + + yearsView.find('.disabled').removeClass('disabled'); + + if (options.minDate && options.minDate.isAfter(startYear, 'y')) { + yearsViewHeader.eq(0).addClass('disabled'); + } + + yearsViewHeader.eq(1).text(startYear.year() + '-' + endYear.year()); + + if (options.maxDate && options.maxDate.isBefore(endYear, 'y')) { + yearsViewHeader.eq(2).addClass('disabled'); + } + + while (!startYear.isAfter(endYear, 'y')) { + html += '' + startYear.year() + ''; + startYear.add(1, 'y'); + } + + yearsView.find('td').html(html); + }, + + fillDate = function () { + var daysView = widget.find('.datepicker-days'), + daysViewHeader = daysView.find('th'), + currentDate, + html = [], + row, + clsName; + + if (!hasDate()) { + return; + } + + daysView.find('.disabled').removeClass('disabled'); + daysViewHeader.eq(1).text(viewDate.format(options.dayViewHeaderFormat)); + + if (!isValid(viewDate.clone().subtract(1, 'M'), 'M')) { + daysViewHeader.eq(0).addClass('disabled'); + } + if (!isValid(viewDate.clone().add(1, 'M'), 'M')) { + daysViewHeader.eq(2).addClass('disabled'); + } + + currentDate = viewDate.clone().startOf('M').startOf('week'); + + while (!viewDate.clone().endOf('M').endOf('w').isBefore(currentDate, 'd')) { + if (currentDate.weekday() === 0) { + row = $('
    ' + currentDate.week() + '' + currentDate.date() + '
    ' + currentHour.format(use24Hours ? 'HH' : 'hh') + '
    ' + currentMinute.format('mm') + '
    ' + currentSecond.format('ss') + '
    ", "
    " ], + col: [ 2, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== "undefined" ? + context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); +var documentElement = document.documentElement; + + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 only +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Support: IE <=9 + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // + // Support: Firefox <=42 + // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) + if ( delegateCount && cur.nodeType && + ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push( { elem: cur, handlers: matches } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: jQuery.isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + return ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event.which; + } +}, jQuery.event.addProp ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +function manipulationTarget( elem, content ) { + if ( jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return elem.getElementsByTagName( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rmargin = ( /^margin/ ); + +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + div.style.cssText = + "box-sizing:border-box;" + + "position:relative;display:block;" + + "margin:auto;border:1px;padding:1px;" + + "top:1%;width:50%"; + div.innerHTML = ""; + documentElement.appendChild( container ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = divStyle.marginLeft === "2px"; + boxSizingReliableVal = divStyle.width === "4px"; + + // Support: Android 4.0 - 4.3 only + // Some styles come back with percentage values, even though they shouldn't + div.style.marginRight = "50%"; + pixelMarginRightVal = divStyle.marginRight === "4px"; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" + + "padding:0;margin-top:1px;position:absolute"; + container.appendChild( div ); + + jQuery.extend( support, { + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelMarginRight: function() { + computeStyleTests(); + return pixelMarginRightVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + style = elem.style; + + computed = computed || getStyles( elem ); + + // Support: IE <=9 only + // getPropertyValue is only needed for .css('filter') (#12537) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + + cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style; + +// Return a css property mapped to a potentially vendor prefixed property +function vendorPropName( name ) { + + // Shortcut for names that are not vendor prefixed + if ( name in emptyStyle ) { + return name; + } + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +function setPositiveNumber( elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + + // If we already have the right measurement, avoid augmentation + 4 : + + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // At this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + + // At this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // At this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var val, + valueIsBorderBox = true, + styles = getStyles( elem ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + if ( elem.getClientRects().length ) { + val = elem.getBoundingClientRect()[ name ]; + } + + // Some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test( val ) ) { + return val; + } + + // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && + ( support.boxSizingReliable() || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // Use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + "float": "cssFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || + ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName ); + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + if ( type === "number" ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + style[ name ] = value; + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || + ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName ); + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + } ) : + getWidthOrHeight( elem, name, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = extra && getStyles( elem ), + subtract = extra && augmentWidthOrHeight( + elem, + name, + extra, + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + styles + ); + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ name ] = value; + value = jQuery.css( elem, name ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( !rmargin.test( prefix ) ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && + ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || + jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, timerId, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function raf() { + if ( timerId ) { + window.requestAnimationFrame( raf ); + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = jQuery.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 13 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = jQuery.camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( jQuery.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + if ( percent < 1 && length ) { + return remaining; + } else { + deferred.resolveWith( elem, [ animation ] ); + return false; + } + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( jQuery.isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + jQuery.proxy( result.stop, result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( jQuery.isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + // attach callbacks from options + return animation.progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnotwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + // Go to the end state if fx are off or if document is hidden + if ( jQuery.fx.off || document.hidden ) { + opt.duration = 0; + + } else { + opt.duration = typeof opt.duration === "number" ? + opt.duration : opt.duration in jQuery.fx.speeds ? + jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = jQuery.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + if ( timer() ) { + jQuery.fx.start(); + } else { + jQuery.timers.pop(); + } +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( !timerId ) { + timerId = window.requestAnimationFrame ? + window.requestAnimationFrame( raf ) : + window.setInterval( jQuery.fx.tick, jQuery.fx.interval ); + } +}; + +jQuery.fx.stop = function() { + if ( window.cancelAnimationFrame ) { + window.cancelAnimationFrame( timerId ); + } else { + window.clearInterval( timerId ); + } + + timerId = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + jQuery.nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + attrNames = value && value.match( rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + return tabindex ? + parseInt( tabindex, 10 ) : + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && elem.href ? + 0 : + -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + +var rclass = /[\t\r\n\f]/g; + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( jQuery.isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( typeof value === "string" && value ) { + classes = value.match( rnotwhite ) || []; + + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && + ( " " + curValue + " " ).replace( rclass, " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = jQuery.trim( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( jQuery.isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + if ( typeof value === "string" && value ) { + classes = value.match( rnotwhite ) || []; + + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && + ( " " + curValue + " " ).replace( rclass, " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = jQuery.trim( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value; + + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( jQuery.isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( type === "string" ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = value.match( rnotwhite ) || []; + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + getClass( elem ) + " " ).replace( rclass, " " ) + .indexOf( className ) > -1 + ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g, + rspaces = /[\x20\t\r\n\f]+/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, isFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + + // Handle most common string cases + ret.replace( rreturn, "" ) : + + // Handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + jQuery.trim( jQuery.text( elem ) ).replace( rspaces, " " ); + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup contextmenu" ).split( " " ), + function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; +} ); + +jQuery.fn.extend( { + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +} ); + + + + +support.focusin = "onfocusin" in window; + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = jQuery.now(); + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( jQuery.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = jQuery.isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( i, elem ) { + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rts = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || []; + + if ( jQuery.isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match == null ? null : match; + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 13 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available, append data to url + if ( s.data ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add anti-cache in uncached url if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rts, "" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + + +jQuery._evalUrl = function( url ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + "throws": true + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( jQuery.isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + +
    +
    +
    + + +
    +
    +
    + + + + diff --git a/www/bower_components/typeahead.js/test/integration/test.js b/www/bower_components/typeahead.js/test/integration/test.js new file mode 100644 index 0000000..37b23a6 --- /dev/null +++ b/www/bower_components/typeahead.js/test/integration/test.js @@ -0,0 +1,395 @@ +/* jshint esnext: true, evil: true, sub: true */ + +var wd = require('yiewd'), + colors = require('colors'), + expect = require('chai').expect, + _ = require('underscore'), + f = require('util').format, + env = process.env; + +var browser, caps; + +browser = (process.env.BROWSER || 'chrome').split(':'); + +caps = { + name: f('[%s] typeahead.js ui', browser.join(' , ')), + browserName: browser[0] +}; + +setIf(caps, 'version', browser[1]); +setIf(caps, 'platform', browser[2]); +setIf(caps, 'tunnel-identifier', env['TRAVIS_JOB_NUMBER']); +setIf(caps, 'build', env['TRAVIS_BUILD_NUMBER']); +setIf(caps, 'tags', env['CI'] ? ['CI'] : ['local']); + +function setIf(obj, key, val) { + val && (obj[key] = val); +} + +describe('jquery-typeahead.js', function() { + var driver, body, input, hint, dropdown, allPassed = true; + + this.timeout(300000); + + before(function(done) { + var host = 'ondemand.saucelabs.com', port = 80, username, password; + + if (env['CI']) { + host = 'localhost'; + port = 4445; + username = env['SAUCE_USERNAME']; + password = env['SAUCE_ACCESS_KEY']; + } + + driver = wd.remote(host, port, username, password); + driver.configureHttp({ + timeout: 30000, + retries: 5, + retryDelay: 200 + }); + + driver.on('status', function(info) { + console.log(info.cyan); + }); + + driver.on('command', function(meth, path, data) { + console.log(' > ' + meth.yellow, path.grey, data || ''); + }); + + driver.run(function*() { + yield this.init(caps); + yield this.get('http://localhost:8888/test/integration/test.html'); + + body = yield this.elementByTagName('body'); + input = yield this.elementById('states'); + hint = yield this.elementByClassName('tt-hint'); + dropdown = yield this.elementByClassName('tt-menu'); + + done(); + }); + }); + + afterEach(function(done) { + allPassed = allPassed && (this.currentTest.state === 'passed'); + + driver.run(function*() { + yield body.click(); + yield this.execute('window.jQuery("#states").typeahead("val", "")'); + done(); + }); + }); + + after(function(done) { + driver.run(function*() { + yield this.quit(); + yield driver.sauceJobStatus(allPassed); + done(); + }); + }); + + describe('on blur', function() { + it('should close dropdown', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + expect(yield dropdown.isDisplayed()).to.equal(true); + + yield body.click(); + expect(yield dropdown.isDisplayed()).to.equal(false); + + done(); + }); + }); + + it('should clear hint', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + expect(yield hint.getValue()).to.equal('michigan'); + + yield body.click(); + expect(yield hint.getValue()).to.equal(''); + + done(); + }); + }); + }); + + describe('on query change', function() { + it('should open dropdown if suggestions', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + + expect(yield dropdown.isDisplayed()).to.equal(true); + + done(); + }); + }); + + it('should close dropdown if no suggestions', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('huh?'); + + expect(yield dropdown.isDisplayed()).to.equal(false); + + done(); + }); + }); + + it('should render suggestions if suggestions', function(done) { + driver.run(function*() { + var suggestions; + + yield input.click(); + yield input.type('mi'); + + suggestions = yield dropdown.elementsByClassName('tt-suggestion'); + + expect(suggestions).to.have.length('4'); + expect(yield suggestions[0].text()).to.equal('Michigan'); + expect(yield suggestions[1].text()).to.equal('Minnesota'); + expect(yield suggestions[2].text()).to.equal('Mississippi'); + expect(yield suggestions[3].text()).to.equal('Missouri'); + + done(); + }); + }); + + it('should show hint if top suggestion is a match', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + + expect(yield hint.getValue()).to.equal('michigan'); + + done(); + }); + }); + + it('should match hint to query', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('NeW JE'); + + expect(yield hint.getValue()).to.equal('NeW JErsey'); + + done(); + }); + }); + + it('should not show hint if top suggestion is not a match', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('ham'); + + expect(yield hint.getValue()).to.equal(''); + + done(); + }); + }); + + it('should not show hint if there is query overflow', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('this is a very long value so '); + + expect(yield hint.getValue()).to.equal(''); + + done(); + }); + }); + }); + + describe('on up arrow', function() { + it('should cycle through suggestions', function(done) { + driver.run(function*() { + var suggestions; + + yield input.click(); + yield input.type('mi'); + + suggestions = yield dropdown.elementsByClassName('tt-suggestion'); + + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('Missouri'); + expect(yield suggestions[3].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor'); + + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('Mississippi'); + expect(yield suggestions[2].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor'); + + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('Minnesota'); + expect(yield suggestions[1].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor'); + + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('Michigan'); + expect(yield suggestions[0].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor'); + + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('mi'); + expect(yield suggestions[0].getAttribute('class')).to.equal('tt-suggestion tt-selectable'); + expect(yield suggestions[1].getAttribute('class')).to.equal('tt-suggestion tt-selectable'); + expect(yield suggestions[2].getAttribute('class')).to.equal('tt-suggestion tt-selectable'); + expect(yield suggestions[3].getAttribute('class')).to.equal('tt-suggestion tt-selectable'); + + done(); + }); + }); + }); + + describe('on down arrow', function() { + it('should cycle through suggestions', function(done) { + driver.run(function*() { + var suggestions; + + yield input.click(); + yield input.type('mi'); + + suggestions = yield dropdown.elementsByClassName('tt-suggestion'); + + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('Michigan'); + expect(yield suggestions[0].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor'); + + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('Minnesota'); + expect(yield suggestions[1].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor'); + + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('Mississippi'); + expect(yield suggestions[2].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor'); + + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('Missouri'); + expect(yield suggestions[3].getAttribute('class')).to.equal('tt-suggestion tt-selectable tt-cursor'); + + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('mi'); + expect(yield suggestions[0].getAttribute('class')).to.equal('tt-suggestion tt-selectable'); + expect(yield suggestions[1].getAttribute('class')).to.equal('tt-suggestion tt-selectable'); + expect(yield suggestions[2].getAttribute('class')).to.equal('tt-suggestion tt-selectable'); + expect(yield suggestions[3].getAttribute('class')).to.equal('tt-suggestion tt-selectable'); + + done(); + }); + }); + }); + + describe('on escape', function() { + it('should close dropdown', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + expect(yield dropdown.isDisplayed()).to.equal(true); + + yield input.type(wd.SPECIAL_KEYS['Escape']); + expect(yield dropdown.isDisplayed()).to.equal(false); + + done(); + }); + }); + + it('should clear hint', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + expect(yield hint.getValue()).to.equal('michigan'); + + yield input.type(wd.SPECIAL_KEYS['Escape']); + expect(yield hint.getValue()).to.equal(''); + + done(); + }); + }); + }); + + describe('on tab', function() { + it('should autocomplete if hint is present', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + + yield input.type(wd.SPECIAL_KEYS['Tab']); + expect(yield input.getValue()).to.equal('Michigan'); + + done(); + }); + }); + + it('should select if cursor is on suggestion', function(done) { + driver.run(function*() { + var suggestions; + + yield input.click(); + yield input.type('mi'); + + suggestions = yield dropdown.elementsByClassName('tt-suggestion'); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + yield input.type(wd.SPECIAL_KEYS['Tab']); + + expect(yield dropdown.isDisplayed()).to.equal(false); + expect(yield input.getValue()).to.equal('Minnesota'); + + done(); + }); + }); + }); + + describe('on right arrow', function() { + it('should autocomplete if hint is present', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + + yield input.type(wd.SPECIAL_KEYS['Right arrow']); + expect(yield input.getValue()).to.equal('Michigan'); + + done(); + }); + }); + }); + + describe('on suggestion click', function() { + it('should select suggestion', function(done) { + driver.run(function*() { + var suggestions; + + yield input.click(); + yield input.type('mi'); + + suggestions = yield dropdown.elementsByClassName('tt-suggestion'); + yield suggestions[1].click(); + + expect(yield dropdown.isDisplayed()).to.equal(false); + expect(yield input.getValue()).to.equal('Minnesota'); + + done(); + }); + }); + }); + + describe('on enter', function() { + it('should select if cursor is on suggestion', function(done) { + driver.run(function*() { + var suggestions; + + yield input.click(); + yield input.type('mi'); + + suggestions = yield dropdown.elementsByClassName('tt-suggestion'); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + yield input.type(wd.SPECIAL_KEYS['Return']); + + expect(yield dropdown.isDisplayed()).to.equal(false); + expect(yield input.getValue()).to.equal('Minnesota'); + + done(); + }); + }); + }); +}); diff --git a/www/bower_components/typeahead.js/test/playground.html b/www/bower_components/typeahead.js/test/playground.html new file mode 100644 index 0000000..8dc2c56 --- /dev/null +++ b/www/bower_components/typeahead.js/test/playground.html @@ -0,0 +1,346 @@ + + + + + + + + + + +
    + +
    +
    + + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + + + + + diff --git a/www/bower_components/typeahead.js/test/typeahead/dataset_spec.js b/www/bower_components/typeahead.js/test/typeahead/dataset_spec.js new file mode 100644 index 0000000..bf16503 --- /dev/null +++ b/www/bower_components/typeahead.js/test/typeahead/dataset_spec.js @@ -0,0 +1,469 @@ +describe('Dataset', function() { + var www = WWW(), mockSuggestions, mockSuggestionsDisplayFn; + + mockSuggestions = [ + { value: 'one', raw: { value: 'one' } }, + { value: 'two', raw: { value: 'two' } }, + { value: 'html', raw: { value: 'html' } } + ]; + + mockSuggestionsDisplayFn = [ + { display: '4' }, + { display: '5' }, + { display: '6' } + ]; + + beforeEach(function() { + this.dataset = new Dataset({ + name: 'test', + node: $('
    '), + source: this.source = jasmine.createSpy('source') + }, www); + }); + + it('should throw an error if source is missing', function() { + expect(noSource).toThrow(); + + function noSource() { new Dataset({}, www); } + }); + + it('should throw an error if the name is not a valid class name', function() { + expect(fn).toThrow(); + + function fn() { + var d = new Dataset({ + name: 'a space', + node: $('
    '), + source: $.noop + }, www); + } + }); + + describe('#getRoot', function() { + it('should return the root element', function() { + var sel = 'div' + www.selectors.dataset + www.selectors.dataset + '-test'; + expect(this.dataset.$el).toBe(sel); + }); + }); + + describe('#update', function() { + it('should render suggestions', function() { + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('woah'); + + expect(this.dataset.$el).toContainText('one'); + expect(this.dataset.$el).toContainText('two'); + expect(this.dataset.$el).toContainText('html'); + }); + + it('should escape html chars from display value when using default template', function() { + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('woah'); + + expect(this.dataset.$el).toContainText('html'); + }); + + it('should respect limit option', function() { + this.dataset.limit = 2; + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('woah'); + + expect(this.dataset.$el).toContainText('one'); + expect(this.dataset.$el).toContainText('two'); + expect(this.dataset.$el).not.toContainText('three'); + }); + + it('should allow custom display functions', function() { + this.dataset = new Dataset({ + name: 'test', + node: $('
    '), + display: function(o) { return o.display; }, + source: this.source = jasmine.createSpy('source') + }, www); + + this.source.andCallFake(syncMockSuggestionsDisplayFn); + this.dataset.update('woah'); + + expect(this.dataset.$el).toContainText('4'); + expect(this.dataset.$el).toContainText('5'); + expect(this.dataset.$el).toContainText('6'); + }); + + it('should ignore async invocations of sync', function() { + this.source.andCallFake(asyncSync); + this.dataset.update('woah'); + + expect(this.dataset.$el).not.toContainText('one'); + }); + + it('should ignore subesequent invocations of sync', function() { + this.source.andCallFake(multipleSync); + this.dataset.update('woah'); + + expect(this.dataset.$el.find('.tt-suggestion')).toHaveLength(3); + }); + + it('should trigger asyncRequested when needing/expecting backfill', function() { + var spy = jasmine.createSpy(); + + this.dataset.async = true; + this.dataset.onSync('asyncRequested', spy); + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + this.dataset.update('woah'); + + expect(spy).toHaveBeenCalled(); + }); + + it('should not trigger asyncRequested when not expecting backfill', function() { + var spy = jasmine.createSpy(); + + this.dataset.async = false; + this.dataset.onSync('asyncRequested', spy); + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + this.dataset.update('woah'); + + expect(spy).not.toHaveBeenCalled(); + }); + + it('should not trigger asyncRequested when not expecting backfill', function() { + var spy = jasmine.createSpy(); + + this.dataset.limit = 2; + this.dataset.async = true; + this.dataset.onSync('asyncRequested', spy); + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + this.dataset.update('woah'); + + expect(spy).not.toHaveBeenCalled(); + }); + + it('should trigger asyncCanceled when pending aysnc is canceled', function() { + var spy = jasmine.createSpy(); + + this.dataset.async = true; + this.dataset.onSync('asyncCanceled', spy); + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + this.dataset.update('woah'); + this.dataset.cancel(); + + waits(100); + + runs(function() { + expect(spy).toHaveBeenCalled(); + }); + }); + + it('should not trigger asyncCanceled when cancel happens after update', function() { + var spy = jasmine.createSpy(); + + this.dataset.async = true; + this.dataset.onSync('asyncCanceled', spy); + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + this.dataset.update('woah'); + + waits(100); + + runs(function() { + this.dataset.cancel(); + expect(spy).not.toHaveBeenCalled(); + }); + }); + + it('should trigger asyncReceived when aysnc is received', function() { + var spy = jasmine.createSpy(); + + this.dataset.async = true; + this.dataset.onSync('asyncReceived', spy); + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + this.dataset.update('woah'); + + waits(100); + + runs(function() { + expect(spy).toHaveBeenCalled(); + }); + }); + + it('should not trigger asyncReceived if canceled', function() { + var spy = jasmine.createSpy(); + + this.dataset.async = true; + this.dataset.onSync('asyncReceived', spy); + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + this.dataset.update('woah'); + this.dataset.cancel(); + + waits(100); + + runs(function() { + expect(spy).not.toHaveBeenCalled(); + }); + }); + + it('should not modify sync when async is added', function() { + var $test; + + this.dataset.async = true; + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + this.dataset.update('woah'); + $test = this.dataset.$el.find('.tt-suggestion').first(); + $test.addClass('test'); + + waits(100); + + runs(function() { + expect($test).toHaveClass('test'); + }); + }); + + it('should respect limit option in regard to async', function() { + this.dataset.async = true; + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + this.dataset.update('woah'); + + waits(100); + + runs(function() { + expect(this.dataset.$el.find('.tt-suggestion')).toHaveLength(5); + }); + }); + + it('should cancel pending async', function() { + var spy1 = jasmine.createSpy(), spy2 = jasmine.createSpy(); + + this.dataset.async = true; + this.dataset.onSync('asyncCanceled', spy1); + this.dataset.onSync('asyncReceived', spy2); + this.source.andCallFake(fakeGetWithAsyncSuggestions); + + + this.dataset.update('woah'); + this.dataset.update('woah again'); + + waits(100); + + runs(function() { + expect(spy1.callCount).toBe(1); + expect(spy2.callCount).toBe(1); + }); + }); + + it('should render notFound when no suggestions are available', function() { + this.dataset = new Dataset({ + source: this.source, + node: $('
    '), + templates: { + notFound: '

    empty

    ' + } + }, www); + + this.source.andCallFake(syncEmptySuggestions); + this.dataset.update('woah'); + + expect(this.dataset.$el).toContainText('empty'); + }); + + it('should render pending when no suggestions are available but async is pending', function() { + this.dataset = new Dataset({ + source: this.source, + node: $('
    '), + async: true, + templates: { + pending: '

    pending

    ' + } + }, www); + + this.source.andCallFake(syncEmptySuggestions); + this.dataset.update('woah'); + + expect(this.dataset.$el).toContainText('pending'); + }); + + it('should render header when suggestions are rendered', function() { + this.dataset = new Dataset({ + source: this.source, + node: $('
    '), + templates: { + header: '

    header

    ' + } + }, www); + + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('woah'); + + expect(this.dataset.$el).toContainText('header'); + }); + + it('should render footer when suggestions are rendered', function() { + this.dataset = new Dataset({ + source: this.source, + node: $('
    '), + templates: { + footer: function(c) { return '

    ' + c.query + '

    '; } + } + }, www); + + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('woah'); + + expect(this.dataset.$el).toContainText('woah'); + }); + + it('should not render header/footer if there is no content', function() { + this.dataset = new Dataset({ + source: this.source, + node: $('
    '), + templates: { + header: '

    header

    ', + footer: '

    footer

    ' + } + }, www); + + this.source.andCallFake(syncEmptySuggestions); + this.dataset.update('woah'); + + expect(this.dataset.$el).not.toContainText('header'); + expect(this.dataset.$el).not.toContainText('footer'); + }); + + it('should not render stale suggestions', function() { + this.source.andCallFake(fakeGetWithAsyncSuggestions); + this.dataset.update('woah'); + + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('nelly'); + + waits(100); + + runs(function() { + expect(this.dataset.$el).toContainText('one'); + expect(this.dataset.$el).toContainText('two'); + expect(this.dataset.$el).toContainText('html'); + expect(this.dataset.$el).not.toContainText('four'); + expect(this.dataset.$el).not.toContainText('five'); + }); + }); + + it('should not render async suggestions if update was canceled', function() { + this.source.andCallFake(fakeGetWithAsyncSuggestions); + this.dataset.update('woah'); + this.dataset.cancel(); + + waits(100); + + runs(function() { + var rendered = this.dataset.$el.find('.tt-suggestion'); + expect(rendered).toHaveLength(3); + }); + }); + + it('should trigger rendered after suggestions are rendered', function() { + var spy; + + this.dataset.onSync('rendered', spy = jasmine.createSpy()); + + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('woah'); + + waitsFor(function() { return spy.callCount; }); + }); + }); + + describe('#clear', function() { + it('should clear suggestions', function() { + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('woah'); + + this.dataset.clear(); + expect(this.dataset.$el).toBeEmpty(); + }); + + it('should cancel pending updates', function() { + var spy; + + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('woah'); + spy = spyOn(this.dataset, 'cancel'); + + this.dataset.clear(); + expect(spy).toHaveBeenCalled(); + }); + + it('should trigger cleared', function() { + var spy; + + this.dataset.onSync('cleared', spy = jasmine.createSpy()); + this.dataset.clear(); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('#isEmpty', function() { + it('should return true when empty', function() { + expect(this.dataset.isEmpty()).toBe(true); + }); + + it('should return false when not empty', function() { + this.source.andCallFake(syncMockSuggestions); + this.dataset.update('woah'); + + expect(this.dataset.isEmpty()).toBe(false); + }); + }); + + describe('#destroy', function() { + it('should set dataset element to dummy element', function() { + var $prevEl = this.dataset.$el; + + this.dataset.destroy(); + expect(this.dataset.$el).not.toBe($prevEl); + }); + }); + + // helper functions + // ---------------- + + function syncEmptySuggestions(q, sync, async) { + sync([]); + } + + function syncMockSuggestions(q, sync, async) { + sync(mockSuggestions); + } + + function syncMockSuggestionsDisplayFn(q, sync, async) { + sync(mockSuggestionsDisplayFn); + } + + function asyncSync(q, sync, async) { + setTimeout(function() { sync(mockSuggestions); }, 0); + } + + function multipleSync(q, sync, async) { + sync(mockSuggestions); + sync(mockSuggestions); + } + + function fakeGetWithAsyncSuggestions(query, sync, async) { + sync(mockSuggestions); + + setTimeout(function() { + async([ + { value: 'four', raw: { value: 'four' } }, + { value: 'five', raw: { value: 'five' } }, + { value: 'six', raw: { value: 'six' } }, + { value: 'seven', raw: { value: 'seven' } }, + { value: 'eight', raw: { value: 'eight' } }, + ]); + }, 0); + } +}); diff --git a/www/bower_components/typeahead.js/test/typeahead/default_results_spec.js b/www/bower_components/typeahead.js/test/typeahead/default_results_spec.js new file mode 100644 index 0000000..bf47231 --- /dev/null +++ b/www/bower_components/typeahead.js/test/typeahead/default_results_spec.js @@ -0,0 +1,103 @@ +describe('DefaultMenu', function() { + var www = WWW(); + + beforeEach(function() { + var $fixture; + + jasmine.Dataset.useMock(); + + setFixtures(''); + + $fixture = $('#jasmine-fixtures'); + this.$node = $fixture.find('#menu-fixture'); + this.$node.html(fixtures.html.dataset); + + this.view = new DefaultMenu({ node: this.$node, datasets: [{}] }, www).bind(); + this.dataset = this.view.datasets[0]; + }); + + describe('when rendered is triggered on a dataset', function() { + it('should hide menu if empty', function() { + this.dataset.isEmpty.andReturn(true); + + this.view._show(); + this.dataset.trigger('rendered'); + + expect(this.$node).not.toBeVisible(); + }); + + it('should not show menu if not open', function() { + this.dataset.isEmpty.andReturn(false); + + this.view._hide(); + this.dataset.trigger('rendered'); + + expect(this.$node).not.toBeVisible(); + }); + + it('should show menu if not empty and open', function() { + this.dataset.isEmpty.andReturn(false); + + this.view._hide(); + this.view.open(); + this.dataset.trigger('rendered'); + + expect(this.$node).toBeVisible(); + }); + }); + + describe('when cleared is triggered on a dataset', function() { + it('should hide menu if empty', function() { + this.dataset.isEmpty.andReturn(true); + + this.view._show(); + this.dataset.trigger('cleared'); + + expect(this.$node).not.toBeVisible(); + }); + + it('should not show menu if not open', function() { + this.dataset.isEmpty.andReturn(false); + + this.view._hide(); + this.dataset.trigger('cleared'); + + expect(this.$node).not.toBeVisible(); + }); + + it('should show menu if not empty and open', function() { + this.dataset.isEmpty.andReturn(false); + + this.view._hide(); + this.view.open(); + this.dataset.trigger('cleared'); + + expect(this.$node).toBeVisible(); + }); + }); + + describe('#open', function() { + it('should show menu if not empty', function() { + spyOn(this.view, '_allDatasetsEmpty').andReturn(false); + this.view.open(); + + expect(this.$node[0].getAttribute('style')).toMatch(/display: block/); + }); + + it('should not show menu if empty', function() { + spyOn(this.view, '_allDatasetsEmpty').andReturn(true); + this.view.open(); + + expect(this.$node).not.toHaveAttr('style', 'display: block;'); + }); + }); + + describe('#close', function() { + it('should hide menu', function() { + this.view._show(); + this.view.close(); + + expect(this.$node).not.toBeVisible(); + }); + }); +}); diff --git a/www/bower_components/typeahead.js/test/typeahead/event_bus_spec.js b/www/bower_components/typeahead.js/test/typeahead/event_bus_spec.js new file mode 100644 index 0000000..9845aa2 --- /dev/null +++ b/www/bower_components/typeahead.js/test/typeahead/event_bus_spec.js @@ -0,0 +1,42 @@ +describe('EventBus', function() { + + beforeEach(function() { + var $fixture; + + setFixtures(fixtures.html.input); + + $fixture = $('#jasmine-fixtures'); + this.$el = $fixture.find('.tt-input'); + + this.eventBus = new EventBus({ el: this.$el }); + }); + + it('#trigger should trigger event', function() { + var spy = jasmine.createSpy(); + + this.$el.on('typeahead:fiz', spy); + + this.eventBus.trigger('fiz'); + expect(spy).toHaveBeenCalled(); + }); + + it('#before should return false if default was not prevented', function() { + var spy = jasmine.createSpy(); + + this.$el.on('typeahead:beforefiz', spy); + + expect(this.eventBus.before('fiz')).toBe(false); + expect(spy).toHaveBeenCalled(); + }); + + it('#before should return true if default was prevented', function() { + var spy = jasmine.createSpy().andCallFake(prevent); + + this.$el.on('typeahead:beforefiz', spy); + + expect(this.eventBus.before('fiz')).toBe(true); + expect(spy).toHaveBeenCalled(); + + function prevent($e) { $e.preventDefault(); } + }); +}); diff --git a/www/bower_components/typeahead.js/test/typeahead/event_emitter_spec.js b/www/bower_components/typeahead.js/test/typeahead/event_emitter_spec.js new file mode 100644 index 0000000..5c51ca6 --- /dev/null +++ b/www/bower_components/typeahead.js/test/typeahead/event_emitter_spec.js @@ -0,0 +1,111 @@ +describe('EventEmitter', function() { + + beforeEach(function() { + this.spy = jasmine.createSpy(); + this.target = _.mixin({}, EventEmitter); + }); + + it('methods should be chainable', function() { + expect(this.target.onSync()).toEqual(this.target); + expect(this.target.onAsync()).toEqual(this.target); + expect(this.target.off()).toEqual(this.target); + expect(this.target.trigger()).toEqual(this.target); + }); + + it('#on should take the context a callback should be called in', function() { + var context = { val: 3 }, cbContext; + + this.target.onSync('xevent', setCbContext, context).trigger('xevent'); + + waitsFor(assertCbContext, 'callback was called in the wrong context'); + + function setCbContext() { cbContext = this; } + function assertCbContext() { return cbContext === context; } + }); + + it('#onAsync callbacks should be invoked asynchronously', function() { + this.target.onAsync('event', this.spy).trigger('event'); + + expect(this.spy.callCount).toBe(0); + waitsFor(assertCallCount(this.spy, 1), 'the callback was not invoked'); + }); + + it('#onSync callbacks should be invoked synchronously', function() { + this.target.onSync('event', this.spy).trigger('event'); + + expect(this.spy.callCount).toBe(1); + }); + + it('#off should remove callbacks', function() { + this.target + .onSync('event1 event2', this.spy) + .onAsync('event1 event2', this.spy) + .off('event1 event2') + .trigger('event1 event2'); + + waits(100); + runs(assertCallCount(this.spy, 0)); + }); + + it('methods should accept multiple event types', function() { + this.target + .onSync('event1 event2', this.spy) + .onAsync('event1 event2', this.spy) + .trigger('event1 event2'); + + expect(this.spy.callCount).toBe(2); + waitsFor(assertCallCount(this.spy, 4), 'the callback was not invoked'); + }); + + it('the event type should be passed to the callback', function() { + this.target + .onSync('sync', this.spy) + .onAsync('async', this.spy) + .trigger('sync async'); + + waitsFor(assertArgs(this.spy, 0, ['sync']), 'bad args'); + waitsFor(assertArgs(this.spy, 1, ['async']), 'bad args'); + }); + + it('arbitrary args should be passed to the callback', function() { + this.target + .onSync('event', this.spy) + .onAsync('event', this.spy) + .trigger('event', 1, 2); + + waitsFor(assertArgs(this.spy, 0, ['event', 1, 2]), 'bad args'); + waitsFor(assertArgs(this.spy, 1, ['event', 1, 2]), 'bad args'); + }); + + it('callback execution should be cancellable', function() { + var cancelSpy = jasmine.createSpy().andCallFake(cancel); + + this.target + .onSync('one', cancelSpy) + .onSync('one', this.spy) + .onAsync('two', cancelSpy) + .onAsync('two', this.spy) + .onSync('three', cancelSpy) + .onAsync('three', this.spy) + .trigger('one two three'); + + waitsFor(assertCallCount(cancelSpy, 3)); + waitsFor(assertCallCount(this.spy, 0)); + + function cancel() { return false; } + }); + + function assertCallCount(spy, expected) { + return function() { return spy.callCount === expected; }; + } + + function assertArgs(spy, call, expected) { + return function() { + var env = jasmine.getEnv(), + actual = spy.calls[call] ? spy.calls[call].args : undefined; + + return env.equals_(actual, expected); + }; + } + +}); diff --git a/www/bower_components/typeahead.js/test/typeahead/highlight_spec.js b/www/bower_components/typeahead.js/test/typeahead/highlight_spec.js new file mode 100644 index 0000000..952d3f2 --- /dev/null +++ b/www/bower_components/typeahead.js/test/typeahead/highlight_spec.js @@ -0,0 +1,117 @@ +describe('highlight', function() { + it('should allow tagName to be specified', function() { + var before = 'abcde', + after = 'abcde', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: 'bcd', tagName: 'span' }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should allow className to be specified', function() { + var before = 'abcde', + after = 'abcde', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: 'bcd', className: 'one two' }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should be case insensitive by default', function() { + var before = 'ABCDE', + after = 'ABCDE', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: 'bcd' }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should support case sensitivity', function() { + var before = 'ABCDE', + after = 'ABCDE', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: 'bcd', caseSensitive: true }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should support words only matching', function() { + var before = 'tone one phone', + after = 'tone one phone', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: 'one', wordsOnly: true }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should support matching multiple patterns', function() { + var before = 'tone one phone', + after = 'tone one phone', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: ['tone', 'phone'] }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should support regex chars in the pattern', function() { + var before = '*.js when?', + after = '*.js when?', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: ['*.', '?'] }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should work on complex html structures', function() { + var before = [ + '
    abcde', + 'abcde', + '

    abcde

    ', + '
    ' + ].join(''), + after = [ + '
    abcde', + 'abcde', + '

    abcde

    ', + '
    ' + ].join(''), + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: 'abc' }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should ignore html tags and attributes', function() { + var before = '', + after = '', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: ['span', 'class'] }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should not match across tags', function() { + var before = 'abc', + after = 'abc', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: 'abc' }); + expect(testNode.innerHTML).toEqual(after); + }); + + it('should ignore html comments', function() { + var before = '', + after = '', + testNode = buildTestNode(before); + + highlight({ node: testNode, pattern: 'abc' }); + expect(testNode.innerHTML).toEqual(after); + }); + + function buildTestNode(content) { + var node = document.createElement('div'); + node.innerHTML = content; + + return node; + } +}); diff --git a/www/bower_components/typeahead.js/test/typeahead/input_spec.js b/www/bower_components/typeahead.js/test/typeahead/input_spec.js new file mode 100644 index 0000000..31d8438 --- /dev/null +++ b/www/bower_components/typeahead.js/test/typeahead/input_spec.js @@ -0,0 +1,538 @@ +describe('Input', function() { + var KEYS, www; + + KEYS = { + enter: 13, + esc: 27, + tab: 9, + left: 37, + right: 39, + up: 38, + down: 40, + normal: 65 // "A" key + }; + + www = WWW(); + + beforeEach(function() { + var $fixture; + + setFixtures(fixtures.html.input + fixtures.html.hint); + + $fixture = $('#jasmine-fixtures'); + this.$input = $fixture.find('.tt-input'); + this.$hint = $fixture.find('.tt-hint'); + + this.view = new Input({ input: this.$input, hint: this.$hint }, www).bind(); + }); + + it('should throw an error if no input is provided', function() { + expect(noInput).toThrow(); + + function noInput() { new Input({}, www); } + }); + + describe('when the blur DOM event is triggered', function() { + it('should reset the input value', function() { + this.view.setQuery('wine'); + this.view.setInputValue('cheese'); + + this.$input.blur(); + + expect(this.$input.val()).toBe('wine'); + }); + + it('should trigger blurred', function() { + var spy; + + this.view.onSync('blurred', spy = jasmine.createSpy()); + this.$input.blur(); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when the focus DOM event is triggered', function() { + it('should update queryWhenFocused', function() { + this.view.setQuery('hi'); + this.$input.focus(); + expect(this.view.hasQueryChangedSinceLastFocus()).toBe(false); + this.view.setQuery('bye'); + expect(this.view.hasQueryChangedSinceLastFocus()).toBe(true); + }); + + it('should trigger focused', function() { + var spy; + + this.view.onSync('focused', spy = jasmine.createSpy()); + this.$input.focus(); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when the keydown DOM event is triggered by tab', function() { + it('should trigger tabKeyed if no modifiers were pressed', function() { + var spy; + + this.view.onSync('tabKeyed', spy = jasmine.createSpy()); + simulateKeyEvent(this.$input, 'keydown', KEYS.tab); + + expect(spy).toHaveBeenCalled(); + }); + + it('should not trigger tabKeyed if modifiers were pressed', function() { + var spy; + + this.view.onSync('tabKeyed', spy = jasmine.createSpy()); + simulateKeyEvent(this.$input, 'keydown', KEYS.tab, true); + + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('when the keydown DOM event is triggered by esc', function() { + it('should trigger escKeyed', function() { + var spy; + + this.view.onSync('escKeyed', spy = jasmine.createSpy()); + simulateKeyEvent(this.$input, 'keydown', KEYS.esc); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when the keydown DOM event is triggered by left', function() { + it('should trigger leftKeyed', function() { + var spy; + + this.view.onSync('leftKeyed', spy = jasmine.createSpy()); + simulateKeyEvent(this.$input, 'keydown', KEYS.left); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when the keydown DOM event is triggered by right', function() { + it('should trigger rightKeyed', function() { + var spy; + + this.view.onSync('rightKeyed', spy = jasmine.createSpy()); + simulateKeyEvent(this.$input, 'keydown', KEYS.right); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when the keydown DOM event is triggered by enter', function() { + it('should trigger enterKeyed', function() { + var spy; + + this.view.onSync('enterKeyed', spy = jasmine.createSpy()); + simulateKeyEvent(this.$input, 'keydown', KEYS.enter); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when the keydown DOM event is triggered by up', function() { + it('should trigger upKeyed', function() { + var spy; + + this.view.onSync('upKeyed', spy = jasmine.createSpy()); + simulateKeyEvent(this.$input, 'keydown', KEYS.up); + + expect(spy).toHaveBeenCalled(); + }); + + it('should prevent default if no modifers were pressed', function() { + var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.up); + + expect($e.preventDefault).toHaveBeenCalled(); + }); + + it('should not prevent default if modifers were pressed', function() { + var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.up, true); + + expect($e.preventDefault).not.toHaveBeenCalled(); + }); + }); + + describe('when the keydown DOM event is triggered by down', function() { + it('should trigger downKeyed', function() { + var spy; + + this.view.onSync('downKeyed', spy = jasmine.createSpy()); + simulateKeyEvent(this.$input, 'keydown', KEYS.down); + + expect(spy).toHaveBeenCalled(); + }); + + it('should prevent default if no modifers were pressed', function() { + var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.down); + + expect($e.preventDefault).toHaveBeenCalled(); + }); + + it('should not prevent default if modifers were pressed', function() { + var $e = simulateKeyEvent(this.$input, 'keydown', KEYS.down, true); + + expect($e.preventDefault).not.toHaveBeenCalled(); + }); + }); + + // NOTE: have to treat these as async because the ie polyfill acts + // in a async manner + describe('when the input DOM event is triggered', function() { + it('should update query', function() { + this.view.setQuery('wine'); + this.view.setInputValue('cheese'); + + simulateInputEvent(this.$input); + + waitsFor(function() { return this.view.getQuery() === 'cheese'; }); + }); + + it('should trigger queryChanged if the query changed', function() { + var spy; + + this.view.setQuery('wine'); + this.view.setInputValue('cheese'); + this.view.onSync('queryChanged', spy = jasmine.createSpy()); + + simulateInputEvent(this.$input); + + expect(spy).toHaveBeenCalled(); + }); + + it('should trigger whitespaceChanged if whitespace changed', function() { + var spy; + + this.view.setQuery('wine bar'); + this.view.setInputValue('wine bar'); + this.view.onSync('whitespaceChanged', spy = jasmine.createSpy()); + + simulateInputEvent(this.$input); + + expect(spy).toHaveBeenCalled(); + }); + + it('should clear hint if invalid', function() { + spyOn(this.view, 'clearHintIfInvalid'); + simulateInputEvent(this.$input); + expect(this.view.clearHintIfInvalid).toHaveBeenCalled(); + }); + + it('should check lang direction', function() { + var spy; + + this.$input.css('direction', 'rtl'); + this.view.onSync('langDirChanged', spy = jasmine.createSpy()); + + simulateInputEvent(this.$input); + + expect(this.view.dir).toBe('rtl'); + expect(this.$hint).toHaveAttr('dir', 'rtl'); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('.normalizeQuery', function() { + it('should strip leading whitespace', function() { + expect(Input.normalizeQuery(' foo')).toBe('foo'); + }); + + it('should condense whitespace', function() { + expect(Input.normalizeQuery('foo bar')).toBe('foo bar'); + }); + + it('should play nice with non-string values', function() { + expect(Input.normalizeQuery(2)).toBe('2'); + expect(Input.normalizeQuery([])).toBe(''); + expect(Input.normalizeQuery(null)).toBe(''); + expect(Input.normalizeQuery(undefined)).toBe(''); + expect(Input.normalizeQuery(false)).toBe('false'); + }); + }); + + describe('#focus', function() { + it('should focus the input', function() { + this.$input.blur(); + this.view.focus(); + + expect(this.$input).toBeFocused(); + }); + }); + + describe('#blur', function() { + it('should blur the input', function() { + this.$input.focus(); + this.view.blur(); + + expect(this.$input).not.toBeFocused(); + }); + }); + + describe('#getQuery', function() { + it('should act as getter to the query property', function() { + this.view.setQuery('mouse'); + expect(this.view.getQuery()).toBe('mouse'); + }); + }); + + describe('#setQuery', function() { + it('should act as setter to the query property', function() { + this.view.setQuery('mouse'); + expect(this.view.getQuery()).toBe('mouse'); + }); + + it('should update input value', function() { + this.view.setQuery('mouse'); + expect(this.view.getInputValue()).toBe('mouse'); + }); + + it('should trigger queryChanged if the query changed', function() { + var spy; + + this.view.setQuery('wine'); + this.view.onSync('queryChanged', spy = jasmine.createSpy()); + this.view.setQuery('cheese'); + + expect(spy).toHaveBeenCalled(); + }); + + it('should trigger whitespaceChanged if whitespace changed', function() { + var spy; + + this.view.setQuery('wine bar'); + this.view.onSync('whitespaceChanged', spy = jasmine.createSpy()); + this.view.setQuery('wine bar'); + + expect(spy).toHaveBeenCalled(); + }); + + it('should clear hint if invalid', function() { + spyOn(this.view, 'clearHintIfInvalid'); + simulateInputEvent(this.$input); + expect(this.view.clearHintIfInvalid).toHaveBeenCalled(); + }); + }); + + describe('#hasQueryChangedSinceLastFocus', function() { + it('should return true if the query has changed since focus', function() { + this.view.setQuery('hi'); + this.$input.focus(); + this.view.setQuery('bye'); + expect(this.view.hasQueryChangedSinceLastFocus()).toBe(true); + }); + + it('should return false if the query has not changed since focus', function() { + this.view.setQuery('hi'); + this.$input.focus(); + expect(this.view.hasQueryChangedSinceLastFocus()).toBe(false); + }); + }); + + describe('#getInputValue', function() { + it('should act as getter to the input value', function() { + this.$input.val('cheese'); + expect(this.view.getInputValue()).toBe('cheese'); + }); + }); + + describe('#setInputValue', function() { + it('should act as setter to the input value', function() { + this.view.setInputValue('cheese'); + expect(this.view.getInputValue()).toBe('cheese'); + }); + + it('should clear hint if invalid', function() { + spyOn(this.view, 'clearHintIfInvalid'); + this.view.setInputValue('cheese head'); + expect(this.view.clearHintIfInvalid).toHaveBeenCalled(); + }); + + it('should check lang direction', function() { + var spy; + + this.$input.css('direction', 'rtl'); + this.view.onSync('langDirChanged', spy = jasmine.createSpy()); + + simulateInputEvent(this.$input); + + expect(this.view.dir).toBe('rtl'); + expect(this.$hint).toHaveAttr('dir', 'rtl'); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('#getHint/#setHint', function() { + it('should act as getter/setter to value of hint', function() { + this.view.setHint('mountain'); + expect(this.view.getHint()).toBe('mountain'); + }); + }); + + describe('#resetInputValue', function() { + it('should reset input value to last query', function() { + this.view.setQuery('cheese'); + this.view.setInputValue('wine'); + + this.view.resetInputValue(); + expect(this.view.getInputValue()).toBe('cheese'); + }); + }); + + describe('#clearHint', function() { + it('should set the hint value to the empty string', function() { + this.view.setHint('cheese'); + this.view.clearHint(); + + expect(this.view.getHint()).toBe(''); + }); + }); + + describe('#clearHintIfInvalid', function() { + it('should clear hint if input value is empty string', function() { + this.view.setInputValue(''); + this.view.setHint('cheese'); + this.view.clearHintIfInvalid(); + + expect(this.view.getHint()).toBe(''); + }); + + it('should clear hint if input value is not prefix of input', function() { + this.view.setInputValue('milk'); + this.view.setHint('cheese'); + this.view.clearHintIfInvalid(); + + expect(this.view.getHint()).toBe(''); + }); + + it('should clear hint if overflow exists', function() { + spyOn(this.view, 'hasOverflow').andReturn(true); + this.view.setInputValue('che'); + this.view.setHint('cheese'); + this.view.clearHintIfInvalid(); + + expect(this.view.getHint()).toBe(''); + }); + + it('should not clear hint if input value is prefix of input', function() { + this.view.setInputValue('che'); + this.view.setHint('cheese'); + this.view.clearHintIfInvalid(); + + expect(this.view.getHint()).toBe('cheese'); + }); + }); + + describe('#hasOverflow', function() { + it('should return true if the input has overflow text', function() { + var longStr = new Array(1000).join('a'); + + this.view.setInputValue(longStr); + expect(this.view.hasOverflow()).toBe(true); + }); + + it('should return false if the input has no overflow text', function() { + var shortStr = 'aah'; + + this.view.setInputValue(shortStr); + expect(this.view.hasOverflow()).toBe(false); + }); + }); + + describe('#isCursorAtEnd', function() { + it('should return true if the text cursor is at the end', function() { + this.view.setInputValue('boo'); + + setCursorPosition(this.$input, 3); + expect(this.view.isCursorAtEnd()).toBe(true); + }); + + it('should return false if the text cursor is not at the end', function() { + this.view.setInputValue('boo'); + + setCursorPosition(this.$input, 1); + expect(this.view.isCursorAtEnd()).toBe(false); + }); + }); + + describe('#destroy', function() { + it('should remove event handlers', function() { + var $input, $hint; + + $hint = this.view.$hint; + $input = this.view.$input; + + spyOn($hint, 'off'); + spyOn($input, 'off'); + + this.view.destroy(); + + expect($hint.off).toHaveBeenCalledWith('.tt'); + expect($input.off).toHaveBeenCalledWith('.tt'); + }); + + it('should set references to DOM elements to dummy element', function() { + var $hint, $input, $overflowHelper; + + $hint = this.view.$hint; + $input = this.view.$input; + $overflowHelper = this.view.$overflowHelper; + + this.view.destroy(); + + expect(this.view.$hint).not.toBe($hint); + expect(this.view.$input).not.toBe($input); + expect(this.view.$overflowHelper).not.toBe($overflowHelper); + }); + }); + + // helper functions + // ---------------- + + function simulateInputEvent($node) { + var $e, type; + + type = _.isMsie() ? 'keypress' : 'input'; + $e = $.Event(type); + + $node.trigger($e); + } + + function simulateKeyEvent($node, type, key, withModifier) { + var $e; + + $e = $.Event(type, { + keyCode: key, + altKey: !!withModifier, + ctrlKey: !!withModifier, + metaKey: !!withModifier, + shiftKey: !!withModifier + }); + + spyOn($e, 'preventDefault'); + $node.trigger($e); + + return $e; + } + + function setCursorPosition($input, pos) { + var input = $input[0], range; + + if (input.setSelectionRange) { + input.focus(); + input.setSelectionRange(pos, pos); + } + + else if (input.createTextRange) { + range = input.createTextRange(); + range.collapse(true); + range.moveEnd('character', pos); + range.moveStart('character', pos); + range.select(); + } + } +}); diff --git a/www/bower_components/typeahead.js/test/typeahead/plugin_spec.js b/www/bower_components/typeahead.js/test/typeahead/plugin_spec.js new file mode 100644 index 0000000..440fb5d --- /dev/null +++ b/www/bower_components/typeahead.js/test/typeahead/plugin_spec.js @@ -0,0 +1,197 @@ +describe('$plugin', function() { + + beforeEach(function() { + var $fixture; + + setFixtures(''); + + $fixture = $('#jasmine-fixtures'); + this.$input = $fixture.find('.test-input'); + + this.$input.typeahead(null, { + displayKey: 'v', + source: function(q, sync) { + sync([{ v: '1' }, { v: '2' }, { v: '3' }]); + } + }); + }); + + it('#enable should enable the typaahead', function() { + this.$input.typeahead('disable'); + expect(this.$input.typeahead('isEnabled')).toBe(false); + + this.$input.typeahead('enable'); + expect(this.$input.typeahead('isEnabled')).toBe(true); + }); + + it('#disable should disable the typaahead', function() { + this.$input.typeahead('enable'); + expect(this.$input.typeahead('isEnabled')).toBe(true); + + this.$input.typeahead('disable'); + expect(this.$input.typeahead('isEnabled')).toBe(false); + }); + + it('#activate should activate the typaahead', function() { + this.$input.typeahead('deactivate'); + expect(this.$input.typeahead('isActive')).toBe(false); + + this.$input.typeahead('activate'); + expect(this.$input.typeahead('isActive')).toBe(true); + }); + + it('#activate should fail to activate the typaahead if disabled', function() { + this.$input.typeahead('deactivate'); + expect(this.$input.typeahead('isActive')).toBe(false); + this.$input.typeahead('disable'); + + this.$input.typeahead('activate'); + expect(this.$input.typeahead('isActive')).toBe(false); + }); + + it('#deactivate should deactivate the typaahead', function() { + this.$input.typeahead('activate'); + expect(this.$input.typeahead('isActive')).toBe(true); + + this.$input.typeahead('deactivate'); + expect(this.$input.typeahead('isActive')).toBe(false); + }); + + it('#open should open the menu', function() { + this.$input.typeahead('close'); + expect(this.$input.typeahead('isOpen')).toBe(false); + + this.$input.typeahead('open'); + expect(this.$input.typeahead('isOpen')).toBe(true); + }); + + it('#close should close the menu', function() { + this.$input.typeahead('open'); + expect(this.$input.typeahead('isOpen')).toBe(true); + + this.$input.typeahead('close'); + expect(this.$input.typeahead('isOpen')).toBe(false); + }); + + it('#select should select selectable', function() { + var $el; + + // activate and set val to render some selectables + this.$input.typeahead('activate'); + this.$input.typeahead('val', 'o'); + $el = $('.tt-selectable').first(); + + expect(this.$input.typeahead('select', $el)).toBe(true); + expect(this.$input.typeahead('val')).toBe('1'); + }); + + it('#select should return false if not valid selectable', function() { + var body; + + // activate and set val to render some selectables + this.$input.typeahead('activate'); + this.$input.typeahead('val', 'o'); + body = document.body; + + expect(this.$input.typeahead('select', body)).toBe(false); + }); + + it('#autocomplete should autocomplete to selectable', function() { + var $el; + + // activate and set val to render some selectables + this.$input.typeahead('activate'); + this.$input.typeahead('val', 'o'); + $el = $('.tt-selectable').first(); + + expect(this.$input.typeahead('autocomplete', $el)).toBe(true); + expect(this.$input.typeahead('val')).toBe('1'); + }); + + it('#autocomplete should return false if not valid selectable', function() { + var body; + + // activate and set val to render some selectables + this.$input.typeahead('activate'); + this.$input.typeahead('val', 'o'); + body = document.body; + + expect(this.$input.typeahead('autocomplete', body)).toBe(false); + }); + + it('#moveCursor should move cursor', function() { + var $el; + + // activate and set val to render some selectables + this.$input.typeahead('activate'); + this.$input.typeahead('val', 'o'); + $el = $('.tt-selectable').first(); + + expect($el).not.toHaveClass('tt-cursor'); + expect(this.$input.typeahead('moveCursor', 1)).toBe(true); + expect($el).toHaveClass('tt-cursor'); + }); + + it('#select should return false if not valid selectable', function() { + var body; + + // activate and set val to render some selectables + this.$input.typeahead('activate'); + this.$input.typeahead('val', 'o'); + body = document.body; + + expect(this.$input.typeahead('select', body)).toBe(false); + }); + + it('#val() should typeahead value of element', function() { + var $els; + + this.$input.typeahead('val', 'foo'); + $els = this.$input.add('
    '); + + expect($els.typeahead('val')).toBe('foo'); + }); + + it('#val(q) should set query', function() { + this.$input.typeahead('val', 'foo'); + expect(this.$input.typeahead('val')).toBe('foo'); + }); + + it('#destroy should revert modified attributes', function() { + expect(this.$input).toHaveAttr('autocomplete', 'off'); + expect(this.$input).toHaveAttr('dir'); + expect(this.$input).toHaveAttr('spellcheck'); + expect(this.$input).toHaveAttr('style'); + + this.$input.typeahead('destroy'); + + expect(this.$input).toHaveAttr('autocomplete', 'on'); + expect(this.$input).not.toHaveAttr('dir'); + expect(this.$input).not.toHaveAttr('spellcheck'); + expect(this.$input).not.toHaveAttr('style'); + }); + + it('#destroy should remove data', function() { + expect(this.$input.data('tt-www')).toBeTruthy(); + expect(this.$input.data('tt-attrs')).toBeTruthy(); + expect(this.$input.data('tt-typeahead')).toBeTruthy(); + + this.$input.typeahead('destroy'); + + expect(this.$input.data('tt-www')).toBeFalsy(); + expect(this.$input.data('tt-attrs')).toBeFalsy(); + expect(this.$input.data('tt-typeahead')).toBeFalsy(); + }); + + it('#destroy should remove add classes', function() { + expect(this.$input).toHaveClass('tt-input'); + this.$input.typeahead('destroy'); + expect(this.$input).not.toHaveClass('tt-input'); + }); + + it('#destroy should revert DOM changes', function() { + expect($('.twitter-typeahead')).toExist(); + this.$input.typeahead('destroy'); + expect($('.twitter-typeahead')).not.toExist(); + }); +}); diff --git a/www/bower_components/typeahead.js/test/typeahead/results_spec.js b/www/bower_components/typeahead.js/test/typeahead/results_spec.js new file mode 100644 index 0000000..7729d43 --- /dev/null +++ b/www/bower_components/typeahead.js/test/typeahead/results_spec.js @@ -0,0 +1,332 @@ +describe('Menu', function() { + var www = WWW(); + + beforeEach(function() { + var $fixture; + + jasmine.Dataset.useMock(); + + setFixtures(''); + + $fixture = $('#jasmine-fixtures'); + this.$node = $fixture.find('#menu-fixture'); + this.$node.html(fixtures.html.dataset); + + this.view = new Menu({ node: this.$node, datasets: [{}] }, www).bind(); + this.dataset = this.view.datasets[0]; + }); + + it('should throw an error if node is missing', function() { + expect(noNode).toThrow(); + function noNode() { new Menu({ datasets: [{}] }, www); } + }); + + describe('when click event is triggered on a selectable', function() { + it('should trigger selectableClicked', function() { + var spy; + + this.view.onSync('selectableClicked', spy = jasmine.createSpy()); + + this.$node.find(www.selectors.selectable).first().click(); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when rendered is triggered on a dataset', function() { + it('should add empty class to node if empty', function() { + this.dataset.isEmpty.andReturn(true); + + this.$node.removeClass(www.classes.empty); + this.dataset.trigger('rendered'); + + expect(this.$node).toHaveClass(www.classes.empty); + }); + + it('should remove empty class from node if not empty', function() { + this.dataset.isEmpty.andReturn(false); + + this.$node.addClass(www.classes.empty); + this.dataset.trigger('rendered'); + + expect(this.$node).not.toHaveClass(www.classes.empty); + }); + + it('should trigger datasetRendered', function() { + var spy; + + this.view.onSync('datasetRendered', spy = jasmine.createSpy()); + this.dataset.trigger('rendered'); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when cleared is triggered on a dataset', function() { + it('should add empty class to node if empty', function() { + this.dataset.isEmpty.andReturn(true); + + this.$node.removeClass(www.classes.empty); + this.dataset.trigger('cleared'); + + expect(this.$node).toHaveClass(www.classes.empty); + }); + + it('should remove empty class from node if not empty', function() { + this.dataset.isEmpty.andReturn(false); + + this.$node.addClass(www.classes.empty); + this.dataset.trigger('cleared'); + + expect(this.$node).not.toHaveClass(www.classes.empty); + }); + + it('should trigger datasetCleared', function() { + var spy; + + this.view.onSync('datasetCleared', spy = jasmine.createSpy()); + this.dataset.trigger('cleared'); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when asyncRequested is triggered on a dataset', function() { + it('should propagate event', function() { + var spy = jasmine.createSpy(); + + this.dataset.onSync('asyncRequested', spy); + this.dataset.trigger('asyncRequested'); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when asyncCanceled is triggered on a dataset', function() { + it('should propagate event', function() { + var spy = jasmine.createSpy(); + + this.dataset.onSync('asyncCanceled', spy); + this.dataset.trigger('asyncCanceled'); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('when asyncReceieved is triggered on a dataset', function() { + it('should propagate event', function() { + var spy = jasmine.createSpy(); + + this.dataset.onSync('asyncReceived', spy); + this.dataset.trigger('asyncReceived'); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('#open', function() { + it('should add open class to node', function() { + this.$node.removeClass(www.classes.open); + this.view.open(); + + expect(this.$node).toHaveClass(www.classes.open); + }); + }); + + describe('#close', function() { + it('should remove open class to node', function() { + this.$node.addClass(www.classes.open); + this.view.close(); + + expect(this.$node).not.toHaveClass(www.classes.open); + }); + + it('should remove cursor', function() { + var $selectable; + + $selectable = this.view._getSelectables().first(); + this.view.setCursor($selectable); + + expect($selectable).toHaveClass(www.classes.cursor); + + this.view.close(); + + expect($selectable).not.toHaveClass(www.classes.cursor); + }); + }); + + describe('#setLanguageDirection', function() { + it('should update css for given language direction', function() { + this.view.setLanguageDirection('rtl'); + expect(this.$node).toHaveAttr('dir', 'rtl'); + + this.view.setLanguageDirection('ltr'); + expect(this.$node).toHaveAttr('dir', 'ltr'); + }); + }); + + describe('#selectableRelativeToCursor', function() { + it('should return selectable delta spots away from cursor', function() { + var $first, $second; + + $first = this.view._getSelectables().eq(0); + $second = this.view._getSelectables().eq(1); + + this.view.setCursor($first); + expect(this.view.selectableRelativeToCursor(+1)).toBe($second); + }); + + it('should support negative deltas', function() { + var $first, $second; + + $first = this.view._getSelectables().eq(0); + $second = this.view._getSelectables().eq(1); + + this.view.setCursor($second); + expect(this.view.selectableRelativeToCursor(-1)).toBe($first); + }); + + it('should wrap', function() { + var $expected, $actual; + + $expected = this.view._getSelectables().eq(-1); + $actual = this.view.selectableRelativeToCursor(-1); + + expect($actual).toBe($expected); + }); + + it('should return null if delta lands on input', function() { + var $first; + + $first = this.view._getSelectables().eq(0); + + this.view.setCursor($first); + expect(this.view.selectableRelativeToCursor(-1)).toBeNull(); + }); + }); + + describe('#setCursor', function() { + it('should remove cursor if null is passed in', function() { + var $selectable; + + $selectable = this.view._getSelectables().eq(0); + this.view.setCursor($selectable); + expect(this.view.getActiveSelectable()).toBe($selectable); + + this.view.setCursor(null); + expect(this.view.getActiveSelectable()).toBeNull(); + }); + + it('should move cursor to passed in selectable', function() { + var $selectable; + + $selectable = this.view._getSelectables().eq(0); + + expect(this.view.getActiveSelectable()).toBeNull(); + this.view.setCursor($selectable); + expect(this.view.getActiveSelectable()).toBe($selectable); + }); + }); + + describe('#getSelectableData', function() { + it('should extract the data from the selectable element', function() { + var $selectable, datum; + + $selectable = $('
    ').data({ + 'tt-selectable-display': 'one', + 'tt-selectable-object': 'two' + }); + + data = this.view.getSelectableData($selectable); + + expect(data).toEqual({ val: 'one', obj: 'two' }); + }); + + it('should return null if no element is given', function() { + expect(this.view.getSelectableData($('notreal'))).toBeNull(); + }); + }); + + describe('#getActiveSelectable', function() { + it('should return the selectable the cursor is on', function() { + var $first; + + $first = this.view._getSelectables().eq(0); + this.view.setCursor($first); + + expect(this.view.getActiveSelectable()).toBe($first); + }); + + it('should return null if the cursor is off', function() { + expect(this.view.getActiveSelectable()).toBeNull(); + }); + }); + + describe('#getTopSelectable', function() { + it('should return the selectable at the top of the menu', function() { + var $first; + + $first = this.view._getSelectables().eq(0); + expect(this.view.getTopSelectable()).toBe($first); + }); + }); + + describe('#update', function() { + it('should invoke update on each dataset if valid update', function() { + this.view.update('fiz'); + expect(this.dataset.update).toHaveBeenCalled(); + }); + + it('should return true when valid update', function() { + expect(this.view.update('fiz')).toBe(true); + }); + + it('should return false when invalid update', function() { + this.view.update('fiz'); + expect(this.view.update('fiz')).toBe(false); + }); + }); + + describe('#empty', function() { + it('should set query to null', function() { + this.view.query = 'fiz'; + this.view.empty(); + + expect(this.view.query).toBeNull(); + }); + + it('should add empty class to node', function() { + this.$node.removeClass(www.classes.empty); + this.view.empty(); + + expect(this.$node).toHaveClass(www.classes.empty); + }); + + it('should invoke clear on each dataset', function() { + this.view.empty(); + expect(this.dataset.clear).toHaveBeenCalled(); + }); + }); + + describe('#destroy', function() { + it('should remove event handlers', function() { + var $node = this.view.$node; + + spyOn($node, 'off'); + this.view.destroy(); + expect($node.off).toHaveBeenCalledWith('.tt'); + }); + + it('should destroy its datasets', function() { + this.view.destroy(); + expect(this.dataset.destroy).toHaveBeenCalled(); + }); + + it('should set node element to dummy element', function() { + var $node = this.view.$node; + + this.view.destroy(); + expect(this.view.$node).not.toBe($node); + }); + }); +}); diff --git a/www/bower_components/typeahead.js/test/typeahead/typeahead_spec.js b/www/bower_components/typeahead.js/test/typeahead/typeahead_spec.js new file mode 100644 index 0000000..199e449 --- /dev/null +++ b/www/bower_components/typeahead.js/test/typeahead/typeahead_spec.js @@ -0,0 +1,1412 @@ +describe('Typeahead', function() { + var www, testData; + + www = WWW(); + + beforeEach(function() { + var $fixture, $input; + + jasmine.Input.useMock(); + jasmine.Dataset.useMock(); + jasmine.Menu.useMock(); + + setFixtures(''); + + $fixture = $('#jasmine-fixtures'); + this.$input = $fixture.find('input'); + + testData = { val: 'foo bar', obj: 'fiz' }; + + this.view = new Typeahead({ + input: new Input(), + menu: new Menu(), + eventBus: new EventBus({ el: this.$input }) + }, www); + + this.input = this.view.input; + this.menu = this.view.menu; + }); + + describe('on selectableClicked', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'selectableClicked'; + payload = $(''); + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onSelectableClicked'); + this.menu.trigger(eventName, payload); + expect(this.view._onSelectableClicked).not.toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + }); + + it('should select the selectable', function() { + spyOn(this.view, 'select'); + this.menu.trigger(eventName, payload); + expect(this.view.select).toHaveBeenCalledWith(payload); + }); + }); + }); + + describe('on asyncRequested', function() { + var eventName; + + beforeEach(function() { + eventName = 'asyncRequested'; + }); + + it('should trigger typeahead:asyncrequest', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:asyncrequest', spy); + this.menu.trigger(eventName); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('on asyncCanceled', function() { + var eventName; + + beforeEach(function() { + eventName = 'asyncCanceled'; + }); + + it('should trigger typeahead:asynccancel', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:asynccancel', spy); + this.menu.trigger(eventName); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('on asyncReceived', function() { + var eventName; + + beforeEach(function() { + eventName = 'asyncReceived'; + }); + + it('should trigger typeahead:asyncreceive', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:asyncreceive', spy); + this.menu.trigger(eventName); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('on datasetRendered', function() { + var eventName; + + beforeEach(function() { + eventName = 'datasetRendered'; + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onDatasetRendered'); + this.menu.trigger(eventName); + expect(this.view._onDatasetRendered).not.toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + }); + + it('should update the hint', function() { + this.input.hasOverflow.andReturn(false); + this.menu.getTopSelectable.andReturn($('')); + this.menu.getSelectableData.andReturn(testData); + this.input.getInputValue.andReturn(testData.val.slice(0, 2)); + + this.menu.trigger(eventName); + + expect(this.input.setHint).toHaveBeenCalled(); + }); + + it('should trigger typeahead:render', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:render', spy); + this.menu.trigger(eventName); + expect(spy).toHaveBeenCalled(); + }); + }); + }); + + describe('on datasetCleared', function() { + var eventName; + + beforeEach(function() { + eventName = 'datasetCleared'; + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onDatasetCleared'); + this.menu.trigger(eventName); + expect(this.view._onDatasetCleared).not.toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + }); + + it('should update the hint', function() { + this.input.hasOverflow.andReturn(false); + this.menu.getTopSelectable.andReturn($('')); + this.menu.getSelectableData.andReturn(testData); + this.input.getInputValue.andReturn(testData.val.slice(0, 2)); + + this.menu.trigger(eventName); + + expect(this.input.setHint).toHaveBeenCalled(); + }); + }); + }); + + describe('on focused', function() { + var eventName; + + beforeEach(function() { + eventName = 'focused'; + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should activate typeahead', function() { + this.input.trigger(eventName); + expect(this.view.isActive()).toBe(true); + }); + + it('should open menu', function() { + this.input.trigger(eventName); + expect(this.menu.open).toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + }); + + it('should open menu', function() { + this.input.trigger(eventName); + expect(this.menu.open).toHaveBeenCalled(); + }); + + it('should update menu for query if minLength met', function() { + this.input.getQuery.andReturn('bar'); + this.input.trigger(eventName); + expect(this.menu.update).toHaveBeenCalledWith('bar'); + }); + + it('should not update menu for query if minLength not met', function() { + this.view.minLength = 1; + this.input.getQuery.andReturn(''); + this.input.trigger(eventName); + expect(this.menu.update).not.toHaveBeenCalled(); + }); + }); + }); + + describe('on blurred', function() { + var eventName; + + beforeEach(function() { + eventName = 'blurred'; + }); + + it('should trigger typeahead:change if query changed since focus', function() { + var spy = jasmine.createSpy(); + + this.input.hasQueryChangedSinceLastFocus.andReturn(true); + this.$input.on('typeahead:change', spy); + + this.input.trigger(eventName); + + expect(spy).toHaveBeenCalled(); + }); + + it('should not trigger typeahead:change if query has not changed since focus', function() { + var spy = jasmine.createSpy(); + + this.input.hasQueryChangedSinceLastFocus.andReturn(false); + this.$input.on('typeahead:change', spy); + + this.input.trigger(eventName); + + expect(spy).not.toHaveBeenCalled(); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + }); + + it('should deactivate typeahead', function() { + this.input.trigger(eventName); + expect(this.view.isActive()).toBe(false); + }); + }); + }); + + describe('on enterKeyed', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'enterKeyed'; + payload = jasmine.createSpyObj('event', ['preventDefault']); + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onEnterKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onEnterKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active and menu is closed', function() { + beforeEach(function() { + this.view.activate(); + this.menu.isOpen.andReturn(false); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onEnterKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onEnterKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active and menu is open', function() { + beforeEach(function() { + this.view.activate(); + this.menu.isOpen.andReturn(true); + }); + + it('should select selectable if there is an active one', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'select'); + this.menu.getActiveSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(this.view.select).toHaveBeenCalledWith($el); + }); + + it('should prevent default if active selectale ', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'select').andReturn(true); + this.menu.getActiveSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(payload.preventDefault).toHaveBeenCalled(); + }); + + it('should not select selectable if there is no active one', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'select'); + + this.input.trigger(eventName, payload); + + expect(this.view.select).not.toHaveBeenCalledWith($el); + }); + + it('should not prevent default if no active selectale', function() { + var $el; + + spyOn(this.view, 'select').andReturn(true); + $el = $(''); + + this.input.trigger(eventName, payload); + + expect(payload.preventDefault).not.toHaveBeenCalled(); + }); + + it('should not prevent default if selection of active selectable fails', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'select').andReturn(false); + this.menu.getActiveSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(payload.preventDefault).not.toHaveBeenCalled(); + }); + }); + }); + + describe('on tabKeyed', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'tabKeyed'; + payload = jasmine.createSpyObj('event', ['preventDefault']); + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onTabKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onTabKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active and menu is closed', function() { + beforeEach(function() { + this.view.activate(); + this.menu.isOpen.andReturn(false); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onTabKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onTabKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active and menu is open', function() { + beforeEach(function() { + this.view.activate(); + this.menu.isOpen.andReturn(true); + }); + + it('should select selectable if there is an active one', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'select'); + this.menu.getActiveSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(this.view.select).toHaveBeenCalledWith($el); + }); + + it('should prevent default if active selectale', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'select').andReturn(true); + this.menu.getActiveSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(payload.preventDefault).toHaveBeenCalled(); + }); + + it('should not select selectable if there is no active one', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'select'); + + this.input.trigger(eventName, payload); + + expect(this.view.select).not.toHaveBeenCalledWith($el); + }); + + it('should not prevent default if no active selectale', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'select'); + + this.input.trigger(eventName, payload); + + expect(payload.preventDefault).not.toHaveBeenCalled(); + }); + + it('should not prevent default if selection of active selectable fails', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'select').andReturn(false); + this.menu.getActiveSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(payload.preventDefault).not.toHaveBeenCalled(); + }); + + it('should autocomplete to top suggestion', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'autocomplete'); + this.menu.getTopSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(this.view.autocomplete).toHaveBeenCalledWith($el); + }); + + it('should prevent default behavior of DOM event if autocompletion succeeds', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'autocomplete').andReturn(true); + this.menu.getTopSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(payload.preventDefault).toHaveBeenCalled(); + }); + + it('should not prevent default behavior of DOM event if autocompletion fails', function() { + var $el; + + $el = $(''); + spyOn(this.view, 'autocomplete').andReturn(false); + this.menu.getTopSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(payload.preventDefault).not.toHaveBeenCalled(); + }); + }); + }); + + describe('on escKeyed', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'escKeyed'; + payload = jasmine.createSpyObj('event', ['preventDefault']); + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onEscKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onEscKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + }); + + it('should close', function() { + spyOn(this.view, 'close'); + this.input.trigger(eventName, payload); + expect(this.view.close).toHaveBeenCalled(); + }); + }); + }); + + describe('on upKeyed', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'upKeyed'; + payload = jasmine.createSpyObj('event', ['preventDefault']); + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onUpKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onUpKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + spyOn(this.view, 'moveCursor'); + }); + + it('should open menu', function() { + this.input.trigger(eventName, payload); + expect(this.menu.open).toHaveBeenCalled(); + }); + + it('should move cursor -1', function() { + this.input.trigger(eventName, payload); + expect(this.view.moveCursor).toHaveBeenCalledWith(-1); + }); + }); + }); + + describe('on downKeyed', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'downKeyed'; + payload = jasmine.createSpyObj('event', ['preventDefault']); + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onDownKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onDownKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + spyOn(this.view, 'moveCursor'); + }); + + it('should open menu', function() { + this.input.trigger(eventName, payload); + expect(this.menu.open).toHaveBeenCalled(); + }); + + it('should move cursor +1', function() { + this.input.trigger(eventName, payload); + expect(this.view.moveCursor).toHaveBeenCalledWith(1); + }); + }); + }); + + describe('on leftKeyed', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'leftKeyed'; + payload = jasmine.createSpyObj('event', ['preventDefault']); + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onLeftKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onLeftKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active and menu is closed', function() { + beforeEach(function() { + this.view.activate(); + this.menu.isOpen.andReturn(false); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onLeftKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onLeftKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active and menu is open', function() { + beforeEach(function() { + this.view.activate(); + this.menu.isOpen.andReturn(true); + }); + + it('should autocomplete if language is rtl and text cursor is at end', function() { + var $el = $(''); + + spyOn(this.view, 'autocomplete'); + this.view.dir = 'rtl'; + this.input.isCursorAtEnd.andReturn(true); + this.menu.getTopSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(this.view.autocomplete).toHaveBeenCalledWith($el); + }); + }); + }); + + describe('on rightKeyed', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'rightKeyed'; + payload = jasmine.createSpyObj('event', ['preventDefault']); + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onRightKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onRightKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active and menu is closed', function() { + beforeEach(function() { + this.view.activate(); + this.menu.isOpen.andReturn(false); + }); + + it('should do nothing', function() { + spyOn(this.view, '_onRightKeyed'); + this.input.trigger(eventName, payload); + expect(this.view._onRightKeyed).not.toHaveBeenCalled(); + }); + }); + + describe('when active and menu is open', function() { + beforeEach(function() { + this.view.activate(); + this.menu.isOpen.andReturn(true); + }); + + it('should autocomplete if language is rtl and text cursor is at end', function() { + var $el = $(''); + + spyOn(this.view, 'autocomplete'); + this.view.dir = 'ltr'; + this.input.isCursorAtEnd.andReturn(true); + this.menu.getTopSelectable.andReturn($el); + + this.input.trigger(eventName, payload); + + expect(this.view.autocomplete).toHaveBeenCalledWith($el); + }); + }); + }); + + describe('on queryChanged', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'queryChanged'; + payload = ''; + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should not open menu', function() { + this.input.trigger(eventName, payload); + expect(this.menu.open).not.toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + this.view.open(); + }); + + it('should open menu', function() { + this.input.trigger(eventName, payload); + expect(this.menu.open).toHaveBeenCalled(); + }); + + it('should empty menu if minLength is not satisfied', function() { + this.view.minLength = 100; + this.input.trigger(eventName, payload); + + expect(this.menu.empty).toHaveBeenCalled(); + }); + + it('should update menu if minLength is satisfied', function() { + this.input.trigger(eventName, 'fiz'); + expect(this.menu.update).toHaveBeenCalledWith('fiz'); + }); + }); + }); + + describe('on whitespaceChanged', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'whitespaceChanged'; + payload = ''; + }); + + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should not open menu', function() { + this.input.trigger(eventName, payload); + expect(this.menu.open).not.toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + }); + + it('should open menu', function() { + this.input.trigger(eventName, payload); + expect(this.menu.open).toHaveBeenCalled(); + }); + + it('should update the hint', function() { + this.input.hasFocus.andReturn(true); + this.input.hasOverflow.andReturn(false); + this.menu.getTopSelectable.andReturn($('')); + this.menu.getSelectableData.andReturn(testData); + + this.input.getInputValue.andReturn(testData.val.slice(0, 2)); + + this.input.trigger(eventName, payload); + + expect(this.input.setHint).toHaveBeenCalledWith(testData.val); + }); + }); + }); + + describe('on langDirChanged', function() { + var eventName, payload; + + beforeEach(function() { + eventName = 'langDirChanged'; + payload = 'rtl'; + }); + + it('should set direction of menu if direction changed', function() { + this.view.dir = 'ltr'; + + this.input.trigger(eventName, payload); + + expect(this.view.dir).toBe(payload); + expect(this.menu.setLanguageDirection).toHaveBeenCalled(); + }); + + it('should do nothing if direction did not changed', function() { + this.view.dir = payload; + + this.input.trigger(eventName, payload); + + expect(this.view.dir).toBe(payload); + expect(this.menu.setLanguageDirection).not.toHaveBeenCalled(); + }); + }); + + describe('#isActive', function() { + it('should return true if active', function() { + this.view.activate(); + expect(this.view.isActive()).toBe(true); + }); + + it('should return false if active', function() { + this.view.deactivate(); + expect(this.view.isActive()).toBe(false); + }); + }); + + describe('#isEnabled', function() { + it('should returned enabled status', function() { + this.view.enable(); + expect(this.view.isEnabled()).toBe(true); + this.view.disable(); + expect(this.view.isEnabled()).toBe(false); + }); + }); + + describe('#enable', function() { + it('should set enabled to true', function() { + this.view.enable(); + expect(this.view.isEnabled()).toBe(true); + }); + }); + + describe('#disable', function() { + it('should set enabled to false', function() { + this.view.disable(); + expect(this.view.isEnabled()).toBe(false); + }); + }); + + describe('#activate', function() { + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + }); + + it('should do nothing', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforeactive', spy); + this.view.activate(); + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('when idle and disabled', function() { + beforeEach(function() { + this.view.disable(); + this.view.activate(); + }); + + it('should do nothing', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforeactive', spy); + this.view.activate(); + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('when idle and enabled', function() { + beforeEach(function() { + this.view.enable(); + this.view.deactivate(); + }); + + it('should trigger typeahead:beforeactive', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforeactive', spy); + this.view.activate(); + expect(spy).toHaveBeenCalled(); + }); + + it('should support cancellation', function() { + var spy1, spy2; + + spy1 = jasmine.createSpy().andCallFake(prevent); + spy2 = jasmine.createSpy(); + this.$input.on('typeahead:beforeactive', spy1); + this.$input.on('typeahead:active', spy2); + + this.view.activate(); + + expect(spy1).toHaveBeenCalled(); + expect(spy2).not.toHaveBeenCalled(); + }); + + it('should change state to active', function() { + expect(this.view.isActive()).toBe(false); + this.view.activate(); + expect(this.view.isActive()).toBe(true); + }); + + it('should trigger typeahead:active if not canceled', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:active', spy); + this.view.activate(); + expect(spy).toHaveBeenCalled(); + }); + }); + }); + + describe('#deactivate', function() { + describe('when idle', function() { + beforeEach(function() { + this.view.deactivate(); + }); + + it('should do nothing', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforeidle', spy); + this.view.deactivate(); + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('when active', function() { + beforeEach(function() { + this.view.activate(); + }); + + it('should trigger typeahead:beforeidle', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforeidle', spy); + this.view.deactivate(); + expect(spy).toHaveBeenCalled(); + }); + + it('should support cancellation', function() { + var spy1, spy2; + + spy1 = jasmine.createSpy().andCallFake(prevent); + spy2 = jasmine.createSpy(); + this.$input.on('typeahead:beforeidle', spy1); + this.$input.on('typeahead:idle', spy2); + + this.view.deactivate(); + + expect(spy1).toHaveBeenCalled(); + expect(spy2).not.toHaveBeenCalled(); + }); + + it('should close', function() { + spyOn(this.view, 'close'); + this.view.deactivate(); + expect(this.view.close).toHaveBeenCalled(); + }); + + it('should change state to idle', function() { + expect(this.view.isActive()).toBe(true); + this.view.deactivate(); + expect(this.view.isActive()).toBe(false); + }); + + it('should trigger typeahead:idle if not canceled', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:idle', spy); + this.view.deactivate(); + expect(spy).toHaveBeenCalled(); + }); + }); + }); + + describe('#isOpen', function() { + it('should return true if open', function() { + this.menu.isOpen.andReturn(true); + expect(this.view.isOpen()).toBe(true); + }); + + it('should return false if closed', function() { + this.menu.isOpen.andReturn(false); + expect(this.view.isOpen()).toBe(false); + }); + }); + + describe('#open', function() { + describe('when open', function() { + beforeEach(function() { + spyOn(this.view, 'isOpen').andReturn(true); + }); + + it('should do nothing', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforeopen', spy); + this.view.open(); + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('when closed', function() { + beforeEach(function() { + spyOn(this.view, 'isOpen').andReturn(false); + }); + + it('should trigger typeahead:beforeopen', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforeopen', spy); + this.view.open(); + expect(spy).toHaveBeenCalled(); + }); + + it('should support cancellation', function() { + var spy1, spy2; + + spy1 = jasmine.createSpy().andCallFake(prevent); + spy2 = jasmine.createSpy(); + this.$input.on('typeahead:beforeopen', spy1); + this.$input.on('typeahead:open', spy2); + + this.view.open(); + + expect(spy1).toHaveBeenCalled(); + expect(spy2).not.toHaveBeenCalled(); + }); + + it('should open menu', function() { + this.view.open(); + expect(this.menu.open).toHaveBeenCalled(); + }); + + it('should update hint if active', function() { + spyOn(this.view, 'isActive').andReturn(true); + + this.input.hasOverflow.andReturn(false); + this.menu.getTopSelectable.andReturn($('')); + this.menu.getSelectableData.andReturn(testData); + this.input.getInputValue.andReturn(testData.val.slice(0, 2)); + + this.view.open(); + + expect(this.input.setHint).toHaveBeenCalled(); + }); + + it('should trigger typeahead:open if not canceled', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:open', spy); + this.view.open(); + expect(spy).toHaveBeenCalled(); + }); + }); + }); + + describe('#close', function() { + describe('when closed', function() { + beforeEach(function() { + spyOn(this.view, 'isOpen').andReturn(false); + }); + + it('should do nothing', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforeclose', spy); + this.view.open(); + expect(spy).not.toHaveBeenCalled(); + }); + }); + + describe('when open', function() { + beforeEach(function() { + spyOn(this.view, 'isOpen').andReturn(true); + }); + + it('should trigger typeahead:beforeclose', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforeclose', spy); + this.view.close(); + expect(spy).toHaveBeenCalled(); + }); + + it('should support cancellation', function() { + var spy1, spy2; + + spy1 = jasmine.createSpy().andCallFake(prevent); + spy2 = jasmine.createSpy(); + this.$input.on('typeahead:beforeclose', spy1); + this.$input.on('typeahead:close', spy2); + + this.view.close(); + + expect(spy1).toHaveBeenCalled(); + expect(spy2).not.toHaveBeenCalled(); + }); + + it('should close menu', function() { + this.view.close(); + expect(this.menu.close).toHaveBeenCalled(); + }); + + it('should clear hint', function() { + this.view.close(); + expect(this.input.clearHint).toHaveBeenCalled(); + }); + + it('should trigger typeahead:close if not canceled', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:close', spy); + this.view.close(); + expect(spy).toHaveBeenCalled(); + }); + }); + }); + + describe('#getVal', function() { + it('should return the current query', function() { + this.input.getQuery.andReturn('woah'); + expect(this.view.getVal()).toBe('woah'); + }); + }); + + describe('#setVal', function() { + it('should update query', function() { + this.input.hasFocus.andReturn(true); + this.view.setVal('woah'); + expect(this.input.setQuery).toHaveBeenCalledWith('woah'); + }); + }); + + + + + + describe('#select', function() { + it('should do nothing if element is not a selectable', function() { + var spy; + + this.menu.getSelectableData.andReturn(null); + this.$input.on('typeahead:beforeselect', spy = jasmine.createSpy()); + + this.view.select($('')); + + expect(spy).not.toHaveBeenCalled(); + }); + + it('should trigger typeahead:beforeselect', function() { + var spy; + + this.menu.getSelectableData.andReturn(testData); + this.$input.on('typeahead:beforeselect', spy = jasmine.createSpy()); + + this.view.select($('')); + + expect(spy).toHaveBeenCalled(); + }); + + it('should support cancellation', function() { + var spy1, spy2; + + spy1 = jasmine.createSpy().andCallFake(prevent); + spy2 = jasmine.createSpy(); + + this.menu.getSelectableData.andReturn(testData); + this.$input.on('typeahead:beforeselect', spy1).on('typeahead:select', spy2); + + this.view.select($('')); + + expect(spy1).toHaveBeenCalled(); + expect(spy2).not.toHaveBeenCalled(); + }); + + it('should update query', function() { + this.menu.getSelectableData.andReturn(testData); + this.view.select($('')); + expect(this.input.setQuery).toHaveBeenCalledWith(testData.val, true); + }); + + it('should trigger typeahead:select', function() { + var spy; + + this.menu.getSelectableData.andReturn(testData); + this.$input.on('typeahead:select', spy = jasmine.createSpy()); + + this.view.select($('')); + + expect(spy).toHaveBeenCalled(); + }); + + it('should close', function() { + spyOn(this.view, 'close'); + this.menu.getSelectableData.andReturn(testData); + + this.view.select($('')); + + expect(this.view.close).toHaveBeenCalled(); + }); + }); + + describe('#autocomplete', function() { + it('should abort if the query matches the top suggestion', function() { + var spy; + + this.input.getQuery.andReturn(testData.val); + this.menu.getSelectableData.andReturn(testData); + this.$input.on('typeahead:beforeautocomplete', spy = jasmine.createSpy()); + + this.view.autocomplete($('')); + + expect(spy).not.toHaveBeenCalled(); + }); + + it('should trigger typeahead:beforeautocomplete', function() { + var spy; + + this.menu.getSelectableData.andReturn(testData); + this.$input.on('typeahead:beforeautocomplete', spy = jasmine.createSpy()); + + this.view.autocomplete($('')); + + expect(spy).toHaveBeenCalled(); + }); + + it('should support cancellation', function() { + var spy1, spy2; + + spy1 = jasmine.createSpy().andCallFake(prevent); + spy2 = jasmine.createSpy(); + this.$input.on('typeahead:beforeautocomplete', spy1); + this.$input.on('typeahead:autocomplete', spy2); + this.menu.getSelectableData.andReturn(testData); + + this.view.autocomplete($('')); + + expect(spy1).toHaveBeenCalled(); + expect(spy2).not.toHaveBeenCalled(); + }); + + it('should update the query', function() { + this.menu.getSelectableData.andReturn(testData); + this.view.autocomplete($('')); + expect(this.input.setQuery).toHaveBeenCalledWith(testData.val); + }); + + it('should trigger typeahead:autocomplete', function() { + var spy; + + this.menu.getSelectableData.andReturn(testData); + this.$input.on('typeahead:autocomplete', spy = jasmine.createSpy()); + + this.view.autocomplete($('')); + + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('#moveCursor', function() { + beforeEach(function() { + this.input.getQuery.andReturn('foo'); + }); + + it('should move cursor if minLength is not satisfied', function() { + var spy = jasmine.createSpy(); + + this.view.minLength = 100; + this.menu.update.andReturn(true); + this.$input.on('typeahead:beforecursorchange', spy); + + this.view.moveCursor(1); + + expect(spy).toHaveBeenCalled(); + }); + + it('should move cursor if invalid update', function() { + var spy = jasmine.createSpy(); + + this.menu.update.andReturn(false); + this.$input.on('typeahead:beforecursorchange', spy); + + this.view.moveCursor(1); + + expect(spy).toHaveBeenCalled(); + }); + + it('should not move cursor if valid update', function() { + var spy = jasmine.createSpy(); + + this.menu.update.andReturn(true); + this.$input.on('typeahead:beforecursorchange', spy); + + this.view.moveCursor(1); + + expect(spy).not.toHaveBeenCalled(); + }); + + it('should trigger typeahead:beforecursorchange', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:beforecursorchange', spy); + this.view.moveCursor(1); + expect(spy).toHaveBeenCalled(); + }); + + it('should support cancellation', function() { + var spy = jasmine.createSpy().andCallFake(prevent); + + this.$input.on('typeahead:beforecursorchange', spy); + this.view.moveCursor(1); + expect(this.menu.setCursor).not.toHaveBeenCalled(); + }); + + it('should update the input value if moved to selectable', function() { + this.menu.getSelectableData.andReturn(testData); + this.view.moveCursor(1); + expect(this.input.setInputValue).toHaveBeenCalledWith(testData.val); + }); + + it('should reset the input value if moved to input', function() { + this.view.moveCursor(1); + expect(this.input.resetInputValue).toHaveBeenCalled(); + }); + + it('should update the hint', function() { + this.input.hasOverflow.andReturn(false); + this.menu.getTopSelectable.andReturn($('')); + this.menu.getSelectableData.andCallFake(fake); + this.input.getInputValue.andReturn(testData.val.slice(0, 1)); + + this.view.moveCursor(1); + + expect(this.input.setHint).toHaveBeenCalledWith(testData.val); + + function fake($el) { + return ($el && $el.prop('tagName') === 'FIZ') ? testData : null; + } + }); + + it('should trigger cursorchange after setting cursor', function() { + var spy = jasmine.createSpy(); + + this.$input.on('typeahead:cursorchange', spy); + this.view.moveCursor(1); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('#destroy', function() { + it('should destroy input', function() { + this.view.destroy(); + + expect(this.input.destroy).toHaveBeenCalled(); + }); + + it('should destroy menu', function() { + this.view.destroy(); + + expect(this.menu.destroy).toHaveBeenCalled(); + }); + }); + + function prevent($e) { $e.preventDefault(); } +}); + diff --git a/www/bower_components/typeahead.js/typeahead.js.jquery.json b/www/bower_components/typeahead.js/typeahead.js.jquery.json new file mode 100644 index 0000000..ac2689e --- /dev/null +++ b/www/bower_components/typeahead.js/typeahead.js.jquery.json @@ -0,0 +1,40 @@ +{ + "licenses": [ + { + "url": "https://github.com/twitter/typeahead.js/blob/master/LICENSE" + } + ], + "dependencies": { + "jquery": ">=1.7" + }, + "docs": "https://github.com/twitter/typeahead.js", + "demo": "http://twitter.github.com/typeahead.js/examples", + "name": "typeahead.js", + "title": "typeahead.js", + "author": { + "name": "Twitter, Inc.", + "url": "https://twitter.com/twitteross" + }, + "description": "fast and fully-featured autocomplete library", + "keywords": [ + "typeahead", + "autocomplete" + ], + "homepage": "http://twitter.github.com/typeahead.js", + "bugs": "https://github.com/twitter/typeahead.js/issues", + "maintainers": [ + { + "name": "Jake Harding", + "url": "https://twitter.com/JakeHarding" + }, + { + "name": "Tim Trueman", + "url": "https://twitter.com/timtrueman" + }, + { + "name": "Veljko Skarich", + "url": "https://twitter.com/vskarich" + } + ], + "version": "0.11.1" +} \ No newline at end of file diff --git a/www/css/_map.scss b/www/css/_map.scss new file mode 100644 index 0000000..9603862 --- /dev/null +++ b/www/css/_map.scss @@ -0,0 +1,125 @@ +/* + Group map on /group/map +*/ +.group-map { + height: 100vh; + width: 100%; + + .groups { + width: 100%; + height: calc(100vh - 130px); + overflow-y: scroll; + + .groupBlock { + cursor: pointer; + + &.focused { + //background-color: #AAA; + //.clickable has already a hover state + } + + img{ + width:92px; + float:left; + margin-right:12px; + } + } + } + + //reset padding in containers + #mapContainer{padding:0;} + + .form-group-container{ + padding-top: 35px; + //the autocomplete field + .form-group { + display: inline-block; + width: 50%; + /*margin-left: auto; + margin-right: auto; + margin-top: 5rem; + */ + } + } + + #logo{ + background-image: url(/img/logo3.png); + background-repeat: no-repeat; + background-position: center; + height: 130px; + } + + .leaflet-container{ + //compute map height + height: calc(100vh - 130px); + } + +.leaflet-popup-content{ + .groupName{ + text-align: center; + font-weight: bold; + font-size: 1.2em; + font-family:Cabin,Helvetica,Arial,sans-serif; + margin-top: 8px; + } + .groupImage{ + max-width: 120px; + max-height: 120px; + display:block; + margin:auto; + } + a{ + color:#3c763d; + } + +} + /* + AUTOCOMPLETE + */ + .autocomplete-input, + .autocomplete-input:focus, + .autocomplete-input:active { + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08); + border: honeydew; + display: block; + width: 100%; + padding: 16px; + font-size: 16px; + border-radius: 2px; + outline: none; + } + + .autocomplete-results { + position: fixed; + width: 50%; + border-bottom: honeydew; + border-left: honeydew; + border-right: honeydew; + border-top: 1px solid #e6e6e6; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); + border-radius: 0 0 2px 2px; + z-index: 1001; + } + + .autocomplete-item { + padding: 4px; + text-align: left; + } + + .autocomplete-icon { + margin-right: 8px; + } + + .fa.fa-map-marker.header { + color: #e53935; + } + + + .icon { + cursor: pointer; + } + + .popup { + cursor: pointer; + } +} diff --git a/www/css/_shop.scss b/www/css/_shop.scss new file mode 100644 index 0000000..3c64381 --- /dev/null +++ b/www/css/_shop.scss @@ -0,0 +1,129 @@ +/* + SHOP +*/ +.shop{ + + .shop-header { + display: flex; + justify-content: space-between; + width: 70%; + padding: 1rem; + margin-bottom: 2rem; + background-color: white; + } + + .categories { + width: 70%; + + .products { + display: flex; + flex-wrap: wrap; + + .product { + display: flex; + width: 250px; + margin-right: 1rem; + margin-bottom: 1rem; + cursor: default; + user-select: none; + + img { + object-fit: cover; + } + + .body { + width: 100%; + background: white; + padding: 10px; + } + + input { + width: 50%; + } + + .button { + cursor: pointer; + } + } + } + } + + .filters { + position: fixed; + bottom: 140px; + right: 47px; + width: 25%; + padding: 1rem; + background-color: white; + cursor: default; + + .filter { + color: #BBB; + cursor: pointer; + + &.active { + color: #070; + } + } + } + + .cart { + position: fixed; + top: 130px; + right: 47px; + width: 25%; + padding: 1rem; + background-color: white; + cursor: default; + + .products-to-order { + margin-bottom: 2rem; + + .product-to-order { + display: flex; + justify-content: space-between; + margin-bottom: .5rem; + + div { + display: flex; + } + + .cart-action-buttons > * { + display: block; + margin-left: .5rem; + width: 2rem; + width: 2rem; + color: white; + text-align: center; + background-color: #070; + cursor: pointer; + line-height: 1.8rem; + border-radius: 50%; + } + } + } + + .cart-footer { + bottom: 1rem; + } + + .total { + display: flex; + justify-content: space-between; + } + + .order-button { + margin-top: 1rem; + padding: .5rem; + background-color: #070; + color: white; + cursor: pointer; + + &--disabled { + background-color: #555; + cursor: not-allowed; + } + } + } + +} diff --git a/www/css/bs3_variables.less b/www/css/bs3_variables.less new file mode 100644 index 0000000..2ae1fdc --- /dev/null +++ b/www/css/bs3_variables.less @@ -0,0 +1,872 @@ +// +// Variables +// -------------------------------------------------- + +//Cagette vars +@lightBeige:#efe9d5; // +@darkBrown:rgb(88, 56, 22); + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +@gray-base: #000; +@gray-darker: lighten(@gray-base, 13.5%); // #222 +@gray-dark: lighten(@gray-base, 20%); // #333 +@gray: lighten(@gray-base, 33.5%); // #555 +@gray-light: lighten(@gray-base, 46.7%); // #777 +@gray-lighter: lighten(@gray-base, 93.5%); // #eee + +@brand-primary: rgb(132, 189, 85); +@brand-success: rgb(255, 228, 0); +@brand-info: rgb(165, 63, 161); +@brand-warning: rgb(240, 173, 78); +@brand-danger: rgb(217, 83, 79); + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for ``. +@body-bg: #F8F4E5; +//** Global text color on ``. +@text-color: @gray-dark; + +//** Global textual link color. +@link-color: #3c763d; +//** Link hover color set via `darken()` function. +@link-hover-color: darken(@link-color, 15%); +//** Link hover decoration. +@link-hover-decoration: underline; + + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +@font-family-sans-serif: Cabin, Helvetica, Arial, sans-serif; +@font-family-serif: Georgia, "Times New Roman", Times, serif; +//** Default monospace fonts for ``, ``, and `
    `.
    +@font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace;
    +@font-family-base:        @font-family-sans-serif;
    +
    +@font-size-base:          16px;
    +@font-size-large:         ceil((@font-size-base * 1.25)); // ~18px
    +@font-size-small:         ceil((@font-size-base * 0.85)); // ~12px
    +
    +@font-size-h1:            floor((@font-size-base * 2.6)); // ~36px
    +@font-size-h2:            floor((@font-size-base * 2.15)); // ~30px
    +@font-size-h3:            ceil((@font-size-base * 1.7)); // ~24px
    +@font-size-h4:            ceil((@font-size-base * 1.25)); // ~18px
    +@font-size-h5:            @font-size-base;
    +@font-size-h6:            ceil((@font-size-base * 0.85)); // ~12px
    +
    +//** Unit-less `line-height` for use in components like buttons.
    +@line-height-base:        1.428571429; // 20/14
    +//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
    +@line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px
    +
    +//** By default, this inherits from the ``.
    +@headings-font-family:    inherit;
    +@headings-font-weight:    700;
    +@headings-line-height:    1.1;
    +@headings-color:          inherit;
    +
    +
    +//== Iconography
    +//
    +//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
    +
    +//** Load fonts from this directory.
    +@icon-font-path:          "../fonts/";
    +//** File name for all font files.
    +@icon-font-name:          "glyphicons-halflings-regular";
    +//** Element ID within SVG icon file.
    +@icon-font-svg-id:        "glyphicons_halflingsregular";
    +
    +
    +//== Components
    +//
    +//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
    +
    +@padding-base-vertical:     6px;
    +@padding-base-horizontal:   12px;
    +
    +@padding-large-vertical:    10px;
    +@padding-large-horizontal:  16px;
    +
    +@padding-small-vertical:    5px;
    +@padding-small-horizontal:  10px;
    +
    +@padding-xs-vertical:       1px;
    +@padding-xs-horizontal:     5px;
    +
    +@line-height-large:         1.3333333; // extra decimals for Win 8.1 Chrome
    +@line-height-small:         1.5;
    +
    +@border-radius-base:        4px;
    +@border-radius-large:       6px;
    +@border-radius-small:       3px;
    +
    +//** Global color for active items (e.g., navs or dropdowns).
    +@component-active-color:    #fff;
    +//** Global background color for active items (e.g., navs or dropdowns).
    +@component-active-bg:       @brand-primary;
    +
    +//** Width of the `border` for generating carets that indicate dropdowns.
    +@caret-width-base:          4px;
    +//** Carets increase slightly in size for larger components.
    +@caret-width-large:         5px;
    +
    +
    +//== Tables
    +//
    +//## Customizes the `.table` component with basic values, each used across all table variations.
    +
    +//** Padding for `
    `s and ``s. +@table-cell-padding: 8px; +//** Padding for cells in `.table-condensed`. +@table-condensed-cell-padding: 5px; + +//** Default background color used for all tables. +@table-bg: transparent; +//** Background color used for `.table-striped`. +@table-bg-accent: #f9f9f9; +//** Background color used for `.table-hover`. +@table-bg-hover: #f5f5f5; +@table-bg-active: @table-bg-hover; + +//** Border color for table and cell borders. +@table-border-color: #ddd; + + +//== Buttons +// +//## For each of Bootstrap's buttons, define text, background and border color. + +@btn-font-weight: normal; + +@btn-default-color: #333; +@btn-default-bg: #fff; +@btn-default-border: #ccc; + +@btn-primary-color: #fff; +@btn-primary-bg: @brand-primary; +@btn-primary-border: darken(@btn-primary-bg, 5%); + +@btn-success-color: rgb(88, 56, 22); +@btn-success-bg: @brand-success; +@btn-success-border: darken(@btn-success-bg, 5%); + +@btn-info-color: #fff; +@btn-info-bg: @brand-info; +@btn-info-border: darken(@btn-info-bg, 5%); + +@btn-warning-color: #fff; +@btn-warning-bg: @brand-warning; +@btn-warning-border: darken(@btn-warning-bg, 5%); + +@btn-danger-color: #fff; +@btn-danger-bg: @brand-danger; +@btn-danger-border: darken(@btn-danger-bg, 5%); + +@btn-link-disabled-color: @gray-light; + +// Allows for customizing button radius independently from global border radius +@btn-border-radius-base: @border-radius-base; +@btn-border-radius-large: @border-radius-large; +@btn-border-radius-small: @border-radius-small; + + +//== Forms +// +//## + +//** `` background color +@input-bg: #fff; +//** `` background color +@input-bg-disabled: @gray-lighter; + +//** Text color for ``s +@input-color: @gray; +//** `` border color +@input-border: #ccc; + +// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4 +//** Default `.form-control` border radius +// This has no effect on ``s in CSS. +@input-border-radius: @border-radius-base; +//** Large `.form-control` border radius +@input-border-radius-large: @border-radius-large; +//** Small `.form-control` border radius +@input-border-radius-small: @border-radius-small; + +//** Border color for inputs on focus +@input-border-focus: #66afe9; + +//** Placeholder text color +@input-color-placeholder: #999; + +//** Default `.form-control` height +@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2); +//** Large `.form-control` height +@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2); +//** Small `.form-control` height +@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2); + +//** `.form-group` margin +@form-group-margin-bottom: 15px; + +@legend-color: @gray-dark; +@legend-border-color: #e5e5e5; + +//** Background color for textual input addons +@input-group-addon-bg: @gray-lighter; +//** Border color for textual input addons +@input-group-addon-border-color: @input-border; + +//** Disabled cursor for form controls and buttons. +@cursor-disabled: not-allowed; + + +//== Dropdowns +// +//## Dropdown menu container and contents. + +//** Background for the dropdown menu. +@dropdown-bg: #fff; +//** Dropdown menu `border-color`. +@dropdown-border: rgba(0,0,0,.15); +//** Dropdown menu `border-color` **for IE8**. +@dropdown-fallback-border: #ccc; +//** Divider color for between dropdown items. +@dropdown-divider-bg: #e5e5e5; + +//** Dropdown link text color. +@dropdown-link-color: @gray-dark; +//** Hover color for dropdown links. +@dropdown-link-hover-color: darken(@gray-dark, 5%); +//** Hover background for dropdown links. +@dropdown-link-hover-bg: #f5f5f5; + +//** Active dropdown menu item text color. +@dropdown-link-active-color: @component-active-color; +//** Active dropdown menu item background color. +@dropdown-link-active-bg: @component-active-bg; + +//** Disabled dropdown menu item background color. +@dropdown-link-disabled-color: @gray-light; + +//** Text color for headers within dropdown menus. +@dropdown-header-color: @gray-light; + +//** Deprecated `@dropdown-caret-color` as of v3.1.0 +@dropdown-caret-color: #000; + + +//-- Z-index master list +// +// Warning: Avoid customizing these values. They're used for a bird's eye view +// of components dependent on the z-axis and are designed to all work together. +// +// Note: These variables are not generated into the Customizer. + +@zindex-navbar: 1000; +@zindex-dropdown: 1000; +@zindex-popover: 1060; +@zindex-tooltip: 1070; +@zindex-navbar-fixed: 1030; +@zindex-modal-background: 1040; +@zindex-modal: 1050; + + +//== Media queries breakpoints +// +//## Define the breakpoints at which your layout will change, adapting to different screen sizes. + +// Extra small screen / phone +//** Deprecated `@screen-xs` as of v3.0.1 +@screen-xs: 480px; +//** Deprecated `@screen-xs-min` as of v3.2.0 +@screen-xs-min: @screen-xs; +//** Deprecated `@screen-phone` as of v3.0.1 +@screen-phone: @screen-xs-min; + +// Small screen / tablet +//** Deprecated `@screen-sm` as of v3.0.1 +@screen-sm: 768px; +@screen-sm-min: @screen-sm; +//** Deprecated `@screen-tablet` as of v3.0.1 +@screen-tablet: @screen-sm-min; + +// Medium screen / desktop +//** Deprecated `@screen-md` as of v3.0.1 +@screen-md: 992px; +@screen-md-min: @screen-md; +//** Deprecated `@screen-desktop` as of v3.0.1 +@screen-desktop: @screen-md-min; + +// Large screen / wide desktop +//** Deprecated `@screen-lg` as of v3.0.1 +@screen-lg: 1200px; +@screen-lg-min: @screen-lg; +//** Deprecated `@screen-lg-desktop` as of v3.0.1 +@screen-lg-desktop: @screen-lg-min; + +// So media queries don't overlap when required, provide a maximum +@screen-xs-max: (@screen-sm-min - 1); +@screen-sm-max: (@screen-md-min - 1); +@screen-md-max: (@screen-lg-min - 1); + + +//== Grid system +// +//## Define your custom responsive grid. + +//** Number of columns in the grid. +@grid-columns: 12; +//** Padding between columns. Gets divided in half for the left and right. +@grid-gutter-width: 30px; +// Navbar collapse +//** Point at which the navbar becomes uncollapsed. +@grid-float-breakpoint: @screen-sm-min; +//** Point at which the navbar begins collapsing. +@grid-float-breakpoint-max: (@grid-float-breakpoint - 1); + + +//== Container sizes +// +//## Define the maximum width of `.container` for different screen sizes. + +// Small screen / tablet +@container-tablet: (720px + @grid-gutter-width); +//** For `@screen-sm-min` and up. +@container-sm: @container-tablet; + +// Medium screen / desktop +@container-desktop: (940px + @grid-gutter-width); +//** For `@screen-md-min` and up. +@container-md: @container-desktop; + +// Large screen / wide desktop +@container-large-desktop: (1140px + @grid-gutter-width); +//** For `@screen-lg-min` and up. +@container-lg: @container-large-desktop; + + +//== Navbar +// +//## + +// Basics of a navbar +@navbar-height: 50px; +@navbar-margin-bottom: @line-height-computed; +@navbar-border-radius: @border-radius-base; +@navbar-padding-horizontal: floor((@grid-gutter-width / 2)); +@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2); +@navbar-collapse-max-height: 340px; + +@navbar-default-color: #777; +@navbar-default-bg: #f8f8f8; +@navbar-default-border: darken(@navbar-default-bg, 6.5%); + +// Navbar links +@navbar-default-link-color: #777; +@navbar-default-link-hover-color: #333; +@navbar-default-link-hover-bg: transparent; +@navbar-default-link-active-color: rgb(233, 82, 22); +@navbar-default-link-active-bg: @lightBeige; +@navbar-default-link-disabled-color: #ccc; +@navbar-default-link-disabled-bg: transparent; + +// Navbar brand label +@navbar-default-brand-color: @navbar-default-link-color; +@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%); +@navbar-default-brand-hover-bg: transparent; + +// Navbar toggle +@navbar-default-toggle-hover-bg: #ddd; +@navbar-default-toggle-icon-bar-bg: #888; +@navbar-default-toggle-border-color: #ddd; + + +//=== Inverted navbar +// Reset inverted navbar basics +@navbar-inverse-color: lighten(@gray-light, 15%); +@navbar-inverse-bg: #222; +@navbar-inverse-border: darken(@navbar-inverse-bg, 10%); + +// Inverted navbar links +@navbar-inverse-link-color: lighten(@gray-light, 15%); +@navbar-inverse-link-hover-color: #fff; +@navbar-inverse-link-hover-bg: transparent; +@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color; +@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%); +@navbar-inverse-link-disabled-color: #444; +@navbar-inverse-link-disabled-bg: transparent; + +// Inverted navbar brand label +@navbar-inverse-brand-color: @navbar-inverse-link-color; +@navbar-inverse-brand-hover-color: #fff; +@navbar-inverse-brand-hover-bg: transparent; + +// Inverted navbar toggle +@navbar-inverse-toggle-hover-bg: #333; +@navbar-inverse-toggle-icon-bar-bg: #fff; +@navbar-inverse-toggle-border-color: #333; + + +//== Navs +// +//## + +//=== Shared nav styles +@nav-link-padding: 10px 15px; +@nav-link-hover-bg: @gray-lighter; + +@nav-disabled-link-color: @gray-light; +@nav-disabled-link-hover-color: @gray-light; + +//== Tabs +@nav-tabs-border-color: #ddd; + +@nav-tabs-link-hover-border-color: @gray-lighter; + +@nav-tabs-active-link-hover-bg: @body-bg; +@nav-tabs-active-link-hover-color: @gray; +@nav-tabs-active-link-hover-border-color: #ddd; + +@nav-tabs-justified-link-border-color: #ddd; +@nav-tabs-justified-active-link-border-color: @body-bg; + +//== Pills +@nav-pills-border-radius: @border-radius-base; +@nav-pills-active-link-hover-bg: @component-active-bg; +@nav-pills-active-link-hover-color: @component-active-color; + + +//== Pagination +// +//## + +@pagination-color: @link-color; +@pagination-bg: #fff; +@pagination-border: #ddd; + +@pagination-hover-color: @link-hover-color; +@pagination-hover-bg: @gray-lighter; +@pagination-hover-border: #ddd; + +@pagination-active-color: #fff; +@pagination-active-bg: @brand-primary; +@pagination-active-border: @brand-primary; + +@pagination-disabled-color: @gray-light; +@pagination-disabled-bg: #fff; +@pagination-disabled-border: #ddd; + + +//== Pager +// +//## + +@pager-bg: @pagination-bg; +@pager-border: @pagination-border; +@pager-border-radius: 15px; + +@pager-hover-bg: @pagination-hover-bg; + +@pager-active-bg: @pagination-active-bg; +@pager-active-color: @pagination-active-color; + +@pager-disabled-color: @pagination-disabled-color; + + +//== Jumbotron +// +//## + +@jumbotron-padding: 30px; +@jumbotron-color: inherit; +@jumbotron-bg: @gray-lighter; +@jumbotron-heading-color: inherit; +@jumbotron-font-size: ceil((@font-size-base * 1.5)); +@jumbotron-heading-font-size: ceil((@font-size-base * 4.5)); + + +//== Form states and alerts +// +//## Define colors for form feedback states and, by default, alerts. + +@state-success-text: #3c763d; +@state-success-bg: #dff0d8; +@state-success-border: darken(spin(@state-success-bg, -10), 5%); + +@state-info-text: #31708f; +@state-info-bg: #d9edf7; +@state-info-border: darken(spin(@state-info-bg, -10), 7%); + +@state-warning-text: #8a6d3b; +@state-warning-bg: #fcf8e3; +@state-warning-border: darken(spin(@state-warning-bg, -10), 5%); + +@state-danger-text: #a94442; +@state-danger-bg: #f2dede; +@state-danger-border: darken(spin(@state-danger-bg, -10), 5%); + + +//== Tooltips +// +//## + +//** Tooltip max width +@tooltip-max-width: 200px; +//** Tooltip text color +@tooltip-color: #fff; +//** Tooltip background color +@tooltip-bg: #000; +@tooltip-opacity: .9; + +//** Tooltip arrow width +@tooltip-arrow-width: 5px; +//** Tooltip arrow color +@tooltip-arrow-color: @tooltip-bg; + + +//== Popovers +// +//## + +//** Popover body background color +@popover-bg: @darkBrown; +//** Popover maximum width +@popover-max-width: 276px; +//** Popover border color +@popover-border-color: rgba(0,0,0,.2); +//** Popover fallback border color +@popover-fallback-border-color: #ccc; + +//** Popover title background color +@popover-title-bg: darken(@popover-bg, 3%); + +//** Popover arrow width +@popover-arrow-width: 10px; +//** Popover arrow color +@popover-arrow-color: @popover-bg; + +//** Popover outer arrow width +@popover-arrow-outer-width: (@popover-arrow-width + 1); +//** Popover outer arrow color +@popover-arrow-outer-color: fadein(@popover-border-color, 5%); +//** Popover outer arrow fallback color +@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%); + + +//== Labels +// +//## + +//** Default label background color +@label-default-bg: @gray-light; +//** Primary label background color +@label-primary-bg: @brand-primary; +//** Success label background color +@label-success-bg: @brand-success; +//** Info label background color +@label-info-bg: @brand-info; +//** Warning label background color +@label-warning-bg: @brand-warning; +//** Danger label background color +@label-danger-bg: @brand-danger; + +//** Default label text color +@label-color: #fff; +//** Default text color of a linked label +@label-link-hover-color: #fff; + + +//== Modals +// +//## + +//** Padding applied to the modal body +@modal-inner-padding: 15px; + +//** Padding applied to the modal title +@modal-title-padding: 15px; +//** Modal title line-height +@modal-title-line-height: @line-height-base; + +//** Background color of modal content area +@modal-content-bg: #fff; +//** Modal content border color +@modal-content-border-color: rgba(0,0,0,.2); +//** Modal content border color **for IE8** +@modal-content-fallback-border-color: #999; + +//** Modal backdrop background color +@modal-backdrop-bg: #000; +//** Modal backdrop opacity +@modal-backdrop-opacity: .5; +//** Modal header border color +@modal-header-border-color: #e5e5e5; +//** Modal footer border color +@modal-footer-border-color: @modal-header-border-color; + +@modal-lg: 900px; +@modal-md: 600px; +@modal-sm: 300px; + + +//== Alerts +// +//## Define alert colors, border radius, and padding. + +@alert-padding: 15px; +@alert-border-radius: @border-radius-base; +@alert-link-font-weight: bold; + +@alert-success-bg: @state-success-bg; +@alert-success-text: @state-success-text; +@alert-success-border: @state-success-border; + +@alert-info-bg: @state-info-bg; +@alert-info-text: @state-info-text; +@alert-info-border: @state-info-border; + +@alert-warning-bg: @state-warning-bg; +@alert-warning-text: @state-warning-text; +@alert-warning-border: @state-warning-border; + +@alert-danger-bg: @state-danger-bg; +@alert-danger-text: @state-danger-text; +@alert-danger-border: @state-danger-border; + + +//== Progress bars +// +//## + +//** Background color of the whole progress component +@progress-bg: #f5f5f5; +//** Progress bar text color +@progress-bar-color: #fff; +//** Variable for setting rounded corners on progress bar. +@progress-border-radius: @border-radius-base; + +//** Default progress bar color +@progress-bar-bg: @brand-primary; +//** Success progress bar color +@progress-bar-success-bg: @brand-success; +//** Warning progress bar color +@progress-bar-warning-bg: @brand-warning; +//** Danger progress bar color +@progress-bar-danger-bg: @brand-danger; +//** Info progress bar color +@progress-bar-info-bg: @brand-info; + + +//== List group +// +//## + +//** Background color on `.list-group-item` +@list-group-bg: #fff; +//** `.list-group-item` border color +@list-group-border: #ddd; +//** List group border radius +@list-group-border-radius: @border-radius-base; + +//** Background color of single list items on hover +@list-group-hover-bg: #f5f5f5; +//** Text color of active list items +@list-group-active-color: @component-active-color; +//** Background color of active list items +@list-group-active-bg: @component-active-bg; +//** Border color of active list elements +@list-group-active-border: @list-group-active-bg; +//** Text color for content within active list items +@list-group-active-text-color: lighten(@list-group-active-bg, 40%); + +//** Text color of disabled list items +@list-group-disabled-color: @gray-light; +//** Background color of disabled list items +@list-group-disabled-bg: @gray-lighter; +//** Text color for content within disabled list items +@list-group-disabled-text-color: @list-group-disabled-color; + +@list-group-link-color: #555; +@list-group-link-hover-color: @list-group-link-color; +@list-group-link-heading-color: #333; + + +//== Panels +// +//## + +@panel-bg: #fff; +@panel-body-padding: 15px; +@panel-heading-padding: 10px 15px; +@panel-footer-padding: @panel-heading-padding; +@panel-border-radius: @border-radius-base; + +//** Border color for elements within panels +@panel-inner-border: #ddd; +@panel-footer-bg: #f5f5f5; + +@panel-default-text: @gray-dark; +@panel-default-border: #ddd; +@panel-default-heading-bg: #f5f5f5; + +@panel-primary-text: #fff; +@panel-primary-border: @brand-primary; +@panel-primary-heading-bg: @brand-primary; + +@panel-success-text: @state-success-text; +@panel-success-border: @state-success-border; +@panel-success-heading-bg: @state-success-bg; + +@panel-info-text: @state-info-text; +@panel-info-border: @state-info-border; +@panel-info-heading-bg: @state-info-bg; + +@panel-warning-text: @state-warning-text; +@panel-warning-border: @state-warning-border; +@panel-warning-heading-bg: @state-warning-bg; + +@panel-danger-text: @state-danger-text; +@panel-danger-border: @state-danger-border; +@panel-danger-heading-bg: @state-danger-bg; + + +//== Thumbnails +// +//## + +//** Padding around the thumbnail image +@thumbnail-padding: 4px; +//** Thumbnail background color +@thumbnail-bg: @body-bg; +//** Thumbnail border color +@thumbnail-border: #ddd; +//** Thumbnail border radius +@thumbnail-border-radius: @border-radius-base; + +//** Custom text color for thumbnail captions +@thumbnail-caption-color: @text-color; +//** Padding around the thumbnail caption +@thumbnail-caption-padding: 9px; + + +//== Wells +// +//## + +@well-bg: #f5f5f5; +@well-border: darken(@well-bg, 7%); + + +//== Badges +// +//## + +@badge-color: #fff; +//** Linked badge text color on hover +@badge-link-hover-color: #fff; +@badge-bg: @gray-light; + +//** Badge text color in active nav link +@badge-active-color: @link-color; +//** Badge background color in active nav link +@badge-active-bg: #fff; + +@badge-font-weight: bold; +@badge-line-height: 1; +@badge-border-radius: 10px; + + +//== Breadcrumbs +// +//## + +@breadcrumb-padding-vertical: 8px; +@breadcrumb-padding-horizontal: 15px; +//** Breadcrumb background color +@breadcrumb-bg: #f5f5f5; +//** Breadcrumb text color +@breadcrumb-color: #ccc; +//** Text color of current page in the breadcrumb +@breadcrumb-active-color: @gray-light; +//** Textual separator for between breadcrumb elements +@breadcrumb-separator: "/"; + + +//== Carousel +// +//## + +@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); + +@carousel-control-color: #fff; +@carousel-control-width: 15%; +@carousel-control-opacity: .5; +@carousel-control-font-size: 20px; + +@carousel-indicator-active-bg: #fff; +@carousel-indicator-border-color: #fff; + +@carousel-caption-color: #fff; + + +//== Close +// +//## + +@close-font-weight: bold; +@close-color: #000; +@close-text-shadow: 0 1px 0 #fff; + + +//== Code +// +//## + +@code-color: #c7254e; +@code-bg: #f9f2f4; + +@kbd-color: #fff; +@kbd-bg: #333; + +@pre-bg: #f5f5f5; +@pre-color: @gray-dark; +@pre-border-color: #ccc; +@pre-scrollable-max-height: 340px; + + +//== Type +// +//## + +//** Horizontal offset for forms and lists. +@component-offset-horizontal: 180px; +//** Text muted color +@text-muted: @gray-light; +//** Abbreviations and acronyms border color +@abbr-border-color: @gray-light; +//** Headings small color +@headings-small-color: @gray-light; +//** Blockquote small color +@blockquote-small-color: @gray-light; +//** Blockquote font size +@blockquote-font-size: (@font-size-base * 1.25); +//** Blockquote border color +@blockquote-border-color: @gray-lighter; +//** Page header border color +@page-header-border-color: @gray-lighter; +//** Width of horizontal description list titles +@dl-horizontal-offset: @component-offset-horizontal; +//** Point at which .dl-horizontal becomes horizontal +@dl-horizontal-breakpoint: @grid-float-breakpoint; +//** Horizontal line color. +@hr-border: @gray-lighter; diff --git a/www/css/lemonway.scss b/www/css/lemonway.scss new file mode 100644 index 0000000..9de350c --- /dev/null +++ b/www/css/lemonway.scss @@ -0,0 +1,40 @@ +$primary:#057801; + +body{ + background:url(https://app.cagette.net/img/bg.gif); +} + +.sips_body_page{ + background-color:white;width:auto;margin:auto;text-align:center;font-family:Arial,sans-serif; +} + +br{display:none} +h3{margin:0} +.sips_shop_name_table{display:none;} +.sips_logo{display:none;} +.sips_trans_ref_table{margin:auto;background-color:#fff;padding:10px;border:none;color:#777;font-size:0.8em;} +.sips_trans_ref_table tr + tr +tr{color:$primary;font-size:1.2em;} +.sips_secure_message{color:grey;display:none;text-align:center;width:auto;margin:auto;font-weight:normal;} + +form{ margin:0; padding:5px 2px 0 2px} + +img{margin-top:-15px;} +.sips_card_number_title{color:$primary;font-size:1.2em;} + +.sips_card_number_input{border-radius:5px;border-color:rgb(227,227,227);border-style:solid;border-width:1px;padding:9px 5px 9px 5px;}.sips_expiry_month_card_input{border-radius:5px;border-color:rgb(227,227,227);border-style:solid;border-width:1px;padding:9px 5px 9px 5px;}.sips_expiry_year_card_input{border-radius:5px;border-color:rgb(227,227,227);border-style:solid;border-width:1px;padding:9px 5px 9px 5px;} +.sips_expiry_date_card_title{margin-left:10px;color:#057801;font-size:1.2em;} +.sips_cryptogram_table{border:none;max-width:590px;text-align:center;color:$primary;margin:auto;padding-top:5px;} +.sips_cryptogram_message{border:none;font-size:1em;} +.sips_cvvkey_mandatory_text_input{border-radius:5px;border-color:#d7d7d7;border-style:solid;border-width:1px; padding:9px 5px 9px 5px;min-width:40px;} +.sips_card_cvv2_message{color:$primary;} +.sips_threeD_info_message{color:white;display:none;text-align:center;width:50%;margin:auto;font-size:0.8em;} +.sips_cryptogram_example{display:none;} +.sips_submit_message{color:$primary;font-size:1.2em;display:none;} +.sips_submit_button{background-color:$primary;color:white;font-size:1em;border-radius:5px;border:none;padding:7px 30px 7px 30px;margin: 10px;-webkit-appearance: none;} +.sips_submit_button:active{background-color: white;color: $primary;} +hr{ border: none;background:white;margin: 0;display:none;} +.sips_customer_return_button{font-size:0.6em;margin:0 0 20px 0;background-color:#de4343;color: white;font-size:0.8em;border-radius:5px;border:none;padding:7px 10px 7px 10px;-webkit-appearance: none;} +.sips_customer_return_button:active{ background-color:white;color:#de4343;} +.sips_error_code{color:#de4343;} +.sips_error{color:#de4343;} +.sips_footer_message{display:none;} \ No newline at end of file diff --git a/www/css/print.scss b/www/css/print.scss new file mode 100644 index 0000000..21b8c25 --- /dev/null +++ b/www/css/print.scss @@ -0,0 +1,57 @@ +body +{ + font-family:'Arial'; + font-size:12px; + + table{ + border-collapse: collapse; + + &.table td{ + + border:solid 1px #CCC; + + } + + tr td,th{ + margin:1px; + padding:4px; + vertical-align:top; + text-align:left; + } + + td.total{ + font-weight:bold; + border-top: 2px solid #CCC; + padding-bottom: 12px; + } + + tr.name td{ + border-top:1px solid #000; + } + } + + h1, h2{ + margin-left:auto; + margin-right:auto; + margin-bottom:20px; + text-align:center; + } + + @media print { + hr { + page-break-after: always; + border: 0px #FFF; + } + + .hidden{ + display:none; + } + + } + @media screen { + hr{ + border: 4px #DDD; + border-style: dashed; + } + } +} diff --git a/www/css/style.scss b/www/css/style.scss new file mode 100644 index 0000000..8328e01 --- /dev/null +++ b/www/css/style.scss @@ -0,0 +1,1145 @@ + +/** + + Cagette.net SASS stylesheet + Use it with with bootstrap 3 + +**/ + +@import url('https://fonts.googleapis.com/css?family=Cabin:400,400i,700'); +//@import '_shop'; +@import '_map'; + +/*body{ + background-color:#F8F4E5 !important; +}*/ + +h1,h2,h3,h4{ + margin-top: 4px !important; + margin-bottom: 8px !important; + font-style: italic; +} + +/* buttons */ + +/*.btn { + text-transform: uppercase; + font-size: 14px !important; + padding : 7px 25px !important; + font-weight:normal !important; +} + +/*.btn-lg{ + font-size: 16px !important; +} + +.btn-sm { + font-size: 12px !important; + padding : 5px 10px !important; +} + +.btn-xs { + font-size: 11px !important; + padding : 2px 5px !important; +} + +.btn-link { + text-transform: none; +}*/ + +a{ + cursor:pointer; +} + +/* navigation */ +ul.nav li { + text-transform: uppercase; + font-size: 14px; +} + +/* subnav in contractadmin*/ +.nav-pills > li.active > a { + background-color: #efe9d5 !important; + color: #e95216 !important; +} + +/* Footer */ + +#footer { + background: #84BD55; + border-radius: 8px; + padding: 20px; + ul>li>a { + color: #333; + } + + ul { + margin: 3px 0 0; + padding: 0; + text-align: left; + list-style: none; + font-size: 14px; + } + + .cagsocialmedia li a { + color : #fff; + font-size: 20px; + + min-width: 32px; + /*height: 32px;*/ + padding: 2px 0 0; + display : inline-block; + } + + .cagsocialmedia { + vertical-align: top; + } + + .cagsocialmedia li { + display: inline-block; + text-align: center; + + } + + .cagfb { + background-color : #4267B2; + } + + .cagtwitter { + background-color : #1DA1F2; + } + + .cagyoutube { + background-color : #FF0000; + } + +} + +#subfooter { + /*background-color : #313131; + color : #fff;*/ + text-align: center; + font-size : 14px; + padding : 15px 0 25px 0; + span { + color :#979797; + font-size : 11px; + } +} + +.sqlLog{ + margin-top:24px; + font-size:10px; + background:#AAA; + a{ color:#666; } +} + +.contact{ + border:2px solid #CCC ; + border-radius:8px; + margin:8px; + padding:8px; + width:250px; + min-height: 100px; +} + +.homeBlock{ + margin-bottom:32px; + background:#FFF; + border-radius:8px; + position: relative; + padding:8px; + .content{ + margin: 8px 8px 8px 90px; + + } +} + +//pour les hauts de nav +span.panel-title{ + color: #966125 !important; + font-size: 20px !important; + font-weight:normal !important; +} + +.article{ + margin-bottom:16px; + background:#FFF; + border-radius:8px; + position: relative; + padding:8px; +} + + +.white{ + margin-bottom:16px; + background:#FFF; + border-radius:4px; + position: relative; +} + +.disabled{ + opacity: 0.5; +} + +/* block marron */ +.block{ + margin-bottom: 12px; + background : #FFF; + border-radius : 8px; + position : relative; + padding : 8px; +} + +/* block marron + contours */ +.block2{ + margin-bottom : 32px; + background : #F0D277; + border-radius : 8px; + position : relative; + padding : 8px; + border: 2px solid #C3A04C; +} + +/* product in shop */ +table.product{ + td{ + padding:8px; + vertical-align:top; + } + + width:100%; + height:135px; + overflow:auto; + + .desc{ + font-size:90%; + color:#666; + } + + a.productName{ + line-height: 20px; + display: block; + font-weight:bold; + } +} + + +/* GLOBAL TABLE SKINNING */ + +//separator between rows +table.table tr.head>td{ + //border-top: 1px solid #BBB; + border-top: 3px solid #DDD; + height: 30px; + font-size:120%; + vertical-align: bottom; +} + +//sub-row, darker bg +table.table tr.subrow{ + background-color:#EEE; +} + +//listing have no overflow +table.listing tr td{ + white-space: nowrap; + overflow-x:hidden; +} + +table.table tr.disabled td,table.table tr.inactive td{ + opacity:0.4; +} + + +/* DIV TABLE */ +// used in UserOrder react comp +div.tableHeader{ + font-weight: bold; + color: #AAA; + border-bottom: 1px solid #DDD; + margin-bottom: 12px; +} + + + +/*table.table>tbody>tr>th{ + background-color:white; + color:#BBB; + border-top:none; +}*/ + +div.reportHeader{ + background-color:#DDD; + +} + + + +/* a product order in a table style */ +.productOrder{ + + border-bottom:1px solid #DDD; + margin-bottom:6px; + + + + .infos{ + color:#AAA; + font-size:0.8em; + } + + + +} +/* A product reference */ +.ref{ + color:#AAA; + font-family:monospace; + font-size:0.9em; +} + +/* Link to share */ +.linkToShare{ + font-family:Monospace; + padding: 12px; + border: 1px solid #DDD; + border-radius: 6px; + background-color: #f5f5f5; +} + +/* make like clickable divs */ +.clickable{ + display:block; + border:1px solid #CCC; + border-radius:8px; + color:inherit; + background-color:white; + margin-bottom:12px; + padding:8px; + + &:hover{ + cursor: pointer; + background-color:lighten(#F0D277,10%); + border-color:darken(#F0D277,10%); + text-decoration:none; + transition: all 0.15s ease; + } + + &:active{ + cursor: pointer; + background-color:#F0D277; + border-color:darken(#F0D277,10%); + text-decoration:none; + transform:scale(0.95); + } + + &.disabled{ + background-color: lightgray; + cursor: not-allowed; + } + +} + +/* a cagette group block (use it with .clickable ) */ +.groupBlock{ + height:110px; + overflow:hidden; + vertical-align:top; + position: relative; + .address{ + font-size: 0.9em; + } + .distance { + font-weight: bold; + color: #84bd55; + font-size: 1.2em; + position: absolute; + right: 8px; + bottom: 6px; + } +} + +/* a cagette pro block (use it with .clickable ) */ +.proBlock{ + height:110px; + overflow:hidden; + vertical-align:top; + position: relative; + background:#bad096; + &:hover{ + background:darken(#bad096,10%); + } + /*span.cpro{ + position: absolute; + right:0px; + top:0px; + background:darken(#bad096,30%); + color:yellow; + height: 12px; + padding:4px; + font-weight: bold; + }*/ +} + +.header{ + .accountBlock{ + a{ + color: #777; + } + + } +} + + +/* + SHOP v1 +*/ +.shop{ + + .header{ + background-color:#749E3C; + display:inline-block; + width:100%; + + border-top-left-radius:8px; + border-top-right-radius:8px; + padding:12px; + + font-size: 16px; + line-height: 22px; + + color:white; + a{ color:white; } + .info{ font-weight: bold; } + } + + + .body{ + background:white; + display:inline-block; + width:100%; + padding-top: 12px; + + .catHeader{ + font-weight:bold; + font-size:1.2em; + color:#583816; + border-bottom:1px solid #CCC; + width:100%; + } + } + + + .product{ + + display: inline-block; + + .name{ + padding-left:8px; + } + + .warning{ + font-size:11px; + color:#900; + font-weight:bold; + } + + } + +} + + +.price{ + font-weight:bold; + font-size:1.3em; +} + +.vat{ + color:#999999; + //font-size:80%; +} + +.detail{ + color:#999999; + font-size:90%; + +} + +//in shop page + +#cartContainer.scrolled{ + + position:fixed; + top:125px; +} + +.cartDiv{ + margin-bottom:16px; + background:#007700; + border-radius:8px; + position: relative; + padding:8px; + color:white; + h2{ + color:white !important; + } + a.infos{ + margin-top:8px; + font-size:12px; + color:white !important; + display:block; + } + + #cart{ + + margin-bottom:12px; + + + div.order{ + margin-bottom:8px; + + a{ + margin-right:8px; + } + } + div.total{ + font-size:120%; + //font-weight:bold; + margin-bottom:12px; + margin-top:12px; + border-top:1px solid #FFF; + } + + } +} + + +.place{ + color:#666; + font-weight:bold; + margin-top:13px; + padding:8px; + font-size:13px; + border-top:1px solid #CCC ; +} + +.distribMessage{ + color:#FFF; + font-weight:bold; + padding:4px; + margin-top:13px; + + font-size:13px; + background-color:#080; + /*border-top:1px solid #333 ;*/ +} + + +.dateBoxOffset{ + position: absolute; + top: -16px; + left: -18px; + display: block; +} +.dateBox{ + + border-radius:8px; + background-color:#c59654; + color:#FFF; + font-size:13px; + line-height: 13px; + + text-align:center; + vertical-align:middle; + + width: 90px; + margin:0; + padding:4px; + box-shadow: 4px 4px 0px #292929; + + .box{ + line-height:22px; + background-color:#FFFFFF; + color:#666; + font-size:14px; + font-weight:bold; + text-align:center; + margin: 0px; + border-radius:4px; + } + + /* place link */ + a{ + color: #FFFFFF !important; + } + a:hover{ + color: #FFFFFF !important; + } +} + + + +/* categories */ +span.tag{ + color:white; + font-size:80%; + //font-weight:bold; + padding:4px; + background-color:#666666; + border-radius:4px; + line-height: 30px; +} +span.tag.active{ + padding:12px; + font-size: 16px; + line-height: 45px; +} + + +/** + Popovers for help and tutorials +**/ +.popover{ + + color:white; + font-size:14px; + //font-family:'Arial','sans-serif' !important; + + h3.popover-title{ + padding: 4px; + color:#BB9931 !important; + border-bottom:1px solid #BB9931 !important; + background-color:none; + } + + .popover-content{ + font-size: 16px; + b{ color:#FC0; font-weight:normal; } + } + + + .footer{ + + border-top: 1px solid #BB9931; + height: 45px; + padding: 12px 0; + + .pull-left .btn{ + + background-color:#583816; + color:white; + border:1px solid white; + } + + .pull-right .btn{ + + background-color:#FC0; + color:#583816; + border:1px solid #583816; + } + } +} + +/* skin a modal like a tuto popover */ +.modal.help{ + + .modal-header{ + font-size: 1.3em; + color:#BB9931 !important; + border-bottom:1px solid #BB9931 !important; + font-weight:bold !important; + //font-family:'Arial','sans-serif' !important; + background-color:#583816 !important; + } + + .modal-content{ + background-color:#583816 !important; + color:white; + + } + + .modal-footer{ + border-top:1px solid #BB9931 !important; + } + +} +/* hightlight a zone for tutos */ +.highlight{ + border: 2px dashed orange; + border-radius: 16px; +} + + + +/* Distribution box for the homepage */ +.distrib{ + + margin: 0px 0px 28px 0px; + position:relative; + + .header{ + + display: block; + height: 72px; + overflow:hidden; + border-top-left-radius:8px; + border-top-right-radius:8px; + color:#FFF; + background-color:#749E3C; + vertical-align:middle; + margin:0; + padding: 6px 6px 6px 90px; + + &.disabled{ + background-color:#999; + opacity: 1; + } + &.orders{ + background-color:#B2823F; + } + + .info{ + font-size:16px; + line-height: 22px; + /* place link */ + a{ + color: #FFFFFF !important; + &:hover{ color: #FFFFFF !important; } + } + + } + + .date{ + line-height:23px; + background-color:#FFFFFF; + color:#666; + font-size:14px; + text-align:center; + margin: 0 8px 0 0; + border-radius:6px; + border: 4px solid #C6C6C6; + } + + .address, .closing{ + font-weight:normal; + font-size:0.9em; + line-height: 16px; + } + } + + .myorder{ + font-weight:bold; + color:#999; + background:white; + padding: 11px 0 4px 16px; + } + + .content{ + padding: 6px 6px 6px 6px; + background-color:#85B14D; + border-bottom-left-radius:8px; + border-bottom-right-radius:8px; + + &.disabled{ + background:#AAA; + opacity: 1; + } + + &.orders{ + background:#FFF; + padding-left: 50px; + } + + .footer{ + color:#999; + a{color:#999;} + font-size:0.9em; + + } + } + + + +} + +div.groupImg{ + background:no-repeat center; + background-size: cover; + width: 110px; + height: 110px; +} + +// product thumbnail for shop and home page +div.productImg{ + display:inline-block; + border-radius: 3px; + width:70px; + height:70px; + margin:4px; + background:no-repeat center; + background-size: cover; + //margin:0; +} + +//Product React component +div.product{ + div.productImg{ + margin-right: 12px; + vertical-align: middle; + } + +} + +//Product in ProductSelect React component +div.productSelect{ + /*div.selector{ + width:64px; + height: 64px; + display: inline-block; + cursor: pointer; + + &.active{ + background-color: #007700; + } + + &:hover{ + background-color: #00AA00; + } + }*/ + + div.product{ + display: inline-block; + } + + + +} + +// Cagette Pro Catalogs +div.productBox{ + + div.productImg{ + background-size: cover; + height: 220px; + width:220px; + background-position: center center; + border-radius:6px; + } + + div.productName{ + display:block; + font-weight:bold; + color:#967820; + text-align:center; + font-size:1.2em; + margin-bottom:4px; + } + + .price{ + font-weight:bold; + text-align:right; + } +} + + +// typeahead ( autocomplete js lib ) +.tt-menu{ + background-color:#f5e3ac; +} + +.tt-suggestion{ + background-color:#f5e3ac; + padding:8px; + border-top:1px solid #CCC; + color:darken(#f5e3ac,60%); + cursor:pointer; + font-style:italic; + &:hover{ + background-color:darken(#f5e3ac,10%); + } +} + +//txp product input +#pInput{ + + .txpProduct{ + color:#AAA; + font-weight:bold; + font-size:0.8em; + margin:4px 0; + } + + img{ + max-width:100px; + max-height:100px; + } +} + + +//Product composer +/* +div.ProductComp{ + margin:8px 0; + padding:8px 0; + border-bottom:solid 1px #CCC; +} + +.ComposerApp{ + .add-button{ + vertical-align:top !important; + } +} +*/ + +//basket +div.basketNumber{ + + display:inline-block; + background-color:#333; + border-radius: 10px; + padding: 6px; + + font-weight:bold; + color:#CCC; + +} + +tr.success td div.basketNumber{ + background-color:#070; + color:white; +} + +tr.danger td div.basketNumber{ + background-color:#d9534f; + color:white; +} + +/* fixed bar on bottom for buttons */ +.fixedBar{ + position: fixed; + bottom: 0; + left: 0; + background: white; + width: 100%; + margin: 0; + padding: 8px; + border-top: 3px solid #CCC; + z-index: 99; +} + + + + +/*! + * https://github.com/YouCanBookMe/react-datetime + */ +.rdt { + position: relative; +} +.rdtPicker { + display: none; + position: absolute; + width: 250px; + padding: 4px; + margin-top: 1px; + z-index: 99999 !important; + background: #fff; + box-shadow: 0 1px 3px rgba(0,0,0,.1); + border: 1px solid #f9f9f9; +} +.rdtOpen .rdtPicker { + display: block; +} +.rdtStatic .rdtPicker { + box-shadow: none; + position: static; +} + +.rdtPicker .rdtTimeToggle { + text-align: center; +} + +.rdtPicker table { + width: 100%; + margin: 0; +} +.rdtPicker td, +.rdtPicker th { + text-align: center; + height: 28px; +} +.rdtPicker td { + cursor: pointer; +} +.rdtPicker td.rdtDay:hover, +.rdtPicker td.rdtHour:hover, +.rdtPicker td.rdtMinute:hover, +.rdtPicker td.rdtSecond:hover, +.rdtPicker .rdtTimeToggle:hover { + background: #eeeeee; + cursor: pointer; +} +.rdtPicker td.rdtOld, +.rdtPicker td.rdtNew { + color: #999999; +} +.rdtPicker td.rdtToday { + position: relative; +} +.rdtPicker td.rdtToday:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-bottom: 7px solid #428bca; + border-top-color: rgba(0, 0, 0, 0.2); + position: absolute; + bottom: 4px; + right: 4px; +} +.rdtPicker td.rdtActive, +.rdtPicker td.rdtActive:hover { + background-color: #428bca; + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.rdtPicker td.rdtActive.rdtToday:before { + border-bottom-color: #fff; +} +.rdtPicker td.rdtDisabled, +.rdtPicker td.rdtDisabled:hover { + background: none; + color: #999999; + cursor: not-allowed; +} + +.rdtPicker td span.rdtOld { + color: #999999; +} +.rdtPicker td span.rdtDisabled, +.rdtPicker td span.rdtDisabled:hover { + background: none; + color: #999999; + cursor: not-allowed; +} +.rdtPicker th { + border-bottom: 1px solid #f9f9f9; +} +.rdtPicker .dow { + width: 14.2857%; + border-bottom: none; +} +.rdtPicker th.rdtSwitch { + width: 100px; +} +.rdtPicker th.rdtNext, +.rdtPicker th.rdtPrev { + font-size: 21px; + vertical-align: top; +} + +.rdtPrev span, +.rdtNext span { + display: block; + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -khtml-user-select: none; /* Konqueror */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; +} + +.rdtPicker th.rdtDisabled, +.rdtPicker th.rdtDisabled:hover { + background: none; + color: #999999; + cursor: not-allowed; +} +.rdtPicker thead tr:first-child th { + cursor: pointer; +} +.rdtPicker thead tr:first-child th:hover { + background: #eeeeee; +} + +.rdtPicker tfoot { + border-top: 1px solid #f9f9f9; +} + +.rdtPicker button { + border: none; + background: none; + cursor: pointer; +} +.rdtPicker button:hover { + background-color: #eee; +} + +.rdtPicker thead button { + width: 100%; + height: 100%; +} + +td.rdtMonth, +td.rdtYear { + height: 50px; + width: 25%; + cursor: pointer; +} +td.rdtMonth:hover, +td.rdtYear:hover { + background: #eee; +} + +.rdtCounters { + display: inline-block; +} + +.rdtCounters > div { + float: left; +} + +.rdtCounter { + height: 100px; +} + +.rdtCounter { + width: 40px; +} + +.rdtCounterSeparator { + line-height: 100px; +} + +.rdtCounter .rdtBtn { + height: 40%; + line-height: 40px; + cursor: pointer; + display: block; + + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -khtml-user-select: none; /* Konqueror */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; +} +.rdtCounter .rdtBtn:hover { + background: #eee; +} +.rdtCounter .rdtCount { + height: 20%; + font-size: 1.2em; +} + +.rdtMilli { + vertical-align: middle; + padding-left: 8px; + width: 48px; +} + +.rdtMilli input { + width: 100%; + font-size: 1.2em; + margin-top: 37px; +} \ No newline at end of file diff --git a/www/favicon.ico b/www/favicon.ico new file mode 100644 index 0000000..c025942 Binary files /dev/null and b/www/favicon.ico differ diff --git a/www/fonts/glyphicons-halflings-regular.eot b/www/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000..b93a495 Binary files /dev/null and b/www/fonts/glyphicons-halflings-regular.eot differ diff --git a/www/fonts/glyphicons-halflings-regular.svg b/www/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..94fb549 --- /dev/null +++ b/www/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/fonts/glyphicons-halflings-regular.ttf b/www/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000..1413fc6 Binary files /dev/null and b/www/fonts/glyphicons-halflings-regular.ttf differ diff --git a/www/fonts/glyphicons-halflings-regular.woff b/www/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000..9e61285 Binary files /dev/null and b/www/fonts/glyphicons-halflings-regular.woff differ diff --git a/www/fonts/glyphicons-halflings-regular.woff2 b/www/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 0000000..64539b5 Binary files /dev/null and b/www/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/www/img/AB.png b/www/img/AB.png new file mode 100644 index 0000000..73c4173 Binary files /dev/null and b/www/img/AB.png differ diff --git a/www/img/AMAPéi-micro.png b/www/img/AMAPéi-micro.png new file mode 100644 index 0000000..0cf715a Binary files /dev/null and b/www/img/AMAPéi-micro.png differ diff --git a/www/img/alilo-trans-black.png b/www/img/alilo-trans-black.png new file mode 100644 index 0000000..7990406 Binary files /dev/null and b/www/img/alilo-trans-black.png differ diff --git a/www/img/alilo-trans-white.png b/www/img/alilo-trans-white.png new file mode 100644 index 0000000..a4bacbe Binary files /dev/null and b/www/img/alilo-trans-white.png differ diff --git a/www/img/facebook-16.png b/www/img/facebook-16.png new file mode 100755 index 0000000..96bf8c4 Binary files /dev/null and b/www/img/facebook-16.png differ diff --git a/www/img/facebook-32.png b/www/img/facebook-32.png new file mode 100755 index 0000000..be55ded Binary files /dev/null and b/www/img/facebook-32.png differ diff --git a/www/img/go.png b/www/img/go.png new file mode 100755 index 0000000..c69a5b0 Binary files /dev/null and b/www/img/go.png differ diff --git a/www/img/home.svg b/www/img/home.svg new file mode 100644 index 0000000..38f1b20 --- /dev/null +++ b/www/img/home.svg @@ -0,0 +1,37 @@ + + + + +]> + + + + + + + + + + + + + + diff --git a/www/img/home2.svg b/www/img/home2.svg new file mode 100644 index 0000000..6add336 --- /dev/null +++ b/www/img/home2.svg @@ -0,0 +1,37 @@ + + + + +]> + + + + + + + + + + + + + + diff --git a/www/img/icons/Thumbs.db b/www/img/icons/Thumbs.db new file mode 100755 index 0000000..4ec50ba Binary files /dev/null and b/www/img/icons/Thumbs.db differ diff --git a/www/img/icons/accept.png b/www/img/icons/accept.png new file mode 100755 index 0000000..89c8129 Binary files /dev/null and b/www/img/icons/accept.png differ diff --git a/www/img/icons/add.png b/www/img/icons/add.png new file mode 100755 index 0000000..6332fef Binary files /dev/null and b/www/img/icons/add.png differ diff --git a/www/img/icons/anchor.png b/www/img/icons/anchor.png new file mode 100755 index 0000000..9b3422c Binary files /dev/null and b/www/img/icons/anchor.png differ diff --git a/www/img/icons/application.png b/www/img/icons/application.png new file mode 100755 index 0000000..1dee9e3 Binary files /dev/null and b/www/img/icons/application.png differ diff --git a/www/img/icons/application_add.png b/www/img/icons/application_add.png new file mode 100755 index 0000000..2e94507 Binary files /dev/null and b/www/img/icons/application_add.png differ diff --git a/www/img/icons/application_cascade.png b/www/img/icons/application_cascade.png new file mode 100755 index 0000000..da5c622 Binary files /dev/null and b/www/img/icons/application_cascade.png differ diff --git a/www/img/icons/application_delete.png b/www/img/icons/application_delete.png new file mode 100755 index 0000000..0a335ac Binary files /dev/null and b/www/img/icons/application_delete.png differ diff --git a/www/img/icons/application_double.png b/www/img/icons/application_double.png new file mode 100755 index 0000000..647592f Binary files /dev/null and b/www/img/icons/application_double.png differ diff --git a/www/img/icons/application_edit.png b/www/img/icons/application_edit.png new file mode 100755 index 0000000..fb2efb8 Binary files /dev/null and b/www/img/icons/application_edit.png differ diff --git a/www/img/icons/application_error.png b/www/img/icons/application_error.png new file mode 100755 index 0000000..b35fa57 Binary files /dev/null and b/www/img/icons/application_error.png differ diff --git a/www/img/icons/application_form.png b/www/img/icons/application_form.png new file mode 100755 index 0000000..807b862 Binary files /dev/null and b/www/img/icons/application_form.png differ diff --git a/www/img/icons/application_form_add.png b/www/img/icons/application_form_add.png new file mode 100755 index 0000000..28c2175 Binary files /dev/null and b/www/img/icons/application_form_add.png differ diff --git a/www/img/icons/application_form_delete.png b/www/img/icons/application_form_delete.png new file mode 100755 index 0000000..cd305ec Binary files /dev/null and b/www/img/icons/application_form_delete.png differ diff --git a/www/img/icons/application_form_edit.png b/www/img/icons/application_form_edit.png new file mode 100755 index 0000000..af486c9 Binary files /dev/null and b/www/img/icons/application_form_edit.png differ diff --git a/www/img/icons/application_form_magnify.png b/www/img/icons/application_form_magnify.png new file mode 100755 index 0000000..7b7fbd1 Binary files /dev/null and b/www/img/icons/application_form_magnify.png differ diff --git a/www/img/icons/application_get.png b/www/img/icons/application_get.png new file mode 100755 index 0000000..28e41ea Binary files /dev/null and b/www/img/icons/application_get.png differ diff --git a/www/img/icons/application_go.png b/www/img/icons/application_go.png new file mode 100755 index 0000000..5cc2b0d Binary files /dev/null and b/www/img/icons/application_go.png differ diff --git a/www/img/icons/application_home.png b/www/img/icons/application_home.png new file mode 100755 index 0000000..b60d0c8 Binary files /dev/null and b/www/img/icons/application_home.png differ diff --git a/www/img/icons/application_key.png b/www/img/icons/application_key.png new file mode 100755 index 0000000..998d65c Binary files /dev/null and b/www/img/icons/application_key.png differ diff --git a/www/img/icons/application_lightning.png b/www/img/icons/application_lightning.png new file mode 100755 index 0000000..7e91545 Binary files /dev/null and b/www/img/icons/application_lightning.png differ diff --git a/www/img/icons/application_link.png b/www/img/icons/application_link.png new file mode 100755 index 0000000..f8fbb3e Binary files /dev/null and b/www/img/icons/application_link.png differ diff --git a/www/img/icons/application_osx.png b/www/img/icons/application_osx.png new file mode 100755 index 0000000..9f022ec Binary files /dev/null and b/www/img/icons/application_osx.png differ diff --git a/www/img/icons/application_osx_terminal.png b/www/img/icons/application_osx_terminal.png new file mode 100755 index 0000000..b3d8ce0 Binary files /dev/null and b/www/img/icons/application_osx_terminal.png differ diff --git a/www/img/icons/application_put.png b/www/img/icons/application_put.png new file mode 100755 index 0000000..c30cf59 Binary files /dev/null and b/www/img/icons/application_put.png differ diff --git a/www/img/icons/application_side_boxes.png b/www/img/icons/application_side_boxes.png new file mode 100755 index 0000000..efbf3c4 Binary files /dev/null and b/www/img/icons/application_side_boxes.png differ diff --git a/www/img/icons/application_side_contract.png b/www/img/icons/application_side_contract.png new file mode 100755 index 0000000..3585f94 Binary files /dev/null and b/www/img/icons/application_side_contract.png differ diff --git a/www/img/icons/application_side_expand.png b/www/img/icons/application_side_expand.png new file mode 100755 index 0000000..030cf7c Binary files /dev/null and b/www/img/icons/application_side_expand.png differ diff --git a/www/img/icons/application_side_list.png b/www/img/icons/application_side_list.png new file mode 100755 index 0000000..248eaf1 Binary files /dev/null and b/www/img/icons/application_side_list.png differ diff --git a/www/img/icons/application_side_tree.png b/www/img/icons/application_side_tree.png new file mode 100755 index 0000000..f04a52b Binary files /dev/null and b/www/img/icons/application_side_tree.png differ diff --git a/www/img/icons/application_split.png b/www/img/icons/application_split.png new file mode 100755 index 0000000..a91c78a Binary files /dev/null and b/www/img/icons/application_split.png differ diff --git a/www/img/icons/application_tile_horizontal.png b/www/img/icons/application_tile_horizontal.png new file mode 100755 index 0000000..8a1191c Binary files /dev/null and b/www/img/icons/application_tile_horizontal.png differ diff --git a/www/img/icons/application_tile_vertical.png b/www/img/icons/application_tile_vertical.png new file mode 100755 index 0000000..1d40383 Binary files /dev/null and b/www/img/icons/application_tile_vertical.png differ diff --git a/www/img/icons/application_view_columns.png b/www/img/icons/application_view_columns.png new file mode 100755 index 0000000..dc2e9d5 Binary files /dev/null and b/www/img/icons/application_view_columns.png differ diff --git a/www/img/icons/application_view_detail.png b/www/img/icons/application_view_detail.png new file mode 100755 index 0000000..aba044b Binary files /dev/null and b/www/img/icons/application_view_detail.png differ diff --git a/www/img/icons/application_view_gallery.png b/www/img/icons/application_view_gallery.png new file mode 100755 index 0000000..851950d Binary files /dev/null and b/www/img/icons/application_view_gallery.png differ diff --git a/www/img/icons/application_view_icons.png b/www/img/icons/application_view_icons.png new file mode 100755 index 0000000..6a93cda Binary files /dev/null and b/www/img/icons/application_view_icons.png differ diff --git a/www/img/icons/application_view_list.png b/www/img/icons/application_view_list.png new file mode 100755 index 0000000..acc30b8 Binary files /dev/null and b/www/img/icons/application_view_list.png differ diff --git a/www/img/icons/application_view_tile.png b/www/img/icons/application_view_tile.png new file mode 100755 index 0000000..3bc0bd3 Binary files /dev/null and b/www/img/icons/application_view_tile.png differ diff --git a/www/img/icons/application_xp.png b/www/img/icons/application_xp.png new file mode 100755 index 0000000..d22860a Binary files /dev/null and b/www/img/icons/application_xp.png differ diff --git a/www/img/icons/application_xp_terminal.png b/www/img/icons/application_xp_terminal.png new file mode 100755 index 0000000..c28dd63 Binary files /dev/null and b/www/img/icons/application_xp_terminal.png differ diff --git a/www/img/icons/arrow_branch.png b/www/img/icons/arrow_branch.png new file mode 100755 index 0000000..7542db1 Binary files /dev/null and b/www/img/icons/arrow_branch.png differ diff --git a/www/img/icons/arrow_divide.png b/www/img/icons/arrow_divide.png new file mode 100755 index 0000000..61a7b1d Binary files /dev/null and b/www/img/icons/arrow_divide.png differ diff --git a/www/img/icons/arrow_down.png b/www/img/icons/arrow_down.png new file mode 100755 index 0000000..2c4e279 Binary files /dev/null and b/www/img/icons/arrow_down.png differ diff --git a/www/img/icons/arrow_in.png b/www/img/icons/arrow_in.png new file mode 100755 index 0000000..745c651 Binary files /dev/null and b/www/img/icons/arrow_in.png differ diff --git a/www/img/icons/arrow_inout.png b/www/img/icons/arrow_inout.png new file mode 100755 index 0000000..1b76367 Binary files /dev/null and b/www/img/icons/arrow_inout.png differ diff --git a/www/img/icons/arrow_join.png b/www/img/icons/arrow_join.png new file mode 100755 index 0000000..a128413 Binary files /dev/null and b/www/img/icons/arrow_join.png differ diff --git a/www/img/icons/arrow_left.png b/www/img/icons/arrow_left.png new file mode 100755 index 0000000..5dc6967 Binary files /dev/null and b/www/img/icons/arrow_left.png differ diff --git a/www/img/icons/arrow_merge.png b/www/img/icons/arrow_merge.png new file mode 100755 index 0000000..7502dbb Binary files /dev/null and b/www/img/icons/arrow_merge.png differ diff --git a/www/img/icons/arrow_out.png b/www/img/icons/arrow_out.png new file mode 100755 index 0000000..2e9bc42 Binary files /dev/null and b/www/img/icons/arrow_out.png differ diff --git a/www/img/icons/arrow_redo.png b/www/img/icons/arrow_redo.png new file mode 100755 index 0000000..fdc394c Binary files /dev/null and b/www/img/icons/arrow_redo.png differ diff --git a/www/img/icons/arrow_refresh.png b/www/img/icons/arrow_refresh.png new file mode 100755 index 0000000..0de2656 Binary files /dev/null and b/www/img/icons/arrow_refresh.png differ diff --git a/www/img/icons/arrow_refresh_small.png b/www/img/icons/arrow_refresh_small.png new file mode 100755 index 0000000..d3087df Binary files /dev/null and b/www/img/icons/arrow_refresh_small.png differ diff --git a/www/img/icons/arrow_right.png b/www/img/icons/arrow_right.png new file mode 100755 index 0000000..b1a1819 Binary files /dev/null and b/www/img/icons/arrow_right.png differ diff --git a/www/img/icons/arrow_rotate_anticlockwise.png b/www/img/icons/arrow_rotate_anticlockwise.png new file mode 100755 index 0000000..46c75aa Binary files /dev/null and b/www/img/icons/arrow_rotate_anticlockwise.png differ diff --git a/www/img/icons/arrow_rotate_clockwise.png b/www/img/icons/arrow_rotate_clockwise.png new file mode 100755 index 0000000..aa65210 Binary files /dev/null and b/www/img/icons/arrow_rotate_clockwise.png differ diff --git a/www/img/icons/arrow_switch.png b/www/img/icons/arrow_switch.png new file mode 100755 index 0000000..258c16c Binary files /dev/null and b/www/img/icons/arrow_switch.png differ diff --git a/www/img/icons/arrow_turn_left.png b/www/img/icons/arrow_turn_left.png new file mode 100755 index 0000000..a3d6c9e Binary files /dev/null and b/www/img/icons/arrow_turn_left.png differ diff --git a/www/img/icons/arrow_turn_right.png b/www/img/icons/arrow_turn_right.png new file mode 100755 index 0000000..629f20d Binary files /dev/null and b/www/img/icons/arrow_turn_right.png differ diff --git a/www/img/icons/arrow_undo.png b/www/img/icons/arrow_undo.png new file mode 100755 index 0000000..6972c5e Binary files /dev/null and b/www/img/icons/arrow_undo.png differ diff --git a/www/img/icons/arrow_up.png b/www/img/icons/arrow_up.png new file mode 100755 index 0000000..1ebb193 Binary files /dev/null and b/www/img/icons/arrow_up.png differ diff --git a/www/img/icons/asterisk_orange.png b/www/img/icons/asterisk_orange.png new file mode 100755 index 0000000..1ebebde Binary files /dev/null and b/www/img/icons/asterisk_orange.png differ diff --git a/www/img/icons/asterisk_yellow.png b/www/img/icons/asterisk_yellow.png new file mode 100755 index 0000000..bab7cc9 Binary files /dev/null and b/www/img/icons/asterisk_yellow.png differ diff --git a/www/img/icons/attach.png b/www/img/icons/attach.png new file mode 100755 index 0000000..ea897cc Binary files /dev/null and b/www/img/icons/attach.png differ diff --git a/www/img/icons/award_star_add.png b/www/img/icons/award_star_add.png new file mode 100755 index 0000000..9c4be9b Binary files /dev/null and b/www/img/icons/award_star_add.png differ diff --git a/www/img/icons/award_star_bronze_1.png b/www/img/icons/award_star_bronze_1.png new file mode 100755 index 0000000..658c711 Binary files /dev/null and b/www/img/icons/award_star_bronze_1.png differ diff --git a/www/img/icons/award_star_bronze_2.png b/www/img/icons/award_star_bronze_2.png new file mode 100755 index 0000000..e47babd Binary files /dev/null and b/www/img/icons/award_star_bronze_2.png differ diff --git a/www/img/icons/award_star_bronze_3.png b/www/img/icons/award_star_bronze_3.png new file mode 100755 index 0000000..396e4b3 Binary files /dev/null and b/www/img/icons/award_star_bronze_3.png differ diff --git a/www/img/icons/award_star_delete.png b/www/img/icons/award_star_delete.png new file mode 100755 index 0000000..4721b15 Binary files /dev/null and b/www/img/icons/award_star_delete.png differ diff --git a/www/img/icons/award_star_gold_1.png b/www/img/icons/award_star_gold_1.png new file mode 100755 index 0000000..97a22b7 Binary files /dev/null and b/www/img/icons/award_star_gold_1.png differ diff --git a/www/img/icons/award_star_gold_2.png b/www/img/icons/award_star_gold_2.png new file mode 100755 index 0000000..0eaa571 Binary files /dev/null and b/www/img/icons/award_star_gold_2.png differ diff --git a/www/img/icons/award_star_gold_3.png b/www/img/icons/award_star_gold_3.png new file mode 100755 index 0000000..124c991 Binary files /dev/null and b/www/img/icons/award_star_gold_3.png differ diff --git a/www/img/icons/award_star_silver_1.png b/www/img/icons/award_star_silver_1.png new file mode 100755 index 0000000..028a546 Binary files /dev/null and b/www/img/icons/award_star_silver_1.png differ diff --git a/www/img/icons/award_star_silver_2.png b/www/img/icons/award_star_silver_2.png new file mode 100755 index 0000000..e487c3a Binary files /dev/null and b/www/img/icons/award_star_silver_2.png differ diff --git a/www/img/icons/award_star_silver_3.png b/www/img/icons/award_star_silver_3.png new file mode 100755 index 0000000..1d72d47 Binary files /dev/null and b/www/img/icons/award_star_silver_3.png differ diff --git a/www/img/icons/basket.png b/www/img/icons/basket.png new file mode 100755 index 0000000..b0686d7 Binary files /dev/null and b/www/img/icons/basket.png differ diff --git a/www/img/icons/basket_add.png b/www/img/icons/basket_add.png new file mode 100755 index 0000000..3554368 Binary files /dev/null and b/www/img/icons/basket_add.png differ diff --git a/www/img/icons/basket_delete.png b/www/img/icons/basket_delete.png new file mode 100755 index 0000000..1349974 Binary files /dev/null and b/www/img/icons/basket_delete.png differ diff --git a/www/img/icons/basket_edit.png b/www/img/icons/basket_edit.png new file mode 100755 index 0000000..8138bbd Binary files /dev/null and b/www/img/icons/basket_edit.png differ diff --git a/www/img/icons/basket_error.png b/www/img/icons/basket_error.png new file mode 100755 index 0000000..3978b29 Binary files /dev/null and b/www/img/icons/basket_error.png differ diff --git a/www/img/icons/basket_go.png b/www/img/icons/basket_go.png new file mode 100755 index 0000000..ed8b9a5 Binary files /dev/null and b/www/img/icons/basket_go.png differ diff --git a/www/img/icons/basket_put.png b/www/img/icons/basket_put.png new file mode 100755 index 0000000..be62faa Binary files /dev/null and b/www/img/icons/basket_put.png differ diff --git a/www/img/icons/basket_remove.png b/www/img/icons/basket_remove.png new file mode 100755 index 0000000..04dd7fd Binary files /dev/null and b/www/img/icons/basket_remove.png differ diff --git a/www/img/icons/bell.png b/www/img/icons/bell.png new file mode 100755 index 0000000..6e0015d Binary files /dev/null and b/www/img/icons/bell.png differ diff --git a/www/img/icons/bell_add.png b/www/img/icons/bell_add.png new file mode 100755 index 0000000..7db01d6 Binary files /dev/null and b/www/img/icons/bell_add.png differ diff --git a/www/img/icons/bell_delete.png b/www/img/icons/bell_delete.png new file mode 100755 index 0000000..23907bb Binary files /dev/null and b/www/img/icons/bell_delete.png differ diff --git a/www/img/icons/bell_error.png b/www/img/icons/bell_error.png new file mode 100755 index 0000000..a0ddc00 Binary files /dev/null and b/www/img/icons/bell_error.png differ diff --git a/www/img/icons/bell_go.png b/www/img/icons/bell_go.png new file mode 100755 index 0000000..b89bb34 Binary files /dev/null and b/www/img/icons/bell_go.png differ diff --git a/www/img/icons/bell_link.png b/www/img/icons/bell_link.png new file mode 100755 index 0000000..b8c64af Binary files /dev/null and b/www/img/icons/bell_link.png differ diff --git a/www/img/icons/bin.png b/www/img/icons/bin.png new file mode 100755 index 0000000..ebad933 Binary files /dev/null and b/www/img/icons/bin.png differ diff --git a/www/img/icons/bin_closed.png b/www/img/icons/bin_closed.png new file mode 100755 index 0000000..afe22ba Binary files /dev/null and b/www/img/icons/bin_closed.png differ diff --git a/www/img/icons/bin_empty.png b/www/img/icons/bin_empty.png new file mode 100755 index 0000000..375b8bf Binary files /dev/null and b/www/img/icons/bin_empty.png differ diff --git a/www/img/icons/bomb.png b/www/img/icons/bomb.png new file mode 100755 index 0000000..1be3797 Binary files /dev/null and b/www/img/icons/bomb.png differ diff --git a/www/img/icons/book.png b/www/img/icons/book.png new file mode 100755 index 0000000..b0f4dd7 Binary files /dev/null and b/www/img/icons/book.png differ diff --git a/www/img/icons/book_add.png b/www/img/icons/book_add.png new file mode 100755 index 0000000..e2f0847 Binary files /dev/null and b/www/img/icons/book_add.png differ diff --git a/www/img/icons/book_addresses.png b/www/img/icons/book_addresses.png new file mode 100755 index 0000000..b73419b Binary files /dev/null and b/www/img/icons/book_addresses.png differ diff --git a/www/img/icons/book_delete.png b/www/img/icons/book_delete.png new file mode 100755 index 0000000..d9a6340 Binary files /dev/null and b/www/img/icons/book_delete.png differ diff --git a/www/img/icons/book_edit.png b/www/img/icons/book_edit.png new file mode 100755 index 0000000..6e756cc Binary files /dev/null and b/www/img/icons/book_edit.png differ diff --git a/www/img/icons/book_error.png b/www/img/icons/book_error.png new file mode 100755 index 0000000..f3fbed0 Binary files /dev/null and b/www/img/icons/book_error.png differ diff --git a/www/img/icons/book_go.png b/www/img/icons/book_go.png new file mode 100755 index 0000000..cd4e196 Binary files /dev/null and b/www/img/icons/book_go.png differ diff --git a/www/img/icons/book_key.png b/www/img/icons/book_key.png new file mode 100755 index 0000000..d8e23ec Binary files /dev/null and b/www/img/icons/book_key.png differ diff --git a/www/img/icons/book_link.png b/www/img/icons/book_link.png new file mode 100755 index 0000000..dd0820e Binary files /dev/null and b/www/img/icons/book_link.png differ diff --git a/www/img/icons/book_next.png b/www/img/icons/book_next.png new file mode 100755 index 0000000..ff2ea1a Binary files /dev/null and b/www/img/icons/book_next.png differ diff --git a/www/img/icons/book_open.png b/www/img/icons/book_open.png new file mode 100755 index 0000000..7d863f9 Binary files /dev/null and b/www/img/icons/book_open.png differ diff --git a/www/img/icons/book_previous.png b/www/img/icons/book_previous.png new file mode 100755 index 0000000..2e53c69 Binary files /dev/null and b/www/img/icons/book_previous.png differ diff --git a/www/img/icons/box.png b/www/img/icons/box.png new file mode 100755 index 0000000..8443c23 Binary files /dev/null and b/www/img/icons/box.png differ diff --git a/www/img/icons/brick.png b/www/img/icons/brick.png new file mode 100755 index 0000000..7851cf3 Binary files /dev/null and b/www/img/icons/brick.png differ diff --git a/www/img/icons/brick_add.png b/www/img/icons/brick_add.png new file mode 100755 index 0000000..fac186b Binary files /dev/null and b/www/img/icons/brick_add.png differ diff --git a/www/img/icons/brick_delete.png b/www/img/icons/brick_delete.png new file mode 100755 index 0000000..3a8c373 Binary files /dev/null and b/www/img/icons/brick_delete.png differ diff --git a/www/img/icons/brick_edit.png b/www/img/icons/brick_edit.png new file mode 100755 index 0000000..eb06df3 Binary files /dev/null and b/www/img/icons/brick_edit.png differ diff --git a/www/img/icons/brick_error.png b/www/img/icons/brick_error.png new file mode 100755 index 0000000..18ab01e Binary files /dev/null and b/www/img/icons/brick_error.png differ diff --git a/www/img/icons/brick_go.png b/www/img/icons/brick_go.png new file mode 100755 index 0000000..fe0d335 Binary files /dev/null and b/www/img/icons/brick_go.png differ diff --git a/www/img/icons/brick_link.png b/www/img/icons/brick_link.png new file mode 100755 index 0000000..9ebf013 Binary files /dev/null and b/www/img/icons/brick_link.png differ diff --git a/www/img/icons/bricks.png b/www/img/icons/bricks.png new file mode 100755 index 0000000..0905f93 Binary files /dev/null and b/www/img/icons/bricks.png differ diff --git a/www/img/icons/briefcase.png b/www/img/icons/briefcase.png new file mode 100755 index 0000000..05c5649 Binary files /dev/null and b/www/img/icons/briefcase.png differ diff --git a/www/img/icons/bug.png b/www/img/icons/bug.png new file mode 100755 index 0000000..2d5fb90 Binary files /dev/null and b/www/img/icons/bug.png differ diff --git a/www/img/icons/bug_add.png b/www/img/icons/bug_add.png new file mode 100755 index 0000000..ced7817 Binary files /dev/null and b/www/img/icons/bug_add.png differ diff --git a/www/img/icons/bug_delete.png b/www/img/icons/bug_delete.png new file mode 100755 index 0000000..e81d757 Binary files /dev/null and b/www/img/icons/bug_delete.png differ diff --git a/www/img/icons/bug_edit.png b/www/img/icons/bug_edit.png new file mode 100755 index 0000000..e5c7dc0 Binary files /dev/null and b/www/img/icons/bug_edit.png differ diff --git a/www/img/icons/bug_error.png b/www/img/icons/bug_error.png new file mode 100755 index 0000000..c4e8c28 Binary files /dev/null and b/www/img/icons/bug_error.png differ diff --git a/www/img/icons/bug_go.png b/www/img/icons/bug_go.png new file mode 100755 index 0000000..4e4ae99 Binary files /dev/null and b/www/img/icons/bug_go.png differ diff --git a/www/img/icons/bug_link.png b/www/img/icons/bug_link.png new file mode 100755 index 0000000..30e25ab Binary files /dev/null and b/www/img/icons/bug_link.png differ diff --git a/www/img/icons/building.png b/www/img/icons/building.png new file mode 100755 index 0000000..11a017c Binary files /dev/null and b/www/img/icons/building.png differ diff --git a/www/img/icons/building_add.png b/www/img/icons/building_add.png new file mode 100755 index 0000000..d88e2b9 Binary files /dev/null and b/www/img/icons/building_add.png differ diff --git a/www/img/icons/building_delete.png b/www/img/icons/building_delete.png new file mode 100755 index 0000000..db6455d Binary files /dev/null and b/www/img/icons/building_delete.png differ diff --git a/www/img/icons/building_edit.png b/www/img/icons/building_edit.png new file mode 100755 index 0000000..646db36 Binary files /dev/null and b/www/img/icons/building_edit.png differ diff --git a/www/img/icons/building_error.png b/www/img/icons/building_error.png new file mode 100755 index 0000000..a342eef Binary files /dev/null and b/www/img/icons/building_error.png differ diff --git a/www/img/icons/building_go.png b/www/img/icons/building_go.png new file mode 100755 index 0000000..cdcbcb3 Binary files /dev/null and b/www/img/icons/building_go.png differ diff --git a/www/img/icons/building_key.png b/www/img/icons/building_key.png new file mode 100755 index 0000000..8b79e30 Binary files /dev/null and b/www/img/icons/building_key.png differ diff --git a/www/img/icons/building_link.png b/www/img/icons/building_link.png new file mode 100755 index 0000000..a340629 Binary files /dev/null and b/www/img/icons/building_link.png differ diff --git a/www/img/icons/bullet_add.png b/www/img/icons/bullet_add.png new file mode 100755 index 0000000..41ff833 Binary files /dev/null and b/www/img/icons/bullet_add.png differ diff --git a/www/img/icons/bullet_arrow_bottom.png b/www/img/icons/bullet_arrow_bottom.png new file mode 100755 index 0000000..1a28d82 Binary files /dev/null and b/www/img/icons/bullet_arrow_bottom.png differ diff --git a/www/img/icons/bullet_arrow_down.png b/www/img/icons/bullet_arrow_down.png new file mode 100755 index 0000000..9b23c06 Binary files /dev/null and b/www/img/icons/bullet_arrow_down.png differ diff --git a/www/img/icons/bullet_arrow_top.png b/www/img/icons/bullet_arrow_top.png new file mode 100755 index 0000000..0ce86d2 Binary files /dev/null and b/www/img/icons/bullet_arrow_top.png differ diff --git a/www/img/icons/bullet_arrow_up.png b/www/img/icons/bullet_arrow_up.png new file mode 100755 index 0000000..24df0f4 Binary files /dev/null and b/www/img/icons/bullet_arrow_up.png differ diff --git a/www/img/icons/bullet_black.png b/www/img/icons/bullet_black.png new file mode 100755 index 0000000..5761970 Binary files /dev/null and b/www/img/icons/bullet_black.png differ diff --git a/www/img/icons/bullet_blue.png b/www/img/icons/bullet_blue.png new file mode 100755 index 0000000..a7651ec Binary files /dev/null and b/www/img/icons/bullet_blue.png differ diff --git a/www/img/icons/bullet_delete.png b/www/img/icons/bullet_delete.png new file mode 100755 index 0000000..bd6271b Binary files /dev/null and b/www/img/icons/bullet_delete.png differ diff --git a/www/img/icons/bullet_disk.png b/www/img/icons/bullet_disk.png new file mode 100755 index 0000000..209c6a7 Binary files /dev/null and b/www/img/icons/bullet_disk.png differ diff --git a/www/img/icons/bullet_error.png b/www/img/icons/bullet_error.png new file mode 100755 index 0000000..bca2b49 Binary files /dev/null and b/www/img/icons/bullet_error.png differ diff --git a/www/img/icons/bullet_feed.png b/www/img/icons/bullet_feed.png new file mode 100755 index 0000000..1a0e0f1 Binary files /dev/null and b/www/img/icons/bullet_feed.png differ diff --git a/www/img/icons/bullet_go.png b/www/img/icons/bullet_go.png new file mode 100755 index 0000000..bc4faa7 Binary files /dev/null and b/www/img/icons/bullet_go.png differ diff --git a/www/img/icons/bullet_green.png b/www/img/icons/bullet_green.png new file mode 100755 index 0000000..058ad26 Binary files /dev/null and b/www/img/icons/bullet_green.png differ diff --git a/www/img/icons/bullet_key.png b/www/img/icons/bullet_key.png new file mode 100755 index 0000000..3d37f2e Binary files /dev/null and b/www/img/icons/bullet_key.png differ diff --git a/www/img/icons/bullet_orange.png b/www/img/icons/bullet_orange.png new file mode 100755 index 0000000..fa63024 Binary files /dev/null and b/www/img/icons/bullet_orange.png differ diff --git a/www/img/icons/bullet_picture.png b/www/img/icons/bullet_picture.png new file mode 100755 index 0000000..386cb30 Binary files /dev/null and b/www/img/icons/bullet_picture.png differ diff --git a/www/img/icons/bullet_pink.png b/www/img/icons/bullet_pink.png new file mode 100755 index 0000000..0c9f73e Binary files /dev/null and b/www/img/icons/bullet_pink.png differ diff --git a/www/img/icons/bullet_purple.png b/www/img/icons/bullet_purple.png new file mode 100755 index 0000000..52ba503 Binary files /dev/null and b/www/img/icons/bullet_purple.png differ diff --git a/www/img/icons/bullet_red.png b/www/img/icons/bullet_red.png new file mode 100755 index 0000000..0cd8031 Binary files /dev/null and b/www/img/icons/bullet_red.png differ diff --git a/www/img/icons/bullet_star.png b/www/img/icons/bullet_star.png new file mode 100755 index 0000000..fab774a Binary files /dev/null and b/www/img/icons/bullet_star.png differ diff --git a/www/img/icons/bullet_toggle_minus.png b/www/img/icons/bullet_toggle_minus.png new file mode 100755 index 0000000..b47ce55 Binary files /dev/null and b/www/img/icons/bullet_toggle_minus.png differ diff --git a/www/img/icons/bullet_toggle_plus.png b/www/img/icons/bullet_toggle_plus.png new file mode 100755 index 0000000..9ab4a89 Binary files /dev/null and b/www/img/icons/bullet_toggle_plus.png differ diff --git a/www/img/icons/bullet_white.png b/www/img/icons/bullet_white.png new file mode 100755 index 0000000..a9af8d4 Binary files /dev/null and b/www/img/icons/bullet_white.png differ diff --git a/www/img/icons/bullet_wrench.png b/www/img/icons/bullet_wrench.png new file mode 100755 index 0000000..67817e6 Binary files /dev/null and b/www/img/icons/bullet_wrench.png differ diff --git a/www/img/icons/bullet_yellow.png b/www/img/icons/bullet_yellow.png new file mode 100755 index 0000000..6469cea Binary files /dev/null and b/www/img/icons/bullet_yellow.png differ diff --git a/www/img/icons/cake.png b/www/img/icons/cake.png new file mode 100755 index 0000000..4ef151a Binary files /dev/null and b/www/img/icons/cake.png differ diff --git a/www/img/icons/calculator.png b/www/img/icons/calculator.png new file mode 100755 index 0000000..701a60a Binary files /dev/null and b/www/img/icons/calculator.png differ diff --git a/www/img/icons/calculator_add.png b/www/img/icons/calculator_add.png new file mode 100755 index 0000000..fd377bd Binary files /dev/null and b/www/img/icons/calculator_add.png differ diff --git a/www/img/icons/calculator_delete.png b/www/img/icons/calculator_delete.png new file mode 100755 index 0000000..ac96170 Binary files /dev/null and b/www/img/icons/calculator_delete.png differ diff --git a/www/img/icons/calculator_edit.png b/www/img/icons/calculator_edit.png new file mode 100755 index 0000000..63b06b9 Binary files /dev/null and b/www/img/icons/calculator_edit.png differ diff --git a/www/img/icons/calculator_error.png b/www/img/icons/calculator_error.png new file mode 100755 index 0000000..0bc4288 Binary files /dev/null and b/www/img/icons/calculator_error.png differ diff --git a/www/img/icons/calculator_link.png b/www/img/icons/calculator_link.png new file mode 100755 index 0000000..a2a8fe6 Binary files /dev/null and b/www/img/icons/calculator_link.png differ diff --git a/www/img/icons/calendar.png b/www/img/icons/calendar.png new file mode 100755 index 0000000..6589138 Binary files /dev/null and b/www/img/icons/calendar.png differ diff --git a/www/img/icons/calendar_add.png b/www/img/icons/calendar_add.png new file mode 100755 index 0000000..17679db Binary files /dev/null and b/www/img/icons/calendar_add.png differ diff --git a/www/img/icons/calendar_delete.png b/www/img/icons/calendar_delete.png new file mode 100755 index 0000000..69a3b10 Binary files /dev/null and b/www/img/icons/calendar_delete.png differ diff --git a/www/img/icons/calendar_edit.png b/www/img/icons/calendar_edit.png new file mode 100755 index 0000000..d1d2d6e Binary files /dev/null and b/www/img/icons/calendar_edit.png differ diff --git a/www/img/icons/calendar_link.png b/www/img/icons/calendar_link.png new file mode 100755 index 0000000..6b106b9 Binary files /dev/null and b/www/img/icons/calendar_link.png differ diff --git a/www/img/icons/calendar_view_day.png b/www/img/icons/calendar_view_day.png new file mode 100755 index 0000000..9740f76 Binary files /dev/null and b/www/img/icons/calendar_view_day.png differ diff --git a/www/img/icons/calendar_view_month.png b/www/img/icons/calendar_view_month.png new file mode 100755 index 0000000..6cff76c Binary files /dev/null and b/www/img/icons/calendar_view_month.png differ diff --git a/www/img/icons/calendar_view_week.png b/www/img/icons/calendar_view_week.png new file mode 100755 index 0000000..8fe695f Binary files /dev/null and b/www/img/icons/calendar_view_week.png differ diff --git a/www/img/icons/camera.png b/www/img/icons/camera.png new file mode 100755 index 0000000..8536d1a Binary files /dev/null and b/www/img/icons/camera.png differ diff --git a/www/img/icons/camera_add.png b/www/img/icons/camera_add.png new file mode 100755 index 0000000..08b5da9 Binary files /dev/null and b/www/img/icons/camera_add.png differ diff --git a/www/img/icons/camera_delete.png b/www/img/icons/camera_delete.png new file mode 100755 index 0000000..3846d74 Binary files /dev/null and b/www/img/icons/camera_delete.png differ diff --git a/www/img/icons/camera_edit.png b/www/img/icons/camera_edit.png new file mode 100755 index 0000000..b5015b1 Binary files /dev/null and b/www/img/icons/camera_edit.png differ diff --git a/www/img/icons/camera_error.png b/www/img/icons/camera_error.png new file mode 100755 index 0000000..3c1bc95 Binary files /dev/null and b/www/img/icons/camera_error.png differ diff --git a/www/img/icons/camera_go.png b/www/img/icons/camera_go.png new file mode 100755 index 0000000..94ce2b2 Binary files /dev/null and b/www/img/icons/camera_go.png differ diff --git a/www/img/icons/camera_link.png b/www/img/icons/camera_link.png new file mode 100755 index 0000000..d2ac9f9 Binary files /dev/null and b/www/img/icons/camera_link.png differ diff --git a/www/img/icons/camera_small.png b/www/img/icons/camera_small.png new file mode 100755 index 0000000..454b0b0 Binary files /dev/null and b/www/img/icons/camera_small.png differ diff --git a/www/img/icons/cancel.png b/www/img/icons/cancel.png new file mode 100755 index 0000000..c149c2b Binary files /dev/null and b/www/img/icons/cancel.png differ diff --git a/www/img/icons/car.png b/www/img/icons/car.png new file mode 100755 index 0000000..4f3a770 Binary files /dev/null and b/www/img/icons/car.png differ diff --git a/www/img/icons/car_add.png b/www/img/icons/car_add.png new file mode 100755 index 0000000..1215a51 Binary files /dev/null and b/www/img/icons/car_add.png differ diff --git a/www/img/icons/car_delete.png b/www/img/icons/car_delete.png new file mode 100755 index 0000000..2803b56 Binary files /dev/null and b/www/img/icons/car_delete.png differ diff --git a/www/img/icons/cart.png b/www/img/icons/cart.png new file mode 100755 index 0000000..1baf7b9 Binary files /dev/null and b/www/img/icons/cart.png differ diff --git a/www/img/icons/cart_add.png b/www/img/icons/cart_add.png new file mode 100755 index 0000000..45c2900 Binary files /dev/null and b/www/img/icons/cart_add.png differ diff --git a/www/img/icons/cart_delete.png b/www/img/icons/cart_delete.png new file mode 100755 index 0000000..ac5bce5 Binary files /dev/null and b/www/img/icons/cart_delete.png differ diff --git a/www/img/icons/cart_edit.png b/www/img/icons/cart_edit.png new file mode 100755 index 0000000..b94ff88 Binary files /dev/null and b/www/img/icons/cart_edit.png differ diff --git a/www/img/icons/cart_error.png b/www/img/icons/cart_error.png new file mode 100755 index 0000000..144c835 Binary files /dev/null and b/www/img/icons/cart_error.png differ diff --git a/www/img/icons/cart_go.png b/www/img/icons/cart_go.png new file mode 100755 index 0000000..20ee058 Binary files /dev/null and b/www/img/icons/cart_go.png differ diff --git a/www/img/icons/cart_put.png b/www/img/icons/cart_put.png new file mode 100755 index 0000000..3aec353 Binary files /dev/null and b/www/img/icons/cart_put.png differ diff --git a/www/img/icons/cart_remove.png b/www/img/icons/cart_remove.png new file mode 100755 index 0000000..360217b Binary files /dev/null and b/www/img/icons/cart_remove.png differ diff --git a/www/img/icons/cd.png b/www/img/icons/cd.png new file mode 100755 index 0000000..ef43223 Binary files /dev/null and b/www/img/icons/cd.png differ diff --git a/www/img/icons/cd_add.png b/www/img/icons/cd_add.png new file mode 100755 index 0000000..b0254ef Binary files /dev/null and b/www/img/icons/cd_add.png differ diff --git a/www/img/icons/cd_burn.png b/www/img/icons/cd_burn.png new file mode 100755 index 0000000..157cb0b Binary files /dev/null and b/www/img/icons/cd_burn.png differ diff --git a/www/img/icons/cd_delete.png b/www/img/icons/cd_delete.png new file mode 100755 index 0000000..7d7b3d5 Binary files /dev/null and b/www/img/icons/cd_delete.png differ diff --git a/www/img/icons/cd_edit.png b/www/img/icons/cd_edit.png new file mode 100755 index 0000000..b0dc194 Binary files /dev/null and b/www/img/icons/cd_edit.png differ diff --git a/www/img/icons/cd_eject.png b/www/img/icons/cd_eject.png new file mode 100755 index 0000000..762932f Binary files /dev/null and b/www/img/icons/cd_eject.png differ diff --git a/www/img/icons/cd_go.png b/www/img/icons/cd_go.png new file mode 100755 index 0000000..13e0499 Binary files /dev/null and b/www/img/icons/cd_go.png differ diff --git a/www/img/icons/chart_bar.png b/www/img/icons/chart_bar.png new file mode 100755 index 0000000..9051fbc Binary files /dev/null and b/www/img/icons/chart_bar.png differ diff --git a/www/img/icons/chart_bar_add.png b/www/img/icons/chart_bar_add.png new file mode 100755 index 0000000..d283e84 Binary files /dev/null and b/www/img/icons/chart_bar_add.png differ diff --git a/www/img/icons/chart_bar_delete.png b/www/img/icons/chart_bar_delete.png new file mode 100755 index 0000000..259f686 Binary files /dev/null and b/www/img/icons/chart_bar_delete.png differ diff --git a/www/img/icons/chart_bar_edit.png b/www/img/icons/chart_bar_edit.png new file mode 100755 index 0000000..df64d97 Binary files /dev/null and b/www/img/icons/chart_bar_edit.png differ diff --git a/www/img/icons/chart_bar_error.png b/www/img/icons/chart_bar_error.png new file mode 100755 index 0000000..bdacea5 Binary files /dev/null and b/www/img/icons/chart_bar_error.png differ diff --git a/www/img/icons/chart_bar_link.png b/www/img/icons/chart_bar_link.png new file mode 100755 index 0000000..bf18aed Binary files /dev/null and b/www/img/icons/chart_bar_link.png differ diff --git a/www/img/icons/chart_curve.png b/www/img/icons/chart_curve.png new file mode 100755 index 0000000..01e933a Binary files /dev/null and b/www/img/icons/chart_curve.png differ diff --git a/www/img/icons/chart_curve_add.png b/www/img/icons/chart_curve_add.png new file mode 100755 index 0000000..f9e2050 Binary files /dev/null and b/www/img/icons/chart_curve_add.png differ diff --git a/www/img/icons/chart_curve_delete.png b/www/img/icons/chart_curve_delete.png new file mode 100755 index 0000000..b411391 Binary files /dev/null and b/www/img/icons/chart_curve_delete.png differ diff --git a/www/img/icons/chart_curve_edit.png b/www/img/icons/chart_curve_edit.png new file mode 100755 index 0000000..bd07673 Binary files /dev/null and b/www/img/icons/chart_curve_edit.png differ diff --git a/www/img/icons/chart_curve_error.png b/www/img/icons/chart_curve_error.png new file mode 100755 index 0000000..906dd03 Binary files /dev/null and b/www/img/icons/chart_curve_error.png differ diff --git a/www/img/icons/chart_curve_go.png b/www/img/icons/chart_curve_go.png new file mode 100755 index 0000000..ac9eda5 Binary files /dev/null and b/www/img/icons/chart_curve_go.png differ diff --git a/www/img/icons/chart_curve_link.png b/www/img/icons/chart_curve_link.png new file mode 100755 index 0000000..144eafe Binary files /dev/null and b/www/img/icons/chart_curve_link.png differ diff --git a/www/img/icons/chart_line.png b/www/img/icons/chart_line.png new file mode 100755 index 0000000..85020f3 Binary files /dev/null and b/www/img/icons/chart_line.png differ diff --git a/www/img/icons/chart_line_add.png b/www/img/icons/chart_line_add.png new file mode 100755 index 0000000..5571a5e Binary files /dev/null and b/www/img/icons/chart_line_add.png differ diff --git a/www/img/icons/chart_line_delete.png b/www/img/icons/chart_line_delete.png new file mode 100755 index 0000000..5b0aa90 Binary files /dev/null and b/www/img/icons/chart_line_delete.png differ diff --git a/www/img/icons/chart_line_edit.png b/www/img/icons/chart_line_edit.png new file mode 100755 index 0000000..9cf6607 Binary files /dev/null and b/www/img/icons/chart_line_edit.png differ diff --git a/www/img/icons/chart_line_error.png b/www/img/icons/chart_line_error.png new file mode 100755 index 0000000..ff23c03 Binary files /dev/null and b/www/img/icons/chart_line_error.png differ diff --git a/www/img/icons/chart_line_link.png b/www/img/icons/chart_line_link.png new file mode 100755 index 0000000..f3727d2 Binary files /dev/null and b/www/img/icons/chart_line_link.png differ diff --git a/www/img/icons/chart_organisation.png b/www/img/icons/chart_organisation.png new file mode 100755 index 0000000..c32d25c Binary files /dev/null and b/www/img/icons/chart_organisation.png differ diff --git a/www/img/icons/chart_organisation_add.png b/www/img/icons/chart_organisation_add.png new file mode 100755 index 0000000..f0dba4a Binary files /dev/null and b/www/img/icons/chart_organisation_add.png differ diff --git a/www/img/icons/chart_organisation_delete.png b/www/img/icons/chart_organisation_delete.png new file mode 100755 index 0000000..7dc8dca Binary files /dev/null and b/www/img/icons/chart_organisation_delete.png differ diff --git a/www/img/icons/chart_pie.png b/www/img/icons/chart_pie.png new file mode 100755 index 0000000..fe00fa0 Binary files /dev/null and b/www/img/icons/chart_pie.png differ diff --git a/www/img/icons/chart_pie_add.png b/www/img/icons/chart_pie_add.png new file mode 100755 index 0000000..bf0822e Binary files /dev/null and b/www/img/icons/chart_pie_add.png differ diff --git a/www/img/icons/chart_pie_delete.png b/www/img/icons/chart_pie_delete.png new file mode 100755 index 0000000..5ab9efd Binary files /dev/null and b/www/img/icons/chart_pie_delete.png differ diff --git a/www/img/icons/chart_pie_edit.png b/www/img/icons/chart_pie_edit.png new file mode 100755 index 0000000..3debc12 Binary files /dev/null and b/www/img/icons/chart_pie_edit.png differ diff --git a/www/img/icons/chart_pie_error.png b/www/img/icons/chart_pie_error.png new file mode 100755 index 0000000..7344174 Binary files /dev/null and b/www/img/icons/chart_pie_error.png differ diff --git a/www/img/icons/chart_pie_link.png b/www/img/icons/chart_pie_link.png new file mode 100755 index 0000000..c072f8e Binary files /dev/null and b/www/img/icons/chart_pie_link.png differ diff --git a/www/img/icons/clock.png b/www/img/icons/clock.png new file mode 100755 index 0000000..e2672c2 Binary files /dev/null and b/www/img/icons/clock.png differ diff --git a/www/img/icons/clock_add.png b/www/img/icons/clock_add.png new file mode 100755 index 0000000..598b839 Binary files /dev/null and b/www/img/icons/clock_add.png differ diff --git a/www/img/icons/clock_delete.png b/www/img/icons/clock_delete.png new file mode 100755 index 0000000..8bf9efe Binary files /dev/null and b/www/img/icons/clock_delete.png differ diff --git a/www/img/icons/clock_edit.png b/www/img/icons/clock_edit.png new file mode 100755 index 0000000..7d35718 Binary files /dev/null and b/www/img/icons/clock_edit.png differ diff --git a/www/img/icons/clock_error.png b/www/img/icons/clock_error.png new file mode 100755 index 0000000..a7c461b Binary files /dev/null and b/www/img/icons/clock_error.png differ diff --git a/www/img/icons/clock_go.png b/www/img/icons/clock_go.png new file mode 100755 index 0000000..a1a24d3 Binary files /dev/null and b/www/img/icons/clock_go.png differ diff --git a/www/img/icons/clock_link.png b/www/img/icons/clock_link.png new file mode 100755 index 0000000..481cf04 Binary files /dev/null and b/www/img/icons/clock_link.png differ diff --git a/www/img/icons/clock_pause.png b/www/img/icons/clock_pause.png new file mode 100755 index 0000000..ba74725 Binary files /dev/null and b/www/img/icons/clock_pause.png differ diff --git a/www/img/icons/clock_play.png b/www/img/icons/clock_play.png new file mode 100755 index 0000000..fb4ebc8 Binary files /dev/null and b/www/img/icons/clock_play.png differ diff --git a/www/img/icons/clock_red.png b/www/img/icons/clock_red.png new file mode 100755 index 0000000..2842cc3 Binary files /dev/null and b/www/img/icons/clock_red.png differ diff --git a/www/img/icons/clock_stop.png b/www/img/icons/clock_stop.png new file mode 100755 index 0000000..6fe8a6f Binary files /dev/null and b/www/img/icons/clock_stop.png differ diff --git a/www/img/icons/cog.png b/www/img/icons/cog.png new file mode 100755 index 0000000..67de2c6 Binary files /dev/null and b/www/img/icons/cog.png differ diff --git a/www/img/icons/cog_add.png b/www/img/icons/cog_add.png new file mode 100755 index 0000000..04f22ba Binary files /dev/null and b/www/img/icons/cog_add.png differ diff --git a/www/img/icons/cog_delete.png b/www/img/icons/cog_delete.png new file mode 100755 index 0000000..8ce71c4 Binary files /dev/null and b/www/img/icons/cog_delete.png differ diff --git a/www/img/icons/cog_edit.png b/www/img/icons/cog_edit.png new file mode 100755 index 0000000..47b75a4 Binary files /dev/null and b/www/img/icons/cog_edit.png differ diff --git a/www/img/icons/cog_error.png b/www/img/icons/cog_error.png new file mode 100755 index 0000000..4766743 Binary files /dev/null and b/www/img/icons/cog_error.png differ diff --git a/www/img/icons/cog_go.png b/www/img/icons/cog_go.png new file mode 100755 index 0000000..3262767 Binary files /dev/null and b/www/img/icons/cog_go.png differ diff --git a/www/img/icons/coins.png b/www/img/icons/coins.png new file mode 100755 index 0000000..0ca9074 Binary files /dev/null and b/www/img/icons/coins.png differ diff --git a/www/img/icons/coins_add.png b/www/img/icons/coins_add.png new file mode 100755 index 0000000..cdff5d3 Binary files /dev/null and b/www/img/icons/coins_add.png differ diff --git a/www/img/icons/coins_delete.png b/www/img/icons/coins_delete.png new file mode 100755 index 0000000..18e0c0f Binary files /dev/null and b/www/img/icons/coins_delete.png differ diff --git a/www/img/icons/color_swatch.png b/www/img/icons/color_swatch.png new file mode 100755 index 0000000..6e6e852 Binary files /dev/null and b/www/img/icons/color_swatch.png differ diff --git a/www/img/icons/color_wheel.png b/www/img/icons/color_wheel.png new file mode 100755 index 0000000..809fb00 Binary files /dev/null and b/www/img/icons/color_wheel.png differ diff --git a/www/img/icons/comment.png b/www/img/icons/comment.png new file mode 100755 index 0000000..7bc9233 Binary files /dev/null and b/www/img/icons/comment.png differ diff --git a/www/img/icons/comment_add.png b/www/img/icons/comment_add.png new file mode 100755 index 0000000..75e78de Binary files /dev/null and b/www/img/icons/comment_add.png differ diff --git a/www/img/icons/comment_delete.png b/www/img/icons/comment_delete.png new file mode 100755 index 0000000..643fdbe Binary files /dev/null and b/www/img/icons/comment_delete.png differ diff --git a/www/img/icons/comment_edit.png b/www/img/icons/comment_edit.png new file mode 100755 index 0000000..73db110 Binary files /dev/null and b/www/img/icons/comment_edit.png differ diff --git a/www/img/icons/comments.png b/www/img/icons/comments.png new file mode 100755 index 0000000..39433cf Binary files /dev/null and b/www/img/icons/comments.png differ diff --git a/www/img/icons/comments_add.png b/www/img/icons/comments_add.png new file mode 100755 index 0000000..b325634 Binary files /dev/null and b/www/img/icons/comments_add.png differ diff --git a/www/img/icons/comments_delete.png b/www/img/icons/comments_delete.png new file mode 100755 index 0000000..6df7376 Binary files /dev/null and b/www/img/icons/comments_delete.png differ diff --git a/www/img/icons/compress.png b/www/img/icons/compress.png new file mode 100755 index 0000000..8606ff0 Binary files /dev/null and b/www/img/icons/compress.png differ diff --git a/www/img/icons/computer.png b/www/img/icons/computer.png new file mode 100755 index 0000000..9bc37dc Binary files /dev/null and b/www/img/icons/computer.png differ diff --git a/www/img/icons/computer_add.png b/www/img/icons/computer_add.png new file mode 100755 index 0000000..db604ee Binary files /dev/null and b/www/img/icons/computer_add.png differ diff --git a/www/img/icons/computer_delete.png b/www/img/icons/computer_delete.png new file mode 100755 index 0000000..5e9b268 Binary files /dev/null and b/www/img/icons/computer_delete.png differ diff --git a/www/img/icons/computer_edit.png b/www/img/icons/computer_edit.png new file mode 100755 index 0000000..34c72fe Binary files /dev/null and b/www/img/icons/computer_edit.png differ diff --git a/www/img/icons/computer_error.png b/www/img/icons/computer_error.png new file mode 100755 index 0000000..b2c3ed5 Binary files /dev/null and b/www/img/icons/computer_error.png differ diff --git a/www/img/icons/computer_go.png b/www/img/icons/computer_go.png new file mode 100755 index 0000000..0b26144 Binary files /dev/null and b/www/img/icons/computer_go.png differ diff --git a/www/img/icons/computer_key.png b/www/img/icons/computer_key.png new file mode 100755 index 0000000..eca5430 Binary files /dev/null and b/www/img/icons/computer_key.png differ diff --git a/www/img/icons/computer_link.png b/www/img/icons/computer_link.png new file mode 100755 index 0000000..3859db2 Binary files /dev/null and b/www/img/icons/computer_link.png differ diff --git a/www/img/icons/connect.png b/www/img/icons/connect.png new file mode 100755 index 0000000..024138e Binary files /dev/null and b/www/img/icons/connect.png differ diff --git a/www/img/icons/contrast.png b/www/img/icons/contrast.png new file mode 100755 index 0000000..adcc004 Binary files /dev/null and b/www/img/icons/contrast.png differ diff --git a/www/img/icons/contrast_decrease.png b/www/img/icons/contrast_decrease.png new file mode 100755 index 0000000..0155bf5 Binary files /dev/null and b/www/img/icons/contrast_decrease.png differ diff --git a/www/img/icons/contrast_high.png b/www/img/icons/contrast_high.png new file mode 100755 index 0000000..d87c8cb Binary files /dev/null and b/www/img/icons/contrast_high.png differ diff --git a/www/img/icons/contrast_increase.png b/www/img/icons/contrast_increase.png new file mode 100755 index 0000000..a3e7f52 Binary files /dev/null and b/www/img/icons/contrast_increase.png differ diff --git a/www/img/icons/contrast_low.png b/www/img/icons/contrast_low.png new file mode 100755 index 0000000..dc9f4b1 Binary files /dev/null and b/www/img/icons/contrast_low.png differ diff --git a/www/img/icons/control_eject.png b/www/img/icons/control_eject.png new file mode 100755 index 0000000..924d817 Binary files /dev/null and b/www/img/icons/control_eject.png differ diff --git a/www/img/icons/control_eject_blue.png b/www/img/icons/control_eject_blue.png new file mode 100755 index 0000000..2bd4963 Binary files /dev/null and b/www/img/icons/control_eject_blue.png differ diff --git a/www/img/icons/control_end.png b/www/img/icons/control_end.png new file mode 100755 index 0000000..036e04d Binary files /dev/null and b/www/img/icons/control_end.png differ diff --git a/www/img/icons/control_end_blue.png b/www/img/icons/control_end_blue.png new file mode 100755 index 0000000..7207935 Binary files /dev/null and b/www/img/icons/control_end_blue.png differ diff --git a/www/img/icons/control_equalizer.png b/www/img/icons/control_equalizer.png new file mode 100755 index 0000000..4606087 Binary files /dev/null and b/www/img/icons/control_equalizer.png differ diff --git a/www/img/icons/control_equalizer_blue.png b/www/img/icons/control_equalizer_blue.png new file mode 100755 index 0000000..1b2e6a3 Binary files /dev/null and b/www/img/icons/control_equalizer_blue.png differ diff --git a/www/img/icons/control_fastforward.png b/www/img/icons/control_fastforward.png new file mode 100755 index 0000000..31f7fd3 Binary files /dev/null and b/www/img/icons/control_fastforward.png differ diff --git a/www/img/icons/control_fastforward_blue.png b/www/img/icons/control_fastforward_blue.png new file mode 100755 index 0000000..4a2f9d4 Binary files /dev/null and b/www/img/icons/control_fastforward_blue.png differ diff --git a/www/img/icons/control_pause.png b/www/img/icons/control_pause.png new file mode 100755 index 0000000..2d9ce9c Binary files /dev/null and b/www/img/icons/control_pause.png differ diff --git a/www/img/icons/control_pause_blue.png b/www/img/icons/control_pause_blue.png new file mode 100755 index 0000000..ec61099 Binary files /dev/null and b/www/img/icons/control_pause_blue.png differ diff --git a/www/img/icons/control_play.png b/www/img/icons/control_play.png new file mode 100755 index 0000000..0846555 Binary files /dev/null and b/www/img/icons/control_play.png differ diff --git a/www/img/icons/control_play_blue.png b/www/img/icons/control_play_blue.png new file mode 100755 index 0000000..f8c8ec6 Binary files /dev/null and b/www/img/icons/control_play_blue.png differ diff --git a/www/img/icons/control_repeat.png b/www/img/icons/control_repeat.png new file mode 100755 index 0000000..1c4f57a Binary files /dev/null and b/www/img/icons/control_repeat.png differ diff --git a/www/img/icons/control_repeat_blue.png b/www/img/icons/control_repeat_blue.png new file mode 100755 index 0000000..406ec33 Binary files /dev/null and b/www/img/icons/control_repeat_blue.png differ diff --git a/www/img/icons/control_rewind.png b/www/img/icons/control_rewind.png new file mode 100755 index 0000000..c029447 Binary files /dev/null and b/www/img/icons/control_rewind.png differ diff --git a/www/img/icons/control_rewind_blue.png b/www/img/icons/control_rewind_blue.png new file mode 100755 index 0000000..15d1584 Binary files /dev/null and b/www/img/icons/control_rewind_blue.png differ diff --git a/www/img/icons/control_start.png b/www/img/icons/control_start.png new file mode 100755 index 0000000..7dd1c07 Binary files /dev/null and b/www/img/icons/control_start.png differ diff --git a/www/img/icons/control_start_blue.png b/www/img/icons/control_start_blue.png new file mode 100755 index 0000000..6f11fcb Binary files /dev/null and b/www/img/icons/control_start_blue.png differ diff --git a/www/img/icons/control_stop.png b/www/img/icons/control_stop.png new file mode 100755 index 0000000..893bb60 Binary files /dev/null and b/www/img/icons/control_stop.png differ diff --git a/www/img/icons/control_stop_blue.png b/www/img/icons/control_stop_blue.png new file mode 100755 index 0000000..e6f75d2 Binary files /dev/null and b/www/img/icons/control_stop_blue.png differ diff --git a/www/img/icons/controller.png b/www/img/icons/controller.png new file mode 100755 index 0000000..5cf76ed Binary files /dev/null and b/www/img/icons/controller.png differ diff --git a/www/img/icons/controller_add.png b/www/img/icons/controller_add.png new file mode 100755 index 0000000..efecb38 Binary files /dev/null and b/www/img/icons/controller_add.png differ diff --git a/www/img/icons/controller_delete.png b/www/img/icons/controller_delete.png new file mode 100755 index 0000000..3d83bc7 Binary files /dev/null and b/www/img/icons/controller_delete.png differ diff --git a/www/img/icons/controller_error.png b/www/img/icons/controller_error.png new file mode 100755 index 0000000..7f17c0c Binary files /dev/null and b/www/img/icons/controller_error.png differ diff --git a/www/img/icons/creditcards.png b/www/img/icons/creditcards.png new file mode 100755 index 0000000..4eae583 Binary files /dev/null and b/www/img/icons/creditcards.png differ diff --git a/www/img/icons/cross.png b/www/img/icons/cross.png new file mode 100755 index 0000000..1514d51 Binary files /dev/null and b/www/img/icons/cross.png differ diff --git a/www/img/icons/css.png b/www/img/icons/css.png new file mode 100755 index 0000000..23f3101 Binary files /dev/null and b/www/img/icons/css.png differ diff --git a/www/img/icons/css_add.png b/www/img/icons/css_add.png new file mode 100755 index 0000000..e8ea10f Binary files /dev/null and b/www/img/icons/css_add.png differ diff --git a/www/img/icons/css_delete.png b/www/img/icons/css_delete.png new file mode 100755 index 0000000..326aba4 Binary files /dev/null and b/www/img/icons/css_delete.png differ diff --git a/www/img/icons/css_go.png b/www/img/icons/css_go.png new file mode 100755 index 0000000..6cdf38c Binary files /dev/null and b/www/img/icons/css_go.png differ diff --git a/www/img/icons/css_valid.png b/www/img/icons/css_valid.png new file mode 100755 index 0000000..4c72ca5 Binary files /dev/null and b/www/img/icons/css_valid.png differ diff --git a/www/img/icons/cup.png b/www/img/icons/cup.png new file mode 100755 index 0000000..b7bfcd1 Binary files /dev/null and b/www/img/icons/cup.png differ diff --git a/www/img/icons/cup_add.png b/www/img/icons/cup_add.png new file mode 100755 index 0000000..4ecaece Binary files /dev/null and b/www/img/icons/cup_add.png differ diff --git a/www/img/icons/cup_delete.png b/www/img/icons/cup_delete.png new file mode 100755 index 0000000..59a6d9c Binary files /dev/null and b/www/img/icons/cup_delete.png differ diff --git a/www/img/icons/cup_edit.png b/www/img/icons/cup_edit.png new file mode 100755 index 0000000..0b8f1e1 Binary files /dev/null and b/www/img/icons/cup_edit.png differ diff --git a/www/img/icons/cup_error.png b/www/img/icons/cup_error.png new file mode 100755 index 0000000..6879874 Binary files /dev/null and b/www/img/icons/cup_error.png differ diff --git a/www/img/icons/cup_go.png b/www/img/icons/cup_go.png new file mode 100755 index 0000000..9527efb Binary files /dev/null and b/www/img/icons/cup_go.png differ diff --git a/www/img/icons/cup_key.png b/www/img/icons/cup_key.png new file mode 100755 index 0000000..7ae160c Binary files /dev/null and b/www/img/icons/cup_key.png differ diff --git a/www/img/icons/cup_link.png b/www/img/icons/cup_link.png new file mode 100755 index 0000000..41d1ace Binary files /dev/null and b/www/img/icons/cup_link.png differ diff --git a/www/img/icons/cursor.png b/www/img/icons/cursor.png new file mode 100755 index 0000000..532f532 Binary files /dev/null and b/www/img/icons/cursor.png differ diff --git a/www/img/icons/cut.png b/www/img/icons/cut.png new file mode 100755 index 0000000..f215d6f Binary files /dev/null and b/www/img/icons/cut.png differ diff --git a/www/img/icons/cut_red.png b/www/img/icons/cut_red.png new file mode 100755 index 0000000..85bb2f0 Binary files /dev/null and b/www/img/icons/cut_red.png differ diff --git a/www/img/icons/database.png b/www/img/icons/database.png new file mode 100755 index 0000000..3d09261 Binary files /dev/null and b/www/img/icons/database.png differ diff --git a/www/img/icons/database_add.png b/www/img/icons/database_add.png new file mode 100755 index 0000000..802bd6c Binary files /dev/null and b/www/img/icons/database_add.png differ diff --git a/www/img/icons/database_connect.png b/www/img/icons/database_connect.png new file mode 100755 index 0000000..3a11197 Binary files /dev/null and b/www/img/icons/database_connect.png differ diff --git a/www/img/icons/database_delete.png b/www/img/icons/database_delete.png new file mode 100755 index 0000000..cce652e Binary files /dev/null and b/www/img/icons/database_delete.png differ diff --git a/www/img/icons/database_edit.png b/www/img/icons/database_edit.png new file mode 100755 index 0000000..e501b66 Binary files /dev/null and b/www/img/icons/database_edit.png differ diff --git a/www/img/icons/database_error.png b/www/img/icons/database_error.png new file mode 100755 index 0000000..578221a Binary files /dev/null and b/www/img/icons/database_error.png differ diff --git a/www/img/icons/database_gear.png b/www/img/icons/database_gear.png new file mode 100755 index 0000000..7c0ab2b Binary files /dev/null and b/www/img/icons/database_gear.png differ diff --git a/www/img/icons/database_go.png b/www/img/icons/database_go.png new file mode 100755 index 0000000..61a8556 Binary files /dev/null and b/www/img/icons/database_go.png differ diff --git a/www/img/icons/database_key.png b/www/img/icons/database_key.png new file mode 100755 index 0000000..3334147 Binary files /dev/null and b/www/img/icons/database_key.png differ diff --git a/www/img/icons/database_lightning.png b/www/img/icons/database_lightning.png new file mode 100755 index 0000000..d9eefc2 Binary files /dev/null and b/www/img/icons/database_lightning.png differ diff --git a/www/img/icons/database_link.png b/www/img/icons/database_link.png new file mode 100755 index 0000000..4c8204a Binary files /dev/null and b/www/img/icons/database_link.png differ diff --git a/www/img/icons/database_refresh.png b/www/img/icons/database_refresh.png new file mode 100755 index 0000000..ff803be Binary files /dev/null and b/www/img/icons/database_refresh.png differ diff --git a/www/img/icons/database_save.png b/www/img/icons/database_save.png new file mode 100755 index 0000000..44c06dd Binary files /dev/null and b/www/img/icons/database_save.png differ diff --git a/www/img/icons/database_table.png b/www/img/icons/database_table.png new file mode 100755 index 0000000..693709c Binary files /dev/null and b/www/img/icons/database_table.png differ diff --git a/www/img/icons/date.png b/www/img/icons/date.png new file mode 100755 index 0000000..783c833 Binary files /dev/null and b/www/img/icons/date.png differ diff --git a/www/img/icons/date_add.png b/www/img/icons/date_add.png new file mode 100755 index 0000000..6a7ae02 Binary files /dev/null and b/www/img/icons/date_add.png differ diff --git a/www/img/icons/date_delete.png b/www/img/icons/date_delete.png new file mode 100755 index 0000000..969a6b7 Binary files /dev/null and b/www/img/icons/date_delete.png differ diff --git a/www/img/icons/date_edit.png b/www/img/icons/date_edit.png new file mode 100755 index 0000000..e681065 Binary files /dev/null and b/www/img/icons/date_edit.png differ diff --git a/www/img/icons/date_error.png b/www/img/icons/date_error.png new file mode 100755 index 0000000..442cd97 Binary files /dev/null and b/www/img/icons/date_error.png differ diff --git a/www/img/icons/date_go.png b/www/img/icons/date_go.png new file mode 100755 index 0000000..52dd9f3 Binary files /dev/null and b/www/img/icons/date_go.png differ diff --git a/www/img/icons/date_link.png b/www/img/icons/date_link.png new file mode 100755 index 0000000..9f0aada Binary files /dev/null and b/www/img/icons/date_link.png differ diff --git a/www/img/icons/date_magnify.png b/www/img/icons/date_magnify.png new file mode 100755 index 0000000..cd05f19 Binary files /dev/null and b/www/img/icons/date_magnify.png differ diff --git a/www/img/icons/date_next.png b/www/img/icons/date_next.png new file mode 100755 index 0000000..48d740a Binary files /dev/null and b/www/img/icons/date_next.png differ diff --git a/www/img/icons/date_previous.png b/www/img/icons/date_previous.png new file mode 100755 index 0000000..e117a83 Binary files /dev/null and b/www/img/icons/date_previous.png differ diff --git a/www/img/icons/delete.png b/www/img/icons/delete.png new file mode 100755 index 0000000..08f2493 Binary files /dev/null and b/www/img/icons/delete.png differ diff --git a/www/img/icons/disconnect.png b/www/img/icons/disconnect.png new file mode 100755 index 0000000..b335cb1 Binary files /dev/null and b/www/img/icons/disconnect.png differ diff --git a/www/img/icons/disk.png b/www/img/icons/disk.png new file mode 100755 index 0000000..99d532e Binary files /dev/null and b/www/img/icons/disk.png differ diff --git a/www/img/icons/disk_multiple.png b/www/img/icons/disk_multiple.png new file mode 100755 index 0000000..fc5a52f Binary files /dev/null and b/www/img/icons/disk_multiple.png differ diff --git a/www/img/icons/door.png b/www/img/icons/door.png new file mode 100755 index 0000000..369fc46 Binary files /dev/null and b/www/img/icons/door.png differ diff --git a/www/img/icons/door_in.png b/www/img/icons/door_in.png new file mode 100755 index 0000000..41676a0 Binary files /dev/null and b/www/img/icons/door_in.png differ diff --git a/www/img/icons/door_open.png b/www/img/icons/door_open.png new file mode 100755 index 0000000..64bab57 Binary files /dev/null and b/www/img/icons/door_open.png differ diff --git a/www/img/icons/door_out.png b/www/img/icons/door_out.png new file mode 100755 index 0000000..2541d2b Binary files /dev/null and b/www/img/icons/door_out.png differ diff --git a/www/img/icons/drink.png b/www/img/icons/drink.png new file mode 100755 index 0000000..d98359c Binary files /dev/null and b/www/img/icons/drink.png differ diff --git a/www/img/icons/drink_empty.png b/www/img/icons/drink_empty.png new file mode 100755 index 0000000..a40211e Binary files /dev/null and b/www/img/icons/drink_empty.png differ diff --git a/www/img/icons/drive.png b/www/img/icons/drive.png new file mode 100755 index 0000000..37b7c9b Binary files /dev/null and b/www/img/icons/drive.png differ diff --git a/www/img/icons/drive_add.png b/www/img/icons/drive_add.png new file mode 100755 index 0000000..29a35d5 Binary files /dev/null and b/www/img/icons/drive_add.png differ diff --git a/www/img/icons/drive_burn.png b/www/img/icons/drive_burn.png new file mode 100755 index 0000000..80fd79f Binary files /dev/null and b/www/img/icons/drive_burn.png differ diff --git a/www/img/icons/drive_cd.png b/www/img/icons/drive_cd.png new file mode 100755 index 0000000..1850b70 Binary files /dev/null and b/www/img/icons/drive_cd.png differ diff --git a/www/img/icons/drive_cd_empty.png b/www/img/icons/drive_cd_empty.png new file mode 100755 index 0000000..8df38d9 Binary files /dev/null and b/www/img/icons/drive_cd_empty.png differ diff --git a/www/img/icons/drive_delete.png b/www/img/icons/drive_delete.png new file mode 100755 index 0000000..e6eb186 Binary files /dev/null and b/www/img/icons/drive_delete.png differ diff --git a/www/img/icons/drive_disk.png b/www/img/icons/drive_disk.png new file mode 100755 index 0000000..5a51e81 Binary files /dev/null and b/www/img/icons/drive_disk.png differ diff --git a/www/img/icons/drive_edit.png b/www/img/icons/drive_edit.png new file mode 100755 index 0000000..7923fad Binary files /dev/null and b/www/img/icons/drive_edit.png differ diff --git a/www/img/icons/drive_error.png b/www/img/icons/drive_error.png new file mode 100755 index 0000000..309f639 Binary files /dev/null and b/www/img/icons/drive_error.png differ diff --git a/www/img/icons/drive_go.png b/www/img/icons/drive_go.png new file mode 100755 index 0000000..fc53379 Binary files /dev/null and b/www/img/icons/drive_go.png differ diff --git a/www/img/icons/drive_key.png b/www/img/icons/drive_key.png new file mode 100755 index 0000000..d0b3c67 Binary files /dev/null and b/www/img/icons/drive_key.png differ diff --git a/www/img/icons/drive_link.png b/www/img/icons/drive_link.png new file mode 100755 index 0000000..8679c4b Binary files /dev/null and b/www/img/icons/drive_link.png differ diff --git a/www/img/icons/drive_magnify.png b/www/img/icons/drive_magnify.png new file mode 100755 index 0000000..0f0f444 Binary files /dev/null and b/www/img/icons/drive_magnify.png differ diff --git a/www/img/icons/drive_network.png b/www/img/icons/drive_network.png new file mode 100755 index 0000000..63d2d5d Binary files /dev/null and b/www/img/icons/drive_network.png differ diff --git a/www/img/icons/drive_rename.png b/www/img/icons/drive_rename.png new file mode 100755 index 0000000..2a9f38b Binary files /dev/null and b/www/img/icons/drive_rename.png differ diff --git a/www/img/icons/drive_user.png b/www/img/icons/drive_user.png new file mode 100755 index 0000000..0b4751c Binary files /dev/null and b/www/img/icons/drive_user.png differ diff --git a/www/img/icons/drive_web.png b/www/img/icons/drive_web.png new file mode 100755 index 0000000..8850a83 Binary files /dev/null and b/www/img/icons/drive_web.png differ diff --git a/www/img/icons/dvd.png b/www/img/icons/dvd.png new file mode 100755 index 0000000..9d94de5 Binary files /dev/null and b/www/img/icons/dvd.png differ diff --git a/www/img/icons/dvd_add.png b/www/img/icons/dvd_add.png new file mode 100755 index 0000000..517d112 Binary files /dev/null and b/www/img/icons/dvd_add.png differ diff --git a/www/img/icons/dvd_delete.png b/www/img/icons/dvd_delete.png new file mode 100755 index 0000000..87bed22 Binary files /dev/null and b/www/img/icons/dvd_delete.png differ diff --git a/www/img/icons/dvd_edit.png b/www/img/icons/dvd_edit.png new file mode 100755 index 0000000..d6330aa Binary files /dev/null and b/www/img/icons/dvd_edit.png differ diff --git a/www/img/icons/dvd_error.png b/www/img/icons/dvd_error.png new file mode 100755 index 0000000..8f6d4be Binary files /dev/null and b/www/img/icons/dvd_error.png differ diff --git a/www/img/icons/dvd_go.png b/www/img/icons/dvd_go.png new file mode 100755 index 0000000..ef6959f Binary files /dev/null and b/www/img/icons/dvd_go.png differ diff --git a/www/img/icons/dvd_key.png b/www/img/icons/dvd_key.png new file mode 100755 index 0000000..da9307f Binary files /dev/null and b/www/img/icons/dvd_key.png differ diff --git a/www/img/icons/dvd_link.png b/www/img/icons/dvd_link.png new file mode 100755 index 0000000..caad726 Binary files /dev/null and b/www/img/icons/dvd_link.png differ diff --git a/www/img/icons/email.png b/www/img/icons/email.png new file mode 100755 index 0000000..7348aed Binary files /dev/null and b/www/img/icons/email.png differ diff --git a/www/img/icons/email_add.png b/www/img/icons/email_add.png new file mode 100755 index 0000000..6c93368 Binary files /dev/null and b/www/img/icons/email_add.png differ diff --git a/www/img/icons/email_attach.png b/www/img/icons/email_attach.png new file mode 100755 index 0000000..1f99485 Binary files /dev/null and b/www/img/icons/email_attach.png differ diff --git a/www/img/icons/email_delete.png b/www/img/icons/email_delete.png new file mode 100755 index 0000000..a9932b1 Binary files /dev/null and b/www/img/icons/email_delete.png differ diff --git a/www/img/icons/email_edit.png b/www/img/icons/email_edit.png new file mode 100755 index 0000000..244f04a Binary files /dev/null and b/www/img/icons/email_edit.png differ diff --git a/www/img/icons/email_error.png b/www/img/icons/email_error.png new file mode 100755 index 0000000..8bdd330 Binary files /dev/null and b/www/img/icons/email_error.png differ diff --git a/www/img/icons/email_go.png b/www/img/icons/email_go.png new file mode 100755 index 0000000..4a6c5d3 Binary files /dev/null and b/www/img/icons/email_go.png differ diff --git a/www/img/icons/email_link.png b/www/img/icons/email_link.png new file mode 100755 index 0000000..2c49f78 Binary files /dev/null and b/www/img/icons/email_link.png differ diff --git a/www/img/icons/email_open.png b/www/img/icons/email_open.png new file mode 100755 index 0000000..7b6f981 Binary files /dev/null and b/www/img/icons/email_open.png differ diff --git a/www/img/icons/email_open_image.png b/www/img/icons/email_open_image.png new file mode 100755 index 0000000..e588e2f Binary files /dev/null and b/www/img/icons/email_open_image.png differ diff --git a/www/img/icons/emoticon_evilgrin.png b/www/img/icons/emoticon_evilgrin.png new file mode 100755 index 0000000..817bd50 Binary files /dev/null and b/www/img/icons/emoticon_evilgrin.png differ diff --git a/www/img/icons/emoticon_grin.png b/www/img/icons/emoticon_grin.png new file mode 100755 index 0000000..fc60c5e Binary files /dev/null and b/www/img/icons/emoticon_grin.png differ diff --git a/www/img/icons/emoticon_happy.png b/www/img/icons/emoticon_happy.png new file mode 100755 index 0000000..6b7336e Binary files /dev/null and b/www/img/icons/emoticon_happy.png differ diff --git a/www/img/icons/emoticon_smile.png b/www/img/icons/emoticon_smile.png new file mode 100755 index 0000000..ade4318 Binary files /dev/null and b/www/img/icons/emoticon_smile.png differ diff --git a/www/img/icons/emoticon_surprised.png b/www/img/icons/emoticon_surprised.png new file mode 100755 index 0000000..4520cfc Binary files /dev/null and b/www/img/icons/emoticon_surprised.png differ diff --git a/www/img/icons/emoticon_tongue.png b/www/img/icons/emoticon_tongue.png new file mode 100755 index 0000000..ecafd2f Binary files /dev/null and b/www/img/icons/emoticon_tongue.png differ diff --git a/www/img/icons/emoticon_unhappy.png b/www/img/icons/emoticon_unhappy.png new file mode 100755 index 0000000..fd5d030 Binary files /dev/null and b/www/img/icons/emoticon_unhappy.png differ diff --git a/www/img/icons/emoticon_waii.png b/www/img/icons/emoticon_waii.png new file mode 100755 index 0000000..458f936 Binary files /dev/null and b/www/img/icons/emoticon_waii.png differ diff --git a/www/img/icons/emoticon_wink.png b/www/img/icons/emoticon_wink.png new file mode 100755 index 0000000..a631949 Binary files /dev/null and b/www/img/icons/emoticon_wink.png differ diff --git a/www/img/icons/error.png b/www/img/icons/error.png new file mode 100755 index 0000000..628cf2d Binary files /dev/null and b/www/img/icons/error.png differ diff --git a/www/img/icons/error_add.png b/www/img/icons/error_add.png new file mode 100755 index 0000000..4c97484 Binary files /dev/null and b/www/img/icons/error_add.png differ diff --git a/www/img/icons/error_delete.png b/www/img/icons/error_delete.png new file mode 100755 index 0000000..7f78bcc Binary files /dev/null and b/www/img/icons/error_delete.png differ diff --git a/www/img/icons/error_go.png b/www/img/icons/error_go.png new file mode 100755 index 0000000..caa1838 Binary files /dev/null and b/www/img/icons/error_go.png differ diff --git a/www/img/icons/exclamation.png b/www/img/icons/exclamation.png new file mode 100755 index 0000000..c37bd06 Binary files /dev/null and b/www/img/icons/exclamation.png differ diff --git a/www/img/icons/eye.png b/www/img/icons/eye.png new file mode 100755 index 0000000..564a1a9 Binary files /dev/null and b/www/img/icons/eye.png differ diff --git a/www/img/icons/feed.png b/www/img/icons/feed.png new file mode 100755 index 0000000..315c4f4 Binary files /dev/null and b/www/img/icons/feed.png differ diff --git a/www/img/icons/feed_add.png b/www/img/icons/feed_add.png new file mode 100755 index 0000000..e77d46e Binary files /dev/null and b/www/img/icons/feed_add.png differ diff --git a/www/img/icons/feed_delete.png b/www/img/icons/feed_delete.png new file mode 100755 index 0000000..5e332b4 Binary files /dev/null and b/www/img/icons/feed_delete.png differ diff --git a/www/img/icons/feed_disk.png b/www/img/icons/feed_disk.png new file mode 100755 index 0000000..a158c99 Binary files /dev/null and b/www/img/icons/feed_disk.png differ diff --git a/www/img/icons/feed_edit.png b/www/img/icons/feed_edit.png new file mode 100755 index 0000000..f1fde7a Binary files /dev/null and b/www/img/icons/feed_edit.png differ diff --git a/www/img/icons/feed_error.png b/www/img/icons/feed_error.png new file mode 100755 index 0000000..c0a801c Binary files /dev/null and b/www/img/icons/feed_error.png differ diff --git a/www/img/icons/feed_go.png b/www/img/icons/feed_go.png new file mode 100755 index 0000000..f2eed1e Binary files /dev/null and b/www/img/icons/feed_go.png differ diff --git a/www/img/icons/feed_key.png b/www/img/icons/feed_key.png new file mode 100755 index 0000000..156bfa9 Binary files /dev/null and b/www/img/icons/feed_key.png differ diff --git a/www/img/icons/feed_link.png b/www/img/icons/feed_link.png new file mode 100755 index 0000000..c45a534 Binary files /dev/null and b/www/img/icons/feed_link.png differ diff --git a/www/img/icons/feed_magnify.png b/www/img/icons/feed_magnify.png new file mode 100755 index 0000000..3023695 Binary files /dev/null and b/www/img/icons/feed_magnify.png differ diff --git a/www/img/icons/female.png b/www/img/icons/female.png new file mode 100755 index 0000000..f92958e Binary files /dev/null and b/www/img/icons/female.png differ diff --git a/www/img/icons/film.png b/www/img/icons/film.png new file mode 100755 index 0000000..b0ce7bb Binary files /dev/null and b/www/img/icons/film.png differ diff --git a/www/img/icons/film_add.png b/www/img/icons/film_add.png new file mode 100755 index 0000000..40d681f Binary files /dev/null and b/www/img/icons/film_add.png differ diff --git a/www/img/icons/film_delete.png b/www/img/icons/film_delete.png new file mode 100755 index 0000000..23a2508 Binary files /dev/null and b/www/img/icons/film_delete.png differ diff --git a/www/img/icons/film_edit.png b/www/img/icons/film_edit.png new file mode 100755 index 0000000..af66b73 Binary files /dev/null and b/www/img/icons/film_edit.png differ diff --git a/www/img/icons/film_error.png b/www/img/icons/film_error.png new file mode 100755 index 0000000..88f3d69 Binary files /dev/null and b/www/img/icons/film_error.png differ diff --git a/www/img/icons/film_go.png b/www/img/icons/film_go.png new file mode 100755 index 0000000..dd0168e Binary files /dev/null and b/www/img/icons/film_go.png differ diff --git a/www/img/icons/film_key.png b/www/img/icons/film_key.png new file mode 100755 index 0000000..5892162 Binary files /dev/null and b/www/img/icons/film_key.png differ diff --git a/www/img/icons/film_link.png b/www/img/icons/film_link.png new file mode 100755 index 0000000..0f24e86 Binary files /dev/null and b/www/img/icons/film_link.png differ diff --git a/www/img/icons/film_save.png b/www/img/icons/film_save.png new file mode 100755 index 0000000..bc8c0d3 Binary files /dev/null and b/www/img/icons/film_save.png differ diff --git a/www/img/icons/find.png b/www/img/icons/find.png new file mode 100755 index 0000000..1547479 Binary files /dev/null and b/www/img/icons/find.png differ diff --git a/www/img/icons/flag_blue.png b/www/img/icons/flag_blue.png new file mode 100755 index 0000000..003924f Binary files /dev/null and b/www/img/icons/flag_blue.png differ diff --git a/www/img/icons/flag_green.png b/www/img/icons/flag_green.png new file mode 100755 index 0000000..e4bc611 Binary files /dev/null and b/www/img/icons/flag_green.png differ diff --git a/www/img/icons/flag_orange.png b/www/img/icons/flag_orange.png new file mode 100755 index 0000000..e632024 Binary files /dev/null and b/www/img/icons/flag_orange.png differ diff --git a/www/img/icons/flag_pink.png b/www/img/icons/flag_pink.png new file mode 100755 index 0000000..5f15e52 Binary files /dev/null and b/www/img/icons/flag_pink.png differ diff --git a/www/img/icons/flag_purple.png b/www/img/icons/flag_purple.png new file mode 100755 index 0000000..d069866 Binary files /dev/null and b/www/img/icons/flag_purple.png differ diff --git a/www/img/icons/flag_red.png b/www/img/icons/flag_red.png new file mode 100755 index 0000000..e8a602d Binary files /dev/null and b/www/img/icons/flag_red.png differ diff --git a/www/img/icons/flag_yellow.png b/www/img/icons/flag_yellow.png new file mode 100755 index 0000000..14c89a5 Binary files /dev/null and b/www/img/icons/flag_yellow.png differ diff --git a/www/img/icons/folder.png b/www/img/icons/folder.png new file mode 100755 index 0000000..784e8fa Binary files /dev/null and b/www/img/icons/folder.png differ diff --git a/www/img/icons/folder_add.png b/www/img/icons/folder_add.png new file mode 100755 index 0000000..529fe8f Binary files /dev/null and b/www/img/icons/folder_add.png differ diff --git a/www/img/icons/folder_bell.png b/www/img/icons/folder_bell.png new file mode 100755 index 0000000..d04dd7f Binary files /dev/null and b/www/img/icons/folder_bell.png differ diff --git a/www/img/icons/folder_brick.png b/www/img/icons/folder_brick.png new file mode 100755 index 0000000..5dea976 Binary files /dev/null and b/www/img/icons/folder_brick.png differ diff --git a/www/img/icons/folder_bug.png b/www/img/icons/folder_bug.png new file mode 100755 index 0000000..4f791b6 Binary files /dev/null and b/www/img/icons/folder_bug.png differ diff --git a/www/img/icons/folder_camera.png b/www/img/icons/folder_camera.png new file mode 100755 index 0000000..c951941 Binary files /dev/null and b/www/img/icons/folder_camera.png differ diff --git a/www/img/icons/folder_database.png b/www/img/icons/folder_database.png new file mode 100755 index 0000000..5193e2e Binary files /dev/null and b/www/img/icons/folder_database.png differ diff --git a/www/img/icons/folder_delete.png b/www/img/icons/folder_delete.png new file mode 100755 index 0000000..112b016 Binary files /dev/null and b/www/img/icons/folder_delete.png differ diff --git a/www/img/icons/folder_edit.png b/www/img/icons/folder_edit.png new file mode 100755 index 0000000..ad669cc Binary files /dev/null and b/www/img/icons/folder_edit.png differ diff --git a/www/img/icons/folder_error.png b/www/img/icons/folder_error.png new file mode 100755 index 0000000..1af8809 Binary files /dev/null and b/www/img/icons/folder_error.png differ diff --git a/www/img/icons/folder_explore.png b/www/img/icons/folder_explore.png new file mode 100755 index 0000000..0ba9391 Binary files /dev/null and b/www/img/icons/folder_explore.png differ diff --git a/www/img/icons/folder_feed.png b/www/img/icons/folder_feed.png new file mode 100755 index 0000000..d06ee51 Binary files /dev/null and b/www/img/icons/folder_feed.png differ diff --git a/www/img/icons/folder_find.png b/www/img/icons/folder_find.png new file mode 100755 index 0000000..c64e2ee Binary files /dev/null and b/www/img/icons/folder_find.png differ diff --git a/www/img/icons/folder_go.png b/www/img/icons/folder_go.png new file mode 100755 index 0000000..34a736f Binary files /dev/null and b/www/img/icons/folder_go.png differ diff --git a/www/img/icons/folder_heart.png b/www/img/icons/folder_heart.png new file mode 100755 index 0000000..56d7da1 Binary files /dev/null and b/www/img/icons/folder_heart.png differ diff --git a/www/img/icons/folder_image.png b/www/img/icons/folder_image.png new file mode 100755 index 0000000..d5df75b Binary files /dev/null and b/www/img/icons/folder_image.png differ diff --git a/www/img/icons/folder_key.png b/www/img/icons/folder_key.png new file mode 100755 index 0000000..fb9b4c2 Binary files /dev/null and b/www/img/icons/folder_key.png differ diff --git a/www/img/icons/folder_lightbulb.png b/www/img/icons/folder_lightbulb.png new file mode 100755 index 0000000..f367a51 Binary files /dev/null and b/www/img/icons/folder_lightbulb.png differ diff --git a/www/img/icons/folder_link.png b/www/img/icons/folder_link.png new file mode 100755 index 0000000..b9b75f6 Binary files /dev/null and b/www/img/icons/folder_link.png differ diff --git a/www/img/icons/folder_magnify.png b/www/img/icons/folder_magnify.png new file mode 100755 index 0000000..0a3e798 Binary files /dev/null and b/www/img/icons/folder_magnify.png differ diff --git a/www/img/icons/folder_page.png b/www/img/icons/folder_page.png new file mode 100755 index 0000000..1ef6e11 Binary files /dev/null and b/www/img/icons/folder_page.png differ diff --git a/www/img/icons/folder_page_white.png b/www/img/icons/folder_page_white.png new file mode 100755 index 0000000..14d6b61 Binary files /dev/null and b/www/img/icons/folder_page_white.png differ diff --git a/www/img/icons/folder_palette.png b/www/img/icons/folder_palette.png new file mode 100755 index 0000000..ba12fe8 Binary files /dev/null and b/www/img/icons/folder_palette.png differ diff --git a/www/img/icons/folder_picture.png b/www/img/icons/folder_picture.png new file mode 100755 index 0000000..052b336 Binary files /dev/null and b/www/img/icons/folder_picture.png differ diff --git a/www/img/icons/folder_star.png b/www/img/icons/folder_star.png new file mode 100755 index 0000000..448e46f Binary files /dev/null and b/www/img/icons/folder_star.png differ diff --git a/www/img/icons/folder_table.png b/www/img/icons/folder_table.png new file mode 100755 index 0000000..473cee3 Binary files /dev/null and b/www/img/icons/folder_table.png differ diff --git a/www/img/icons/folder_user.png b/www/img/icons/folder_user.png new file mode 100755 index 0000000..f021c3e Binary files /dev/null and b/www/img/icons/folder_user.png differ diff --git a/www/img/icons/folder_wrench.png b/www/img/icons/folder_wrench.png new file mode 100755 index 0000000..ea3404e Binary files /dev/null and b/www/img/icons/folder_wrench.png differ diff --git a/www/img/icons/font.png b/www/img/icons/font.png new file mode 100755 index 0000000..b7960db Binary files /dev/null and b/www/img/icons/font.png differ diff --git a/www/img/icons/font_add.png b/www/img/icons/font_add.png new file mode 100755 index 0000000..b709eba Binary files /dev/null and b/www/img/icons/font_add.png differ diff --git a/www/img/icons/font_delete.png b/www/img/icons/font_delete.png new file mode 100755 index 0000000..1d6124d Binary files /dev/null and b/www/img/icons/font_delete.png differ diff --git a/www/img/icons/font_go.png b/www/img/icons/font_go.png new file mode 100755 index 0000000..75eba80 Binary files /dev/null and b/www/img/icons/font_go.png differ diff --git a/www/img/icons/group.png b/www/img/icons/group.png new file mode 100755 index 0000000..7fb4e1f Binary files /dev/null and b/www/img/icons/group.png differ diff --git a/www/img/icons/group_add.png b/www/img/icons/group_add.png new file mode 100755 index 0000000..06c5350 Binary files /dev/null and b/www/img/icons/group_add.png differ diff --git a/www/img/icons/group_delete.png b/www/img/icons/group_delete.png new file mode 100755 index 0000000..4489ca2 Binary files /dev/null and b/www/img/icons/group_delete.png differ diff --git a/www/img/icons/group_edit.png b/www/img/icons/group_edit.png new file mode 100755 index 0000000..c88b945 Binary files /dev/null and b/www/img/icons/group_edit.png differ diff --git a/www/img/icons/group_error.png b/www/img/icons/group_error.png new file mode 100755 index 0000000..7364a13 Binary files /dev/null and b/www/img/icons/group_error.png differ diff --git a/www/img/icons/group_gear.png b/www/img/icons/group_gear.png new file mode 100755 index 0000000..2544f2e Binary files /dev/null and b/www/img/icons/group_gear.png differ diff --git a/www/img/icons/group_go.png b/www/img/icons/group_go.png new file mode 100755 index 0000000..1f52333 Binary files /dev/null and b/www/img/icons/group_go.png differ diff --git a/www/img/icons/group_key.png b/www/img/icons/group_key.png new file mode 100755 index 0000000..257f111 Binary files /dev/null and b/www/img/icons/group_key.png differ diff --git a/www/img/icons/group_link.png b/www/img/icons/group_link.png new file mode 100755 index 0000000..c77ed88 Binary files /dev/null and b/www/img/icons/group_link.png differ diff --git a/www/img/icons/heart.png b/www/img/icons/heart.png new file mode 100755 index 0000000..d9ee53e Binary files /dev/null and b/www/img/icons/heart.png differ diff --git a/www/img/icons/heart_add.png b/www/img/icons/heart_add.png new file mode 100755 index 0000000..d4195ff Binary files /dev/null and b/www/img/icons/heart_add.png differ diff --git a/www/img/icons/heart_delete.png b/www/img/icons/heart_delete.png new file mode 100755 index 0000000..ce523e3 Binary files /dev/null and b/www/img/icons/heart_delete.png differ diff --git a/www/img/icons/help.png b/www/img/icons/help.png new file mode 100755 index 0000000..5c87017 Binary files /dev/null and b/www/img/icons/help.png differ diff --git a/www/img/icons/hourglass.png b/www/img/icons/hourglass.png new file mode 100755 index 0000000..57b03ce Binary files /dev/null and b/www/img/icons/hourglass.png differ diff --git a/www/img/icons/hourglass_add.png b/www/img/icons/hourglass_add.png new file mode 100755 index 0000000..170dfff Binary files /dev/null and b/www/img/icons/hourglass_add.png differ diff --git a/www/img/icons/hourglass_delete.png b/www/img/icons/hourglass_delete.png new file mode 100755 index 0000000..4b1337b Binary files /dev/null and b/www/img/icons/hourglass_delete.png differ diff --git a/www/img/icons/hourglass_go.png b/www/img/icons/hourglass_go.png new file mode 100755 index 0000000..b2d3a98 Binary files /dev/null and b/www/img/icons/hourglass_go.png differ diff --git a/www/img/icons/hourglass_link.png b/www/img/icons/hourglass_link.png new file mode 100755 index 0000000..ecc59b0 Binary files /dev/null and b/www/img/icons/hourglass_link.png differ diff --git a/www/img/icons/house.png b/www/img/icons/house.png new file mode 100755 index 0000000..fed6221 Binary files /dev/null and b/www/img/icons/house.png differ diff --git a/www/img/icons/house_go.png b/www/img/icons/house_go.png new file mode 100755 index 0000000..5457dbd Binary files /dev/null and b/www/img/icons/house_go.png differ diff --git a/www/img/icons/house_link.png b/www/img/icons/house_link.png new file mode 100755 index 0000000..be2c271 Binary files /dev/null and b/www/img/icons/house_link.png differ diff --git a/www/img/icons/html.png b/www/img/icons/html.png new file mode 100755 index 0000000..55d1072 Binary files /dev/null and b/www/img/icons/html.png differ diff --git a/www/img/icons/html_add.png b/www/img/icons/html_add.png new file mode 100755 index 0000000..f1c08b7 Binary files /dev/null and b/www/img/icons/html_add.png differ diff --git a/www/img/icons/html_delete.png b/www/img/icons/html_delete.png new file mode 100755 index 0000000..1bd2848 Binary files /dev/null and b/www/img/icons/html_delete.png differ diff --git a/www/img/icons/html_go.png b/www/img/icons/html_go.png new file mode 100755 index 0000000..a95cede Binary files /dev/null and b/www/img/icons/html_go.png differ diff --git a/www/img/icons/html_valid.png b/www/img/icons/html_valid.png new file mode 100755 index 0000000..71cec92 Binary files /dev/null and b/www/img/icons/html_valid.png differ diff --git a/www/img/icons/image.png b/www/img/icons/image.png new file mode 100755 index 0000000..fc3c393 Binary files /dev/null and b/www/img/icons/image.png differ diff --git a/www/img/icons/image_add.png b/www/img/icons/image_add.png new file mode 100755 index 0000000..fc5d613 Binary files /dev/null and b/www/img/icons/image_add.png differ diff --git a/www/img/icons/image_delete.png b/www/img/icons/image_delete.png new file mode 100755 index 0000000..c260e1d Binary files /dev/null and b/www/img/icons/image_delete.png differ diff --git a/www/img/icons/image_edit.png b/www/img/icons/image_edit.png new file mode 100755 index 0000000..0aa4cc6 Binary files /dev/null and b/www/img/icons/image_edit.png differ diff --git a/www/img/icons/image_link.png b/www/img/icons/image_link.png new file mode 100755 index 0000000..4bdb354 Binary files /dev/null and b/www/img/icons/image_link.png differ diff --git a/www/img/icons/images.png b/www/img/icons/images.png new file mode 100755 index 0000000..184860d Binary files /dev/null and b/www/img/icons/images.png differ diff --git a/www/img/icons/index_abc.png b/www/img/icons/index_abc.png new file mode 100755 index 0000000..1800909 Binary files /dev/null and b/www/img/icons/index_abc.png differ diff --git a/www/img/icons/information.png b/www/img/icons/information.png new file mode 100755 index 0000000..12cd1ae Binary files /dev/null and b/www/img/icons/information.png differ diff --git a/www/img/icons/ipod.png b/www/img/icons/ipod.png new file mode 100755 index 0000000..3f768da Binary files /dev/null and b/www/img/icons/ipod.png differ diff --git a/www/img/icons/ipod_cast.png b/www/img/icons/ipod_cast.png new file mode 100755 index 0000000..6f6d340 Binary files /dev/null and b/www/img/icons/ipod_cast.png differ diff --git a/www/img/icons/ipod_cast_add.png b/www/img/icons/ipod_cast_add.png new file mode 100755 index 0000000..c3257f5 Binary files /dev/null and b/www/img/icons/ipod_cast_add.png differ diff --git a/www/img/icons/ipod_cast_delete.png b/www/img/icons/ipod_cast_delete.png new file mode 100755 index 0000000..377ab69 Binary files /dev/null and b/www/img/icons/ipod_cast_delete.png differ diff --git a/www/img/icons/ipod_sound.png b/www/img/icons/ipod_sound.png new file mode 100755 index 0000000..fef6e8b Binary files /dev/null and b/www/img/icons/ipod_sound.png differ diff --git a/www/img/icons/joystick.png b/www/img/icons/joystick.png new file mode 100755 index 0000000..62168f5 Binary files /dev/null and b/www/img/icons/joystick.png differ diff --git a/www/img/icons/joystick_add.png b/www/img/icons/joystick_add.png new file mode 100755 index 0000000..77e7107 Binary files /dev/null and b/www/img/icons/joystick_add.png differ diff --git a/www/img/icons/joystick_delete.png b/www/img/icons/joystick_delete.png new file mode 100755 index 0000000..5d44b59 Binary files /dev/null and b/www/img/icons/joystick_delete.png differ diff --git a/www/img/icons/joystick_error.png b/www/img/icons/joystick_error.png new file mode 100755 index 0000000..b32149e Binary files /dev/null and b/www/img/icons/joystick_error.png differ diff --git a/www/img/icons/key.png b/www/img/icons/key.png new file mode 100755 index 0000000..4ec1a92 Binary files /dev/null and b/www/img/icons/key.png differ diff --git a/www/img/icons/key_add.png b/www/img/icons/key_add.png new file mode 100755 index 0000000..d407403 Binary files /dev/null and b/www/img/icons/key_add.png differ diff --git a/www/img/icons/key_delete.png b/www/img/icons/key_delete.png new file mode 100755 index 0000000..00dec80 Binary files /dev/null and b/www/img/icons/key_delete.png differ diff --git a/www/img/icons/key_go.png b/www/img/icons/key_go.png new file mode 100755 index 0000000..30b0dc3 Binary files /dev/null and b/www/img/icons/key_go.png differ diff --git a/www/img/icons/keyboard.png b/www/img/icons/keyboard.png new file mode 100755 index 0000000..898d402 Binary files /dev/null and b/www/img/icons/keyboard.png differ diff --git a/www/img/icons/keyboard_add.png b/www/img/icons/keyboard_add.png new file mode 100755 index 0000000..26938dd Binary files /dev/null and b/www/img/icons/keyboard_add.png differ diff --git a/www/img/icons/keyboard_delete.png b/www/img/icons/keyboard_delete.png new file mode 100755 index 0000000..1786ed5 Binary files /dev/null and b/www/img/icons/keyboard_delete.png differ diff --git a/www/img/icons/keyboard_magnify.png b/www/img/icons/keyboard_magnify.png new file mode 100755 index 0000000..928fc17 Binary files /dev/null and b/www/img/icons/keyboard_magnify.png differ diff --git a/www/img/icons/layers.png b/www/img/icons/layers.png new file mode 100755 index 0000000..00818f6 Binary files /dev/null and b/www/img/icons/layers.png differ diff --git a/www/img/icons/layout.png b/www/img/icons/layout.png new file mode 100755 index 0000000..ea086b0 Binary files /dev/null and b/www/img/icons/layout.png differ diff --git a/www/img/icons/layout_add.png b/www/img/icons/layout_add.png new file mode 100755 index 0000000..6203722 Binary files /dev/null and b/www/img/icons/layout_add.png differ diff --git a/www/img/icons/layout_content.png b/www/img/icons/layout_content.png new file mode 100755 index 0000000..b4aaad9 Binary files /dev/null and b/www/img/icons/layout_content.png differ diff --git a/www/img/icons/layout_delete.png b/www/img/icons/layout_delete.png new file mode 100755 index 0000000..4bd45f1 Binary files /dev/null and b/www/img/icons/layout_delete.png differ diff --git a/www/img/icons/layout_edit.png b/www/img/icons/layout_edit.png new file mode 100755 index 0000000..ab3100b Binary files /dev/null and b/www/img/icons/layout_edit.png differ diff --git a/www/img/icons/layout_error.png b/www/img/icons/layout_error.png new file mode 100755 index 0000000..5b5acea Binary files /dev/null and b/www/img/icons/layout_error.png differ diff --git a/www/img/icons/layout_header.png b/www/img/icons/layout_header.png new file mode 100755 index 0000000..c6ea7f2 Binary files /dev/null and b/www/img/icons/layout_header.png differ diff --git a/www/img/icons/layout_link.png b/www/img/icons/layout_link.png new file mode 100755 index 0000000..3445d42 Binary files /dev/null and b/www/img/icons/layout_link.png differ diff --git a/www/img/icons/layout_sidebar.png b/www/img/icons/layout_sidebar.png new file mode 100755 index 0000000..3be27bb Binary files /dev/null and b/www/img/icons/layout_sidebar.png differ diff --git a/www/img/icons/lightbulb.png b/www/img/icons/lightbulb.png new file mode 100755 index 0000000..d22fde8 Binary files /dev/null and b/www/img/icons/lightbulb.png differ diff --git a/www/img/icons/lightbulb_add.png b/www/img/icons/lightbulb_add.png new file mode 100755 index 0000000..0dd848b Binary files /dev/null and b/www/img/icons/lightbulb_add.png differ diff --git a/www/img/icons/lightbulb_delete.png b/www/img/icons/lightbulb_delete.png new file mode 100755 index 0000000..f4781da Binary files /dev/null and b/www/img/icons/lightbulb_delete.png differ diff --git a/www/img/icons/lightbulb_off.png b/www/img/icons/lightbulb_off.png new file mode 100755 index 0000000..e95b8c5 Binary files /dev/null and b/www/img/icons/lightbulb_off.png differ diff --git a/www/img/icons/lightning.png b/www/img/icons/lightning.png new file mode 100755 index 0000000..9680afd Binary files /dev/null and b/www/img/icons/lightning.png differ diff --git a/www/img/icons/lightning_add.png b/www/img/icons/lightning_add.png new file mode 100755 index 0000000..dac3c90 Binary files /dev/null and b/www/img/icons/lightning_add.png differ diff --git a/www/img/icons/lightning_delete.png b/www/img/icons/lightning_delete.png new file mode 100755 index 0000000..dfe2770 Binary files /dev/null and b/www/img/icons/lightning_delete.png differ diff --git a/www/img/icons/lightning_go.png b/www/img/icons/lightning_go.png new file mode 100755 index 0000000..29039e6 Binary files /dev/null and b/www/img/icons/lightning_go.png differ diff --git a/www/img/icons/link.png b/www/img/icons/link.png new file mode 100755 index 0000000..25eacb7 Binary files /dev/null and b/www/img/icons/link.png differ diff --git a/www/img/icons/link_add.png b/www/img/icons/link_add.png new file mode 100755 index 0000000..00be352 Binary files /dev/null and b/www/img/icons/link_add.png differ diff --git a/www/img/icons/link_break.png b/www/img/icons/link_break.png new file mode 100755 index 0000000..5235753 Binary files /dev/null and b/www/img/icons/link_break.png differ diff --git a/www/img/icons/link_delete.png b/www/img/icons/link_delete.png new file mode 100755 index 0000000..f66e297 Binary files /dev/null and b/www/img/icons/link_delete.png differ diff --git a/www/img/icons/link_edit.png b/www/img/icons/link_edit.png new file mode 100755 index 0000000..5b3aed0 Binary files /dev/null and b/www/img/icons/link_edit.png differ diff --git a/www/img/icons/link_error.png b/www/img/icons/link_error.png new file mode 100755 index 0000000..ab694b1 Binary files /dev/null and b/www/img/icons/link_error.png differ diff --git a/www/img/icons/link_go.png b/www/img/icons/link_go.png new file mode 100755 index 0000000..ae8cae8 Binary files /dev/null and b/www/img/icons/link_go.png differ diff --git a/www/img/icons/lock.png b/www/img/icons/lock.png new file mode 100755 index 0000000..2ebc4f6 Binary files /dev/null and b/www/img/icons/lock.png differ diff --git a/www/img/icons/lock_add.png b/www/img/icons/lock_add.png new file mode 100755 index 0000000..a7b566b Binary files /dev/null and b/www/img/icons/lock_add.png differ diff --git a/www/img/icons/lock_break.png b/www/img/icons/lock_break.png new file mode 100755 index 0000000..13578ab Binary files /dev/null and b/www/img/icons/lock_break.png differ diff --git a/www/img/icons/lock_delete.png b/www/img/icons/lock_delete.png new file mode 100755 index 0000000..ecb50a9 Binary files /dev/null and b/www/img/icons/lock_delete.png differ diff --git a/www/img/icons/lock_edit.png b/www/img/icons/lock_edit.png new file mode 100755 index 0000000..116aa5b Binary files /dev/null and b/www/img/icons/lock_edit.png differ diff --git a/www/img/icons/lock_go.png b/www/img/icons/lock_go.png new file mode 100755 index 0000000..8c7c89b Binary files /dev/null and b/www/img/icons/lock_go.png differ diff --git a/www/img/icons/lock_open.png b/www/img/icons/lock_open.png new file mode 100755 index 0000000..a471765 Binary files /dev/null and b/www/img/icons/lock_open.png differ diff --git a/www/img/icons/lorry.png b/www/img/icons/lorry.png new file mode 100755 index 0000000..8f95f5a Binary files /dev/null and b/www/img/icons/lorry.png differ diff --git a/www/img/icons/lorry_add.png b/www/img/icons/lorry_add.png new file mode 100755 index 0000000..a2c5124 Binary files /dev/null and b/www/img/icons/lorry_add.png differ diff --git a/www/img/icons/lorry_delete.png b/www/img/icons/lorry_delete.png new file mode 100755 index 0000000..66217f5 Binary files /dev/null and b/www/img/icons/lorry_delete.png differ diff --git a/www/img/icons/lorry_error.png b/www/img/icons/lorry_error.png new file mode 100755 index 0000000..3619ead Binary files /dev/null and b/www/img/icons/lorry_error.png differ diff --git a/www/img/icons/lorry_flatbed.png b/www/img/icons/lorry_flatbed.png new file mode 100755 index 0000000..8b20f55 Binary files /dev/null and b/www/img/icons/lorry_flatbed.png differ diff --git a/www/img/icons/lorry_go.png b/www/img/icons/lorry_go.png new file mode 100755 index 0000000..1c296a6 Binary files /dev/null and b/www/img/icons/lorry_go.png differ diff --git a/www/img/icons/lorry_link.png b/www/img/icons/lorry_link.png new file mode 100755 index 0000000..5e6663e Binary files /dev/null and b/www/img/icons/lorry_link.png differ diff --git a/www/img/icons/magifier_zoom_out.png b/www/img/icons/magifier_zoom_out.png new file mode 100755 index 0000000..81f2819 Binary files /dev/null and b/www/img/icons/magifier_zoom_out.png differ diff --git a/www/img/icons/magnifier.png b/www/img/icons/magnifier.png new file mode 100755 index 0000000..cf3d97f Binary files /dev/null and b/www/img/icons/magnifier.png differ diff --git a/www/img/icons/magnifier_zoom_in.png b/www/img/icons/magnifier_zoom_in.png new file mode 100755 index 0000000..af4fe07 Binary files /dev/null and b/www/img/icons/magnifier_zoom_in.png differ diff --git a/www/img/icons/male.png b/www/img/icons/male.png new file mode 100755 index 0000000..25d6ea9 Binary files /dev/null and b/www/img/icons/male.png differ diff --git a/www/img/icons/map.png b/www/img/icons/map.png new file mode 100755 index 0000000..f90ef25 Binary files /dev/null and b/www/img/icons/map.png differ diff --git a/www/img/icons/map_add.png b/www/img/icons/map_add.png new file mode 100755 index 0000000..2b72da0 Binary files /dev/null and b/www/img/icons/map_add.png differ diff --git a/www/img/icons/map_delete.png b/www/img/icons/map_delete.png new file mode 100755 index 0000000..e74402f Binary files /dev/null and b/www/img/icons/map_delete.png differ diff --git a/www/img/icons/map_edit.png b/www/img/icons/map_edit.png new file mode 100755 index 0000000..93d4d7e Binary files /dev/null and b/www/img/icons/map_edit.png differ diff --git a/www/img/icons/map_go.png b/www/img/icons/map_go.png new file mode 100755 index 0000000..11eab26 Binary files /dev/null and b/www/img/icons/map_go.png differ diff --git a/www/img/icons/map_magnify.png b/www/img/icons/map_magnify.png new file mode 100755 index 0000000..7184c9d Binary files /dev/null and b/www/img/icons/map_magnify.png differ diff --git a/www/img/icons/medal_bronze_1.png b/www/img/icons/medal_bronze_1.png new file mode 100755 index 0000000..5f8a6d6 Binary files /dev/null and b/www/img/icons/medal_bronze_1.png differ diff --git a/www/img/icons/medal_bronze_2.png b/www/img/icons/medal_bronze_2.png new file mode 100755 index 0000000..623d68c Binary files /dev/null and b/www/img/icons/medal_bronze_2.png differ diff --git a/www/img/icons/medal_bronze_3.png b/www/img/icons/medal_bronze_3.png new file mode 100755 index 0000000..ed3f43e Binary files /dev/null and b/www/img/icons/medal_bronze_3.png differ diff --git a/www/img/icons/medal_bronze_add.png b/www/img/icons/medal_bronze_add.png new file mode 100755 index 0000000..8487b2c Binary files /dev/null and b/www/img/icons/medal_bronze_add.png differ diff --git a/www/img/icons/medal_bronze_delete.png b/www/img/icons/medal_bronze_delete.png new file mode 100755 index 0000000..d32aed7 Binary files /dev/null and b/www/img/icons/medal_bronze_delete.png differ diff --git a/www/img/icons/medal_gold_1.png b/www/img/icons/medal_gold_1.png new file mode 100755 index 0000000..87584dc Binary files /dev/null and b/www/img/icons/medal_gold_1.png differ diff --git a/www/img/icons/medal_gold_2.png b/www/img/icons/medal_gold_2.png new file mode 100755 index 0000000..fa3a15d Binary files /dev/null and b/www/img/icons/medal_gold_2.png differ diff --git a/www/img/icons/medal_gold_3.png b/www/img/icons/medal_gold_3.png new file mode 100755 index 0000000..ef1b08b Binary files /dev/null and b/www/img/icons/medal_gold_3.png differ diff --git a/www/img/icons/medal_gold_add.png b/www/img/icons/medal_gold_add.png new file mode 100755 index 0000000..dcade0d Binary files /dev/null and b/www/img/icons/medal_gold_add.png differ diff --git a/www/img/icons/medal_gold_delete.png b/www/img/icons/medal_gold_delete.png new file mode 100755 index 0000000..84b06d5 Binary files /dev/null and b/www/img/icons/medal_gold_delete.png differ diff --git a/www/img/icons/medal_silver_1.png b/www/img/icons/medal_silver_1.png new file mode 100755 index 0000000..75a64da Binary files /dev/null and b/www/img/icons/medal_silver_1.png differ diff --git a/www/img/icons/medal_silver_2.png b/www/img/icons/medal_silver_2.png new file mode 100755 index 0000000..2e0fe75 Binary files /dev/null and b/www/img/icons/medal_silver_2.png differ diff --git a/www/img/icons/medal_silver_3.png b/www/img/icons/medal_silver_3.png new file mode 100755 index 0000000..e385b54 Binary files /dev/null and b/www/img/icons/medal_silver_3.png differ diff --git a/www/img/icons/medal_silver_add.png b/www/img/icons/medal_silver_add.png new file mode 100755 index 0000000..b0633fa Binary files /dev/null and b/www/img/icons/medal_silver_add.png differ diff --git a/www/img/icons/medal_silver_delete.png b/www/img/icons/medal_silver_delete.png new file mode 100755 index 0000000..06cab46 Binary files /dev/null and b/www/img/icons/medal_silver_delete.png differ diff --git a/www/img/icons/money.png b/www/img/icons/money.png new file mode 100755 index 0000000..42c52d0 Binary files /dev/null and b/www/img/icons/money.png differ diff --git a/www/img/icons/money_add.png b/www/img/icons/money_add.png new file mode 100755 index 0000000..588fa9d Binary files /dev/null and b/www/img/icons/money_add.png differ diff --git a/www/img/icons/money_delete.png b/www/img/icons/money_delete.png new file mode 100755 index 0000000..eae2c52 Binary files /dev/null and b/www/img/icons/money_delete.png differ diff --git a/www/img/icons/money_dollar.png b/www/img/icons/money_dollar.png new file mode 100755 index 0000000..59af163 Binary files /dev/null and b/www/img/icons/money_dollar.png differ diff --git a/www/img/icons/money_euro.png b/www/img/icons/money_euro.png new file mode 100755 index 0000000..b322ba9 Binary files /dev/null and b/www/img/icons/money_euro.png differ diff --git a/www/img/icons/money_pound.png b/www/img/icons/money_pound.png new file mode 100755 index 0000000..b711364 Binary files /dev/null and b/www/img/icons/money_pound.png differ diff --git a/www/img/icons/money_yen.png b/www/img/icons/money_yen.png new file mode 100755 index 0000000..228a677 Binary files /dev/null and b/www/img/icons/money_yen.png differ diff --git a/www/img/icons/monitor.png b/www/img/icons/monitor.png new file mode 100755 index 0000000..d040bd0 Binary files /dev/null and b/www/img/icons/monitor.png differ diff --git a/www/img/icons/monitor_add.png b/www/img/icons/monitor_add.png new file mode 100755 index 0000000..a818066 Binary files /dev/null and b/www/img/icons/monitor_add.png differ diff --git a/www/img/icons/monitor_delete.png b/www/img/icons/monitor_delete.png new file mode 100755 index 0000000..3733256 Binary files /dev/null and b/www/img/icons/monitor_delete.png differ diff --git a/www/img/icons/monitor_edit.png b/www/img/icons/monitor_edit.png new file mode 100755 index 0000000..f772c56 Binary files /dev/null and b/www/img/icons/monitor_edit.png differ diff --git a/www/img/icons/monitor_error.png b/www/img/icons/monitor_error.png new file mode 100755 index 0000000..270c501 Binary files /dev/null and b/www/img/icons/monitor_error.png differ diff --git a/www/img/icons/monitor_go.png b/www/img/icons/monitor_go.png new file mode 100755 index 0000000..8af3eda Binary files /dev/null and b/www/img/icons/monitor_go.png differ diff --git a/www/img/icons/monitor_lightning.png b/www/img/icons/monitor_lightning.png new file mode 100755 index 0000000..06e53a9 Binary files /dev/null and b/www/img/icons/monitor_lightning.png differ diff --git a/www/img/icons/monitor_link.png b/www/img/icons/monitor_link.png new file mode 100755 index 0000000..a014b02 Binary files /dev/null and b/www/img/icons/monitor_link.png differ diff --git a/www/img/icons/mouse.png b/www/img/icons/mouse.png new file mode 100755 index 0000000..63a92fa Binary files /dev/null and b/www/img/icons/mouse.png differ diff --git a/www/img/icons/mouse_add.png b/www/img/icons/mouse_add.png new file mode 100755 index 0000000..65bcab5 Binary files /dev/null and b/www/img/icons/mouse_add.png differ diff --git a/www/img/icons/mouse_delete.png b/www/img/icons/mouse_delete.png new file mode 100755 index 0000000..7286566 Binary files /dev/null and b/www/img/icons/mouse_delete.png differ diff --git a/www/img/icons/mouse_error.png b/www/img/icons/mouse_error.png new file mode 100755 index 0000000..bcc1562 Binary files /dev/null and b/www/img/icons/mouse_error.png differ diff --git a/www/img/icons/music.png b/www/img/icons/music.png new file mode 100755 index 0000000..a8b3ede Binary files /dev/null and b/www/img/icons/music.png differ diff --git a/www/img/icons/new.png b/www/img/icons/new.png new file mode 100755 index 0000000..6a9bf03 Binary files /dev/null and b/www/img/icons/new.png differ diff --git a/www/img/icons/newspaper.png b/www/img/icons/newspaper.png new file mode 100755 index 0000000..6a2ecce Binary files /dev/null and b/www/img/icons/newspaper.png differ diff --git a/www/img/icons/newspaper_add.png b/www/img/icons/newspaper_add.png new file mode 100755 index 0000000..8140e8c Binary files /dev/null and b/www/img/icons/newspaper_add.png differ diff --git a/www/img/icons/newspaper_delete.png b/www/img/icons/newspaper_delete.png new file mode 100755 index 0000000..bde96ce Binary files /dev/null and b/www/img/icons/newspaper_delete.png differ diff --git a/www/img/icons/newspaper_go.png b/www/img/icons/newspaper_go.png new file mode 100755 index 0000000..fd61428 Binary files /dev/null and b/www/img/icons/newspaper_go.png differ diff --git a/www/img/icons/newspaper_link.png b/www/img/icons/newspaper_link.png new file mode 100755 index 0000000..99e57cb Binary files /dev/null and b/www/img/icons/newspaper_link.png differ diff --git a/www/img/icons/note.png b/www/img/icons/note.png new file mode 100755 index 0000000..244e6ca Binary files /dev/null and b/www/img/icons/note.png differ diff --git a/www/img/icons/note_add.png b/www/img/icons/note_add.png new file mode 100755 index 0000000..abdad91 Binary files /dev/null and b/www/img/icons/note_add.png differ diff --git a/www/img/icons/note_delete.png b/www/img/icons/note_delete.png new file mode 100755 index 0000000..8a1f0ff Binary files /dev/null and b/www/img/icons/note_delete.png differ diff --git a/www/img/icons/note_edit.png b/www/img/icons/note_edit.png new file mode 100755 index 0000000..291bfc7 Binary files /dev/null and b/www/img/icons/note_edit.png differ diff --git a/www/img/icons/note_error.png b/www/img/icons/note_error.png new file mode 100755 index 0000000..896dadf Binary files /dev/null and b/www/img/icons/note_error.png differ diff --git a/www/img/icons/note_go.png b/www/img/icons/note_go.png new file mode 100755 index 0000000..49e54fd Binary files /dev/null and b/www/img/icons/note_go.png differ diff --git a/www/img/icons/overlays.png b/www/img/icons/overlays.png new file mode 100755 index 0000000..ab3100b Binary files /dev/null and b/www/img/icons/overlays.png differ diff --git a/www/img/icons/package.png b/www/img/icons/package.png new file mode 100755 index 0000000..da3c2a2 Binary files /dev/null and b/www/img/icons/package.png differ diff --git a/www/img/icons/package_add.png b/www/img/icons/package_add.png new file mode 100755 index 0000000..9c8a9da Binary files /dev/null and b/www/img/icons/package_add.png differ diff --git a/www/img/icons/package_delete.png b/www/img/icons/package_delete.png new file mode 100755 index 0000000..86f7fbc Binary files /dev/null and b/www/img/icons/package_delete.png differ diff --git a/www/img/icons/package_go.png b/www/img/icons/package_go.png new file mode 100755 index 0000000..aace63a Binary files /dev/null and b/www/img/icons/package_go.png differ diff --git a/www/img/icons/package_green.png b/www/img/icons/package_green.png new file mode 100755 index 0000000..25b28bb Binary files /dev/null and b/www/img/icons/package_green.png differ diff --git a/www/img/icons/package_link.png b/www/img/icons/package_link.png new file mode 100755 index 0000000..48e7ab5 Binary files /dev/null and b/www/img/icons/package_link.png differ diff --git a/www/img/icons/page.png b/www/img/icons/page.png new file mode 100755 index 0000000..03ddd79 Binary files /dev/null and b/www/img/icons/page.png differ diff --git a/www/img/icons/page_add.png b/www/img/icons/page_add.png new file mode 100755 index 0000000..d5bfa07 Binary files /dev/null and b/www/img/icons/page_add.png differ diff --git a/www/img/icons/page_attach.png b/www/img/icons/page_attach.png new file mode 100755 index 0000000..89ee2da Binary files /dev/null and b/www/img/icons/page_attach.png differ diff --git a/www/img/icons/page_code.png b/www/img/icons/page_code.png new file mode 100755 index 0000000..f7ea904 Binary files /dev/null and b/www/img/icons/page_code.png differ diff --git a/www/img/icons/page_copy.png b/www/img/icons/page_copy.png new file mode 100755 index 0000000..195dc6d Binary files /dev/null and b/www/img/icons/page_copy.png differ diff --git a/www/img/icons/page_delete.png b/www/img/icons/page_delete.png new file mode 100755 index 0000000..3141467 Binary files /dev/null and b/www/img/icons/page_delete.png differ diff --git a/www/img/icons/page_edit.png b/www/img/icons/page_edit.png new file mode 100755 index 0000000..046811e Binary files /dev/null and b/www/img/icons/page_edit.png differ diff --git a/www/img/icons/page_error.png b/www/img/icons/page_error.png new file mode 100755 index 0000000..f07f449 Binary files /dev/null and b/www/img/icons/page_error.png differ diff --git a/www/img/icons/page_excel.png b/www/img/icons/page_excel.png new file mode 100755 index 0000000..eb6158e Binary files /dev/null and b/www/img/icons/page_excel.png differ diff --git a/www/img/icons/page_find.png b/www/img/icons/page_find.png new file mode 100755 index 0000000..2f19388 Binary files /dev/null and b/www/img/icons/page_find.png differ diff --git a/www/img/icons/page_gear.png b/www/img/icons/page_gear.png new file mode 100755 index 0000000..8e83281 Binary files /dev/null and b/www/img/icons/page_gear.png differ diff --git a/www/img/icons/page_go.png b/www/img/icons/page_go.png new file mode 100755 index 0000000..80fe1ed Binary files /dev/null and b/www/img/icons/page_go.png differ diff --git a/www/img/icons/page_green.png b/www/img/icons/page_green.png new file mode 100755 index 0000000..de8e003 Binary files /dev/null and b/www/img/icons/page_green.png differ diff --git a/www/img/icons/page_key.png b/www/img/icons/page_key.png new file mode 100755 index 0000000..d6626cb Binary files /dev/null and b/www/img/icons/page_key.png differ diff --git a/www/img/icons/page_lightning.png b/www/img/icons/page_lightning.png new file mode 100755 index 0000000..7e56870 Binary files /dev/null and b/www/img/icons/page_lightning.png differ diff --git a/www/img/icons/page_link.png b/www/img/icons/page_link.png new file mode 100755 index 0000000..312eab0 Binary files /dev/null and b/www/img/icons/page_link.png differ diff --git a/www/img/icons/page_paintbrush.png b/www/img/icons/page_paintbrush.png new file mode 100755 index 0000000..246a2f0 Binary files /dev/null and b/www/img/icons/page_paintbrush.png differ diff --git a/www/img/icons/page_paste.png b/www/img/icons/page_paste.png new file mode 100755 index 0000000..968f073 Binary files /dev/null and b/www/img/icons/page_paste.png differ diff --git a/www/img/icons/page_red.png b/www/img/icons/page_red.png new file mode 100755 index 0000000..0b18247 Binary files /dev/null and b/www/img/icons/page_red.png differ diff --git a/www/img/icons/page_refresh.png b/www/img/icons/page_refresh.png new file mode 100755 index 0000000..cf347c7 Binary files /dev/null and b/www/img/icons/page_refresh.png differ diff --git a/www/img/icons/page_save.png b/www/img/icons/page_save.png new file mode 100755 index 0000000..caea546 Binary files /dev/null and b/www/img/icons/page_save.png differ diff --git a/www/img/icons/page_white.png b/www/img/icons/page_white.png new file mode 100755 index 0000000..8b8b1ca Binary files /dev/null and b/www/img/icons/page_white.png differ diff --git a/www/img/icons/page_white_acrobat.png b/www/img/icons/page_white_acrobat.png new file mode 100755 index 0000000..8f8095e Binary files /dev/null and b/www/img/icons/page_white_acrobat.png differ diff --git a/www/img/icons/page_white_actionscript.png b/www/img/icons/page_white_actionscript.png new file mode 100755 index 0000000..159b240 Binary files /dev/null and b/www/img/icons/page_white_actionscript.png differ diff --git a/www/img/icons/page_white_add.png b/www/img/icons/page_white_add.png new file mode 100755 index 0000000..aa23dde Binary files /dev/null and b/www/img/icons/page_white_add.png differ diff --git a/www/img/icons/page_white_c.png b/www/img/icons/page_white_c.png new file mode 100755 index 0000000..34a05cc Binary files /dev/null and b/www/img/icons/page_white_c.png differ diff --git a/www/img/icons/page_white_camera.png b/www/img/icons/page_white_camera.png new file mode 100755 index 0000000..f501a59 Binary files /dev/null and b/www/img/icons/page_white_camera.png differ diff --git a/www/img/icons/page_white_cd.png b/www/img/icons/page_white_cd.png new file mode 100755 index 0000000..848bdaf Binary files /dev/null and b/www/img/icons/page_white_cd.png differ diff --git a/www/img/icons/page_white_code.png b/www/img/icons/page_white_code.png new file mode 100755 index 0000000..0c76bd1 Binary files /dev/null and b/www/img/icons/page_white_code.png differ diff --git a/www/img/icons/page_white_code_red.png b/www/img/icons/page_white_code_red.png new file mode 100755 index 0000000..87a6914 Binary files /dev/null and b/www/img/icons/page_white_code_red.png differ diff --git a/www/img/icons/page_white_coldfusion.png b/www/img/icons/page_white_coldfusion.png new file mode 100755 index 0000000..c66011f Binary files /dev/null and b/www/img/icons/page_white_coldfusion.png differ diff --git a/www/img/icons/page_white_compressed.png b/www/img/icons/page_white_compressed.png new file mode 100755 index 0000000..2b6b100 Binary files /dev/null and b/www/img/icons/page_white_compressed.png differ diff --git a/www/img/icons/page_white_copy.png b/www/img/icons/page_white_copy.png new file mode 100755 index 0000000..a9f31a2 Binary files /dev/null and b/www/img/icons/page_white_copy.png differ diff --git a/www/img/icons/page_white_cplusplus.png b/www/img/icons/page_white_cplusplus.png new file mode 100755 index 0000000..a87cf84 Binary files /dev/null and b/www/img/icons/page_white_cplusplus.png differ diff --git a/www/img/icons/page_white_csharp.png b/www/img/icons/page_white_csharp.png new file mode 100755 index 0000000..ffb8fc9 Binary files /dev/null and b/www/img/icons/page_white_csharp.png differ diff --git a/www/img/icons/page_white_cup.png b/www/img/icons/page_white_cup.png new file mode 100755 index 0000000..0a7d6f4 Binary files /dev/null and b/www/img/icons/page_white_cup.png differ diff --git a/www/img/icons/page_white_database.png b/www/img/icons/page_white_database.png new file mode 100755 index 0000000..bddba1f Binary files /dev/null and b/www/img/icons/page_white_database.png differ diff --git a/www/img/icons/page_white_delete.png b/www/img/icons/page_white_delete.png new file mode 100755 index 0000000..af1ecaf Binary files /dev/null and b/www/img/icons/page_white_delete.png differ diff --git a/www/img/icons/page_white_dvd.png b/www/img/icons/page_white_dvd.png new file mode 100755 index 0000000..4cc537a Binary files /dev/null and b/www/img/icons/page_white_dvd.png differ diff --git a/www/img/icons/page_white_edit.png b/www/img/icons/page_white_edit.png new file mode 100755 index 0000000..b93e776 Binary files /dev/null and b/www/img/icons/page_white_edit.png differ diff --git a/www/img/icons/page_white_error.png b/www/img/icons/page_white_error.png new file mode 100755 index 0000000..9fc5a0a Binary files /dev/null and b/www/img/icons/page_white_error.png differ diff --git a/www/img/icons/page_white_excel.png b/www/img/icons/page_white_excel.png new file mode 100755 index 0000000..b977d7e Binary files /dev/null and b/www/img/icons/page_white_excel.png differ diff --git a/www/img/icons/page_white_find.png b/www/img/icons/page_white_find.png new file mode 100755 index 0000000..5818436 Binary files /dev/null and b/www/img/icons/page_white_find.png differ diff --git a/www/img/icons/page_white_flash.png b/www/img/icons/page_white_flash.png new file mode 100755 index 0000000..5769120 Binary files /dev/null and b/www/img/icons/page_white_flash.png differ diff --git a/www/img/icons/page_white_freehand.png b/www/img/icons/page_white_freehand.png new file mode 100755 index 0000000..8d719df Binary files /dev/null and b/www/img/icons/page_white_freehand.png differ diff --git a/www/img/icons/page_white_gear.png b/www/img/icons/page_white_gear.png new file mode 100755 index 0000000..106f5aa Binary files /dev/null and b/www/img/icons/page_white_gear.png differ diff --git a/www/img/icons/page_white_get.png b/www/img/icons/page_white_get.png new file mode 100755 index 0000000..e4a1ecb Binary files /dev/null and b/www/img/icons/page_white_get.png differ diff --git a/www/img/icons/page_white_go.png b/www/img/icons/page_white_go.png new file mode 100755 index 0000000..7e62a92 Binary files /dev/null and b/www/img/icons/page_white_go.png differ diff --git a/www/img/icons/page_white_h.png b/www/img/icons/page_white_h.png new file mode 100755 index 0000000..e902abb Binary files /dev/null and b/www/img/icons/page_white_h.png differ diff --git a/www/img/icons/page_white_horizontal.png b/www/img/icons/page_white_horizontal.png new file mode 100755 index 0000000..1d2d0a4 Binary files /dev/null and b/www/img/icons/page_white_horizontal.png differ diff --git a/www/img/icons/page_white_key.png b/www/img/icons/page_white_key.png new file mode 100755 index 0000000..d616484 Binary files /dev/null and b/www/img/icons/page_white_key.png differ diff --git a/www/img/icons/page_white_lightning.png b/www/img/icons/page_white_lightning.png new file mode 100755 index 0000000..7215d1e Binary files /dev/null and b/www/img/icons/page_white_lightning.png differ diff --git a/www/img/icons/page_white_link.png b/www/img/icons/page_white_link.png new file mode 100755 index 0000000..bf7bd1c Binary files /dev/null and b/www/img/icons/page_white_link.png differ diff --git a/www/img/icons/page_white_magnify.png b/www/img/icons/page_white_magnify.png new file mode 100755 index 0000000..f6b74cc Binary files /dev/null and b/www/img/icons/page_white_magnify.png differ diff --git a/www/img/icons/page_white_medal.png b/www/img/icons/page_white_medal.png new file mode 100755 index 0000000..d3fffb6 Binary files /dev/null and b/www/img/icons/page_white_medal.png differ diff --git a/www/img/icons/page_white_office.png b/www/img/icons/page_white_office.png new file mode 100755 index 0000000..a65bcb3 Binary files /dev/null and b/www/img/icons/page_white_office.png differ diff --git a/www/img/icons/page_white_paint.png b/www/img/icons/page_white_paint.png new file mode 100755 index 0000000..23a37b8 Binary files /dev/null and b/www/img/icons/page_white_paint.png differ diff --git a/www/img/icons/page_white_paintbrush.png b/www/img/icons/page_white_paintbrush.png new file mode 100755 index 0000000..f907e44 Binary files /dev/null and b/www/img/icons/page_white_paintbrush.png differ diff --git a/www/img/icons/page_white_paste.png b/www/img/icons/page_white_paste.png new file mode 100755 index 0000000..5b2cbb3 Binary files /dev/null and b/www/img/icons/page_white_paste.png differ diff --git a/www/img/icons/page_white_php.png b/www/img/icons/page_white_php.png new file mode 100755 index 0000000..7868a25 Binary files /dev/null and b/www/img/icons/page_white_php.png differ diff --git a/www/img/icons/page_white_picture.png b/www/img/icons/page_white_picture.png new file mode 100755 index 0000000..134b669 Binary files /dev/null and b/www/img/icons/page_white_picture.png differ diff --git a/www/img/icons/page_white_powerpoint.png b/www/img/icons/page_white_powerpoint.png new file mode 100755 index 0000000..c4eff03 Binary files /dev/null and b/www/img/icons/page_white_powerpoint.png differ diff --git a/www/img/icons/page_white_put.png b/www/img/icons/page_white_put.png new file mode 100755 index 0000000..884ffd6 Binary files /dev/null and b/www/img/icons/page_white_put.png differ diff --git a/www/img/icons/page_white_ruby.png b/www/img/icons/page_white_ruby.png new file mode 100755 index 0000000..f59b7c4 Binary files /dev/null and b/www/img/icons/page_white_ruby.png differ diff --git a/www/img/icons/page_white_stack.png b/www/img/icons/page_white_stack.png new file mode 100755 index 0000000..44084ad Binary files /dev/null and b/www/img/icons/page_white_stack.png differ diff --git a/www/img/icons/page_white_star.png b/www/img/icons/page_white_star.png new file mode 100755 index 0000000..3a1441c Binary files /dev/null and b/www/img/icons/page_white_star.png differ diff --git a/www/img/icons/page_white_swoosh.png b/www/img/icons/page_white_swoosh.png new file mode 100755 index 0000000..e770829 Binary files /dev/null and b/www/img/icons/page_white_swoosh.png differ diff --git a/www/img/icons/page_white_text.png b/www/img/icons/page_white_text.png new file mode 100755 index 0000000..813f712 Binary files /dev/null and b/www/img/icons/page_white_text.png differ diff --git a/www/img/icons/page_white_text_width.png b/www/img/icons/page_white_text_width.png new file mode 100755 index 0000000..d9cf132 Binary files /dev/null and b/www/img/icons/page_white_text_width.png differ diff --git a/www/img/icons/page_white_tux.png b/www/img/icons/page_white_tux.png new file mode 100755 index 0000000..52699bf Binary files /dev/null and b/www/img/icons/page_white_tux.png differ diff --git a/www/img/icons/page_white_vector.png b/www/img/icons/page_white_vector.png new file mode 100755 index 0000000..4a05955 Binary files /dev/null and b/www/img/icons/page_white_vector.png differ diff --git a/www/img/icons/page_white_visualstudio.png b/www/img/icons/page_white_visualstudio.png new file mode 100755 index 0000000..a0a433d Binary files /dev/null and b/www/img/icons/page_white_visualstudio.png differ diff --git a/www/img/icons/page_white_width.png b/www/img/icons/page_white_width.png new file mode 100755 index 0000000..1eb8809 Binary files /dev/null and b/www/img/icons/page_white_width.png differ diff --git a/www/img/icons/page_white_word.png b/www/img/icons/page_white_word.png new file mode 100755 index 0000000..ae8ecbf Binary files /dev/null and b/www/img/icons/page_white_word.png differ diff --git a/www/img/icons/page_white_world.png b/www/img/icons/page_white_world.png new file mode 100755 index 0000000..6ed2490 Binary files /dev/null and b/www/img/icons/page_white_world.png differ diff --git a/www/img/icons/page_white_wrench.png b/www/img/icons/page_white_wrench.png new file mode 100755 index 0000000..fecadd0 Binary files /dev/null and b/www/img/icons/page_white_wrench.png differ diff --git a/www/img/icons/page_white_zip.png b/www/img/icons/page_white_zip.png new file mode 100755 index 0000000..fd4bbcc Binary files /dev/null and b/www/img/icons/page_white_zip.png differ diff --git a/www/img/icons/page_word.png b/www/img/icons/page_word.png new file mode 100755 index 0000000..834cdfa Binary files /dev/null and b/www/img/icons/page_word.png differ diff --git a/www/img/icons/page_world.png b/www/img/icons/page_world.png new file mode 100755 index 0000000..b8895dd Binary files /dev/null and b/www/img/icons/page_world.png differ diff --git a/www/img/icons/paintbrush.png b/www/img/icons/paintbrush.png new file mode 100755 index 0000000..a3ecf87 Binary files /dev/null and b/www/img/icons/paintbrush.png differ diff --git a/www/img/icons/paintcan.png b/www/img/icons/paintcan.png new file mode 100755 index 0000000..f82a886 Binary files /dev/null and b/www/img/icons/paintcan.png differ diff --git a/www/img/icons/palette.png b/www/img/icons/palette.png new file mode 100755 index 0000000..73c5b3f Binary files /dev/null and b/www/img/icons/palette.png differ diff --git a/www/img/icons/paste_plain.png b/www/img/icons/paste_plain.png new file mode 100755 index 0000000..c0490eb Binary files /dev/null and b/www/img/icons/paste_plain.png differ diff --git a/www/img/icons/paste_word.png b/www/img/icons/paste_word.png new file mode 100755 index 0000000..f6b87f8 Binary files /dev/null and b/www/img/icons/paste_word.png differ diff --git a/www/img/icons/pencil.png b/www/img/icons/pencil.png new file mode 100755 index 0000000..0bfecd5 Binary files /dev/null and b/www/img/icons/pencil.png differ diff --git a/www/img/icons/pencil_add.png b/www/img/icons/pencil_add.png new file mode 100755 index 0000000..902bbe6 Binary files /dev/null and b/www/img/icons/pencil_add.png differ diff --git a/www/img/icons/pencil_delete.png b/www/img/icons/pencil_delete.png new file mode 100755 index 0000000..d8944e6 Binary files /dev/null and b/www/img/icons/pencil_delete.png differ diff --git a/www/img/icons/pencil_go.png b/www/img/icons/pencil_go.png new file mode 100755 index 0000000..937bded Binary files /dev/null and b/www/img/icons/pencil_go.png differ diff --git a/www/img/icons/phone.png b/www/img/icons/phone.png new file mode 100755 index 0000000..c39f162 Binary files /dev/null and b/www/img/icons/phone.png differ diff --git a/www/img/icons/phone_add.png b/www/img/icons/phone_add.png new file mode 100755 index 0000000..d3555e0 Binary files /dev/null and b/www/img/icons/phone_add.png differ diff --git a/www/img/icons/phone_delete.png b/www/img/icons/phone_delete.png new file mode 100755 index 0000000..bbe4f8a Binary files /dev/null and b/www/img/icons/phone_delete.png differ diff --git a/www/img/icons/phone_sound.png b/www/img/icons/phone_sound.png new file mode 100755 index 0000000..7fdf1c5 Binary files /dev/null and b/www/img/icons/phone_sound.png differ diff --git a/www/img/icons/photo.png b/www/img/icons/photo.png new file mode 100755 index 0000000..6c2aaaa Binary files /dev/null and b/www/img/icons/photo.png differ diff --git a/www/img/icons/photo_add.png b/www/img/icons/photo_add.png new file mode 100755 index 0000000..63cc355 Binary files /dev/null and b/www/img/icons/photo_add.png differ diff --git a/www/img/icons/photo_delete.png b/www/img/icons/photo_delete.png new file mode 100755 index 0000000..18b67df Binary files /dev/null and b/www/img/icons/photo_delete.png differ diff --git a/www/img/icons/photo_link.png b/www/img/icons/photo_link.png new file mode 100755 index 0000000..e6bb35f Binary files /dev/null and b/www/img/icons/photo_link.png differ diff --git a/www/img/icons/photos.png b/www/img/icons/photos.png new file mode 100755 index 0000000..8836fe6 Binary files /dev/null and b/www/img/icons/photos.png differ diff --git a/www/img/icons/picture.png b/www/img/icons/picture.png new file mode 100755 index 0000000..4a158fe Binary files /dev/null and b/www/img/icons/picture.png differ diff --git a/www/img/icons/picture_add.png b/www/img/icons/picture_add.png new file mode 100755 index 0000000..d6d3f85 Binary files /dev/null and b/www/img/icons/picture_add.png differ diff --git a/www/img/icons/picture_delete.png b/www/img/icons/picture_delete.png new file mode 100755 index 0000000..cca9f53 Binary files /dev/null and b/www/img/icons/picture_delete.png differ diff --git a/www/img/icons/picture_edit.png b/www/img/icons/picture_edit.png new file mode 100755 index 0000000..9a70c34 Binary files /dev/null and b/www/img/icons/picture_edit.png differ diff --git a/www/img/icons/picture_empty.png b/www/img/icons/picture_empty.png new file mode 100755 index 0000000..abd2b9b Binary files /dev/null and b/www/img/icons/picture_empty.png differ diff --git a/www/img/icons/picture_error.png b/www/img/icons/picture_error.png new file mode 100755 index 0000000..d41d90d Binary files /dev/null and b/www/img/icons/picture_error.png differ diff --git a/www/img/icons/picture_go.png b/www/img/icons/picture_go.png new file mode 100755 index 0000000..27c63c5 Binary files /dev/null and b/www/img/icons/picture_go.png differ diff --git a/www/img/icons/picture_key.png b/www/img/icons/picture_key.png new file mode 100755 index 0000000..667086c Binary files /dev/null and b/www/img/icons/picture_key.png differ diff --git a/www/img/icons/picture_link.png b/www/img/icons/picture_link.png new file mode 100755 index 0000000..42dca74 Binary files /dev/null and b/www/img/icons/picture_link.png differ diff --git a/www/img/icons/picture_save.png b/www/img/icons/picture_save.png new file mode 100755 index 0000000..777fb5d Binary files /dev/null and b/www/img/icons/picture_save.png differ diff --git a/www/img/icons/pictures.png b/www/img/icons/pictures.png new file mode 100755 index 0000000..d9591c1 Binary files /dev/null and b/www/img/icons/pictures.png differ diff --git a/www/img/icons/pilcrow.png b/www/img/icons/pilcrow.png new file mode 100755 index 0000000..95704fb Binary files /dev/null and b/www/img/icons/pilcrow.png differ diff --git a/www/img/icons/pill.png b/www/img/icons/pill.png new file mode 100755 index 0000000..f2bdef6 Binary files /dev/null and b/www/img/icons/pill.png differ diff --git a/www/img/icons/pill_add.png b/www/img/icons/pill_add.png new file mode 100755 index 0000000..ac9c2df Binary files /dev/null and b/www/img/icons/pill_add.png differ diff --git a/www/img/icons/pill_delete.png b/www/img/icons/pill_delete.png new file mode 100755 index 0000000..c61592e Binary files /dev/null and b/www/img/icons/pill_delete.png differ diff --git a/www/img/icons/pill_go.png b/www/img/icons/pill_go.png new file mode 100755 index 0000000..e5c07d4 Binary files /dev/null and b/www/img/icons/pill_go.png differ diff --git a/www/img/icons/plugin.png b/www/img/icons/plugin.png new file mode 100755 index 0000000..6187b15 Binary files /dev/null and b/www/img/icons/plugin.png differ diff --git a/www/img/icons/plugin_add.png b/www/img/icons/plugin_add.png new file mode 100755 index 0000000..ae43690 Binary files /dev/null and b/www/img/icons/plugin_add.png differ diff --git a/www/img/icons/plugin_delete.png b/www/img/icons/plugin_delete.png new file mode 100755 index 0000000..d9c3376 Binary files /dev/null and b/www/img/icons/plugin_delete.png differ diff --git a/www/img/icons/plugin_disabled.png b/www/img/icons/plugin_disabled.png new file mode 100755 index 0000000..f4f6be5 Binary files /dev/null and b/www/img/icons/plugin_disabled.png differ diff --git a/www/img/icons/plugin_edit.png b/www/img/icons/plugin_edit.png new file mode 100755 index 0000000..b6cb0ec Binary files /dev/null and b/www/img/icons/plugin_edit.png differ diff --git a/www/img/icons/plugin_error.png b/www/img/icons/plugin_error.png new file mode 100755 index 0000000..cff65d7 Binary files /dev/null and b/www/img/icons/plugin_error.png differ diff --git a/www/img/icons/plugin_go.png b/www/img/icons/plugin_go.png new file mode 100755 index 0000000..41da991 Binary files /dev/null and b/www/img/icons/plugin_go.png differ diff --git a/www/img/icons/plugin_link.png b/www/img/icons/plugin_link.png new file mode 100755 index 0000000..445c188 Binary files /dev/null and b/www/img/icons/plugin_link.png differ diff --git a/www/img/icons/printer.png b/www/img/icons/printer.png new file mode 100755 index 0000000..a350d18 Binary files /dev/null and b/www/img/icons/printer.png differ diff --git a/www/img/icons/printer_add.png b/www/img/icons/printer_add.png new file mode 100755 index 0000000..d228d05 Binary files /dev/null and b/www/img/icons/printer_add.png differ diff --git a/www/img/icons/printer_delete.png b/www/img/icons/printer_delete.png new file mode 100755 index 0000000..1d8605f Binary files /dev/null and b/www/img/icons/printer_delete.png differ diff --git a/www/img/icons/printer_empty.png b/www/img/icons/printer_empty.png new file mode 100755 index 0000000..94e8c16 Binary files /dev/null and b/www/img/icons/printer_empty.png differ diff --git a/www/img/icons/printer_error.png b/www/img/icons/printer_error.png new file mode 100755 index 0000000..279ebb0 Binary files /dev/null and b/www/img/icons/printer_error.png differ diff --git a/www/img/icons/rainbow.png b/www/img/icons/rainbow.png new file mode 100755 index 0000000..5ede989 Binary files /dev/null and b/www/img/icons/rainbow.png differ diff --git a/www/img/icons/report.png b/www/img/icons/report.png new file mode 100755 index 0000000..779ad58 Binary files /dev/null and b/www/img/icons/report.png differ diff --git a/www/img/icons/report_add.png b/www/img/icons/report_add.png new file mode 100755 index 0000000..d5eac9b Binary files /dev/null and b/www/img/icons/report_add.png differ diff --git a/www/img/icons/report_delete.png b/www/img/icons/report_delete.png new file mode 100755 index 0000000..dcce0b6 Binary files /dev/null and b/www/img/icons/report_delete.png differ diff --git a/www/img/icons/report_disk.png b/www/img/icons/report_disk.png new file mode 100755 index 0000000..1c856cd Binary files /dev/null and b/www/img/icons/report_disk.png differ diff --git a/www/img/icons/report_edit.png b/www/img/icons/report_edit.png new file mode 100755 index 0000000..c61a6d8 Binary files /dev/null and b/www/img/icons/report_edit.png differ diff --git a/www/img/icons/report_go.png b/www/img/icons/report_go.png new file mode 100755 index 0000000..f35a979 Binary files /dev/null and b/www/img/icons/report_go.png differ diff --git a/www/img/icons/report_key.png b/www/img/icons/report_key.png new file mode 100755 index 0000000..90b758e Binary files /dev/null and b/www/img/icons/report_key.png differ diff --git a/www/img/icons/report_link.png b/www/img/icons/report_link.png new file mode 100755 index 0000000..23f2611 Binary files /dev/null and b/www/img/icons/report_link.png differ diff --git a/www/img/icons/report_magnify.png b/www/img/icons/report_magnify.png new file mode 100755 index 0000000..aeaa889 Binary files /dev/null and b/www/img/icons/report_magnify.png differ diff --git a/www/img/icons/report_picture.png b/www/img/icons/report_picture.png new file mode 100755 index 0000000..3a9a7e5 Binary files /dev/null and b/www/img/icons/report_picture.png differ diff --git a/www/img/icons/report_user.png b/www/img/icons/report_user.png new file mode 100755 index 0000000..7766edd Binary files /dev/null and b/www/img/icons/report_user.png differ diff --git a/www/img/icons/report_word.png b/www/img/icons/report_word.png new file mode 100755 index 0000000..9951342 Binary files /dev/null and b/www/img/icons/report_word.png differ diff --git a/www/img/icons/resultset_first.png b/www/img/icons/resultset_first.png new file mode 100755 index 0000000..b03eaf8 Binary files /dev/null and b/www/img/icons/resultset_first.png differ diff --git a/www/img/icons/resultset_last.png b/www/img/icons/resultset_last.png new file mode 100755 index 0000000..8ec8947 Binary files /dev/null and b/www/img/icons/resultset_last.png differ diff --git a/www/img/icons/resultset_next.png b/www/img/icons/resultset_next.png new file mode 100755 index 0000000..e252606 Binary files /dev/null and b/www/img/icons/resultset_next.png differ diff --git a/www/img/icons/resultset_previous.png b/www/img/icons/resultset_previous.png new file mode 100755 index 0000000..18f9cc1 Binary files /dev/null and b/www/img/icons/resultset_previous.png differ diff --git a/www/img/icons/rosette.png b/www/img/icons/rosette.png new file mode 100755 index 0000000..f233bc7 Binary files /dev/null and b/www/img/icons/rosette.png differ diff --git a/www/img/icons/rss.png b/www/img/icons/rss.png new file mode 100755 index 0000000..1dc6ff3 Binary files /dev/null and b/www/img/icons/rss.png differ diff --git a/www/img/icons/rss_add.png b/www/img/icons/rss_add.png new file mode 100755 index 0000000..b590beb Binary files /dev/null and b/www/img/icons/rss_add.png differ diff --git a/www/img/icons/rss_delete.png b/www/img/icons/rss_delete.png new file mode 100755 index 0000000..9deb738 Binary files /dev/null and b/www/img/icons/rss_delete.png differ diff --git a/www/img/icons/rss_go.png b/www/img/icons/rss_go.png new file mode 100755 index 0000000..43a86bf Binary files /dev/null and b/www/img/icons/rss_go.png differ diff --git a/www/img/icons/rss_valid.png b/www/img/icons/rss_valid.png new file mode 100755 index 0000000..a6d0b0e Binary files /dev/null and b/www/img/icons/rss_valid.png differ diff --git a/www/img/icons/ruby.png b/www/img/icons/ruby.png new file mode 100755 index 0000000..f763a16 Binary files /dev/null and b/www/img/icons/ruby.png differ diff --git a/www/img/icons/ruby_add.png b/www/img/icons/ruby_add.png new file mode 100755 index 0000000..a2cd648 Binary files /dev/null and b/www/img/icons/ruby_add.png differ diff --git a/www/img/icons/ruby_delete.png b/www/img/icons/ruby_delete.png new file mode 100755 index 0000000..3002263 Binary files /dev/null and b/www/img/icons/ruby_delete.png differ diff --git a/www/img/icons/ruby_gear.png b/www/img/icons/ruby_gear.png new file mode 100755 index 0000000..4a10590 Binary files /dev/null and b/www/img/icons/ruby_gear.png differ diff --git a/www/img/icons/ruby_get.png b/www/img/icons/ruby_get.png new file mode 100755 index 0000000..f5203c7 Binary files /dev/null and b/www/img/icons/ruby_get.png differ diff --git a/www/img/icons/ruby_go.png b/www/img/icons/ruby_go.png new file mode 100755 index 0000000..d8d276e Binary files /dev/null and b/www/img/icons/ruby_go.png differ diff --git a/www/img/icons/ruby_key.png b/www/img/icons/ruby_key.png new file mode 100755 index 0000000..451cfeb Binary files /dev/null and b/www/img/icons/ruby_key.png differ diff --git a/www/img/icons/ruby_link.png b/www/img/icons/ruby_link.png new file mode 100755 index 0000000..bf4be52 Binary files /dev/null and b/www/img/icons/ruby_link.png differ diff --git a/www/img/icons/ruby_put.png b/www/img/icons/ruby_put.png new file mode 100755 index 0000000..e026323 Binary files /dev/null and b/www/img/icons/ruby_put.png differ diff --git a/www/img/icons/script.png b/www/img/icons/script.png new file mode 100755 index 0000000..0f9ed4d Binary files /dev/null and b/www/img/icons/script.png differ diff --git a/www/img/icons/script_add.png b/www/img/icons/script_add.png new file mode 100755 index 0000000..d650552 Binary files /dev/null and b/www/img/icons/script_add.png differ diff --git a/www/img/icons/script_code.png b/www/img/icons/script_code.png new file mode 100755 index 0000000..63fe6ce Binary files /dev/null and b/www/img/icons/script_code.png differ diff --git a/www/img/icons/script_code_red.png b/www/img/icons/script_code_red.png new file mode 100755 index 0000000..8fcf0f0 Binary files /dev/null and b/www/img/icons/script_code_red.png differ diff --git a/www/img/icons/script_delete.png b/www/img/icons/script_delete.png new file mode 100755 index 0000000..e6500ce Binary files /dev/null and b/www/img/icons/script_delete.png differ diff --git a/www/img/icons/script_edit.png b/www/img/icons/script_edit.png new file mode 100755 index 0000000..b4d31ce Binary files /dev/null and b/www/img/icons/script_edit.png differ diff --git a/www/img/icons/script_error.png b/www/img/icons/script_error.png new file mode 100755 index 0000000..0491954 Binary files /dev/null and b/www/img/icons/script_error.png differ diff --git a/www/img/icons/script_gear.png b/www/img/icons/script_gear.png new file mode 100755 index 0000000..56fcf84 Binary files /dev/null and b/www/img/icons/script_gear.png differ diff --git a/www/img/icons/script_go.png b/www/img/icons/script_go.png new file mode 100755 index 0000000..8e154e2 Binary files /dev/null and b/www/img/icons/script_go.png differ diff --git a/www/img/icons/script_key.png b/www/img/icons/script_key.png new file mode 100755 index 0000000..49bb24d Binary files /dev/null and b/www/img/icons/script_key.png differ diff --git a/www/img/icons/script_lightning.png b/www/img/icons/script_lightning.png new file mode 100755 index 0000000..b3fa18c Binary files /dev/null and b/www/img/icons/script_lightning.png differ diff --git a/www/img/icons/script_link.png b/www/img/icons/script_link.png new file mode 100755 index 0000000..bdeb985 Binary files /dev/null and b/www/img/icons/script_link.png differ diff --git a/www/img/icons/script_palette.png b/www/img/icons/script_palette.png new file mode 100755 index 0000000..6d46962 Binary files /dev/null and b/www/img/icons/script_palette.png differ diff --git a/www/img/icons/script_save.png b/www/img/icons/script_save.png new file mode 100755 index 0000000..36216d8 Binary files /dev/null and b/www/img/icons/script_save.png differ diff --git a/www/img/icons/server.png b/www/img/icons/server.png new file mode 100755 index 0000000..720a237 Binary files /dev/null and b/www/img/icons/server.png differ diff --git a/www/img/icons/server_add.png b/www/img/icons/server_add.png new file mode 100755 index 0000000..3f10a3a Binary files /dev/null and b/www/img/icons/server_add.png differ diff --git a/www/img/icons/server_chart.png b/www/img/icons/server_chart.png new file mode 100755 index 0000000..1128d3f Binary files /dev/null and b/www/img/icons/server_chart.png differ diff --git a/www/img/icons/server_compressed.png b/www/img/icons/server_compressed.png new file mode 100755 index 0000000..bf49fad Binary files /dev/null and b/www/img/icons/server_compressed.png differ diff --git a/www/img/icons/server_connect.png b/www/img/icons/server_connect.png new file mode 100755 index 0000000..49b2691 Binary files /dev/null and b/www/img/icons/server_connect.png differ diff --git a/www/img/icons/server_database.png b/www/img/icons/server_database.png new file mode 100755 index 0000000..b24e826 Binary files /dev/null and b/www/img/icons/server_database.png differ diff --git a/www/img/icons/server_delete.png b/www/img/icons/server_delete.png new file mode 100755 index 0000000..61e740f Binary files /dev/null and b/www/img/icons/server_delete.png differ diff --git a/www/img/icons/server_edit.png b/www/img/icons/server_edit.png new file mode 100755 index 0000000..dc76253 Binary files /dev/null and b/www/img/icons/server_edit.png differ diff --git a/www/img/icons/server_error.png b/www/img/icons/server_error.png new file mode 100755 index 0000000..f640256 Binary files /dev/null and b/www/img/icons/server_error.png differ diff --git a/www/img/icons/server_go.png b/www/img/icons/server_go.png new file mode 100755 index 0000000..540c8e2 Binary files /dev/null and b/www/img/icons/server_go.png differ diff --git a/www/img/icons/server_key.png b/www/img/icons/server_key.png new file mode 100755 index 0000000..ecd5174 Binary files /dev/null and b/www/img/icons/server_key.png differ diff --git a/www/img/icons/server_lightning.png b/www/img/icons/server_lightning.png new file mode 100755 index 0000000..b0f4e46 Binary files /dev/null and b/www/img/icons/server_lightning.png differ diff --git a/www/img/icons/server_link.png b/www/img/icons/server_link.png new file mode 100755 index 0000000..e8821df Binary files /dev/null and b/www/img/icons/server_link.png differ diff --git a/www/img/icons/server_uncompressed.png b/www/img/icons/server_uncompressed.png new file mode 100755 index 0000000..86e8325 Binary files /dev/null and b/www/img/icons/server_uncompressed.png differ diff --git a/www/img/icons/shading.png b/www/img/icons/shading.png new file mode 100755 index 0000000..09275f9 Binary files /dev/null and b/www/img/icons/shading.png differ diff --git a/www/img/icons/shape_align_bottom.png b/www/img/icons/shape_align_bottom.png new file mode 100755 index 0000000..55d2694 Binary files /dev/null and b/www/img/icons/shape_align_bottom.png differ diff --git a/www/img/icons/shape_align_center.png b/www/img/icons/shape_align_center.png new file mode 100755 index 0000000..efe9a98 Binary files /dev/null and b/www/img/icons/shape_align_center.png differ diff --git a/www/img/icons/shape_align_left.png b/www/img/icons/shape_align_left.png new file mode 100755 index 0000000..aaedc41 Binary files /dev/null and b/www/img/icons/shape_align_left.png differ diff --git a/www/img/icons/shape_align_middle.png b/www/img/icons/shape_align_middle.png new file mode 100755 index 0000000..d350dd8 Binary files /dev/null and b/www/img/icons/shape_align_middle.png differ diff --git a/www/img/icons/shape_align_right.png b/www/img/icons/shape_align_right.png new file mode 100755 index 0000000..ff556b6 Binary files /dev/null and b/www/img/icons/shape_align_right.png differ diff --git a/www/img/icons/shape_align_top.png b/www/img/icons/shape_align_top.png new file mode 100755 index 0000000..1181b43 Binary files /dev/null and b/www/img/icons/shape_align_top.png differ diff --git a/www/img/icons/shape_flip_horizontal.png b/www/img/icons/shape_flip_horizontal.png new file mode 100755 index 0000000..8667c81 Binary files /dev/null and b/www/img/icons/shape_flip_horizontal.png differ diff --git a/www/img/icons/shape_flip_vertical.png b/www/img/icons/shape_flip_vertical.png new file mode 100755 index 0000000..0bd66d1 Binary files /dev/null and b/www/img/icons/shape_flip_vertical.png differ diff --git a/www/img/icons/shape_group.png b/www/img/icons/shape_group.png new file mode 100755 index 0000000..bb2ff51 Binary files /dev/null and b/www/img/icons/shape_group.png differ diff --git a/www/img/icons/shape_handles.png b/www/img/icons/shape_handles.png new file mode 100755 index 0000000..ce27fe3 Binary files /dev/null and b/www/img/icons/shape_handles.png differ diff --git a/www/img/icons/shape_move_back.png b/www/img/icons/shape_move_back.png new file mode 100755 index 0000000..a216ffd Binary files /dev/null and b/www/img/icons/shape_move_back.png differ diff --git a/www/img/icons/shape_move_backwards.png b/www/img/icons/shape_move_backwards.png new file mode 100755 index 0000000..ee3f9b2 Binary files /dev/null and b/www/img/icons/shape_move_backwards.png differ diff --git a/www/img/icons/shape_move_forwards.png b/www/img/icons/shape_move_forwards.png new file mode 100755 index 0000000..cfe4493 Binary files /dev/null and b/www/img/icons/shape_move_forwards.png differ diff --git a/www/img/icons/shape_move_front.png b/www/img/icons/shape_move_front.png new file mode 100755 index 0000000..b4a4e3b Binary files /dev/null and b/www/img/icons/shape_move_front.png differ diff --git a/www/img/icons/shape_rotate_anticlockwise.png b/www/img/icons/shape_rotate_anticlockwise.png new file mode 100755 index 0000000..07a3020 Binary files /dev/null and b/www/img/icons/shape_rotate_anticlockwise.png differ diff --git a/www/img/icons/shape_rotate_clockwise.png b/www/img/icons/shape_rotate_clockwise.png new file mode 100755 index 0000000..b99db7d Binary files /dev/null and b/www/img/icons/shape_rotate_clockwise.png differ diff --git a/www/img/icons/shape_square.png b/www/img/icons/shape_square.png new file mode 100755 index 0000000..33af046 Binary files /dev/null and b/www/img/icons/shape_square.png differ diff --git a/www/img/icons/shape_square_add.png b/www/img/icons/shape_square_add.png new file mode 100755 index 0000000..31edfce Binary files /dev/null and b/www/img/icons/shape_square_add.png differ diff --git a/www/img/icons/shape_square_delete.png b/www/img/icons/shape_square_delete.png new file mode 100755 index 0000000..ede912d Binary files /dev/null and b/www/img/icons/shape_square_delete.png differ diff --git a/www/img/icons/shape_square_edit.png b/www/img/icons/shape_square_edit.png new file mode 100755 index 0000000..d28dc6b Binary files /dev/null and b/www/img/icons/shape_square_edit.png differ diff --git a/www/img/icons/shape_square_error.png b/www/img/icons/shape_square_error.png new file mode 100755 index 0000000..0d0dcfa Binary files /dev/null and b/www/img/icons/shape_square_error.png differ diff --git a/www/img/icons/shape_square_go.png b/www/img/icons/shape_square_go.png new file mode 100755 index 0000000..5a2ad90 Binary files /dev/null and b/www/img/icons/shape_square_go.png differ diff --git a/www/img/icons/shape_square_key.png b/www/img/icons/shape_square_key.png new file mode 100755 index 0000000..c34b982 Binary files /dev/null and b/www/img/icons/shape_square_key.png differ diff --git a/www/img/icons/shape_square_link.png b/www/img/icons/shape_square_link.png new file mode 100755 index 0000000..b885fcc Binary files /dev/null and b/www/img/icons/shape_square_link.png differ diff --git a/www/img/icons/shape_ungroup.png b/www/img/icons/shape_ungroup.png new file mode 100755 index 0000000..3a6f369 Binary files /dev/null and b/www/img/icons/shape_ungroup.png differ diff --git a/www/img/icons/shield.png b/www/img/icons/shield.png new file mode 100755 index 0000000..3cb4e25 Binary files /dev/null and b/www/img/icons/shield.png differ diff --git a/www/img/icons/shield_add.png b/www/img/icons/shield_add.png new file mode 100755 index 0000000..e20a1b4 Binary files /dev/null and b/www/img/icons/shield_add.png differ diff --git a/www/img/icons/shield_delete.png b/www/img/icons/shield_delete.png new file mode 100755 index 0000000..22823a7 Binary files /dev/null and b/www/img/icons/shield_delete.png differ diff --git a/www/img/icons/shield_go.png b/www/img/icons/shield_go.png new file mode 100755 index 0000000..e9bd852 Binary files /dev/null and b/www/img/icons/shield_go.png differ diff --git a/www/img/icons/sitemap.png b/www/img/icons/sitemap.png new file mode 100755 index 0000000..ca779f3 Binary files /dev/null and b/www/img/icons/sitemap.png differ diff --git a/www/img/icons/sitemap_color.png b/www/img/icons/sitemap_color.png new file mode 100755 index 0000000..c64582b Binary files /dev/null and b/www/img/icons/sitemap_color.png differ diff --git a/www/img/icons/sound.png b/www/img/icons/sound.png new file mode 100755 index 0000000..6056d23 Binary files /dev/null and b/www/img/icons/sound.png differ diff --git a/www/img/icons/sound_add.png b/www/img/icons/sound_add.png new file mode 100755 index 0000000..965c503 Binary files /dev/null and b/www/img/icons/sound_add.png differ diff --git a/www/img/icons/sound_delete.png b/www/img/icons/sound_delete.png new file mode 100755 index 0000000..ab9577a Binary files /dev/null and b/www/img/icons/sound_delete.png differ diff --git a/www/img/icons/sound_low.png b/www/img/icons/sound_low.png new file mode 100755 index 0000000..4d91863 Binary files /dev/null and b/www/img/icons/sound_low.png differ diff --git a/www/img/icons/sound_mute.png b/www/img/icons/sound_mute.png new file mode 100755 index 0000000..b652d2a Binary files /dev/null and b/www/img/icons/sound_mute.png differ diff --git a/www/img/icons/sound_none.png b/www/img/icons/sound_none.png new file mode 100755 index 0000000..b497ebd Binary files /dev/null and b/www/img/icons/sound_none.png differ diff --git a/www/img/icons/spellcheck.png b/www/img/icons/spellcheck.png new file mode 100755 index 0000000..ebc632d Binary files /dev/null and b/www/img/icons/spellcheck.png differ diff --git a/www/img/icons/sport_8ball.png b/www/img/icons/sport_8ball.png new file mode 100755 index 0000000..4f627b7 Binary files /dev/null and b/www/img/icons/sport_8ball.png differ diff --git a/www/img/icons/sport_basketball.png b/www/img/icons/sport_basketball.png new file mode 100755 index 0000000..f7a000b Binary files /dev/null and b/www/img/icons/sport_basketball.png differ diff --git a/www/img/icons/sport_football.png b/www/img/icons/sport_football.png new file mode 100755 index 0000000..199f0f7 Binary files /dev/null and b/www/img/icons/sport_football.png differ diff --git a/www/img/icons/sport_golf.png b/www/img/icons/sport_golf.png new file mode 100755 index 0000000..e21fa44 Binary files /dev/null and b/www/img/icons/sport_golf.png differ diff --git a/www/img/icons/sport_raquet.png b/www/img/icons/sport_raquet.png new file mode 100755 index 0000000..f5e0f0c Binary files /dev/null and b/www/img/icons/sport_raquet.png differ diff --git a/www/img/icons/sport_shuttlecock.png b/www/img/icons/sport_shuttlecock.png new file mode 100755 index 0000000..917287f Binary files /dev/null and b/www/img/icons/sport_shuttlecock.png differ diff --git a/www/img/icons/sport_soccer.png b/www/img/icons/sport_soccer.png new file mode 100755 index 0000000..3eb1828 Binary files /dev/null and b/www/img/icons/sport_soccer.png differ diff --git a/www/img/icons/sport_tennis.png b/www/img/icons/sport_tennis.png new file mode 100755 index 0000000..e88a6ef Binary files /dev/null and b/www/img/icons/sport_tennis.png differ diff --git a/www/img/icons/star.png b/www/img/icons/star.png new file mode 100755 index 0000000..b88c857 Binary files /dev/null and b/www/img/icons/star.png differ diff --git a/www/img/icons/status_away.png b/www/img/icons/status_away.png new file mode 100755 index 0000000..70bcbcc Binary files /dev/null and b/www/img/icons/status_away.png differ diff --git a/www/img/icons/status_busy.png b/www/img/icons/status_busy.png new file mode 100755 index 0000000..987c806 Binary files /dev/null and b/www/img/icons/status_busy.png differ diff --git a/www/img/icons/status_offline.png b/www/img/icons/status_offline.png new file mode 100755 index 0000000..a88261a Binary files /dev/null and b/www/img/icons/status_offline.png differ diff --git a/www/img/icons/status_online.png b/www/img/icons/status_online.png new file mode 100755 index 0000000..947bd4b Binary files /dev/null and b/www/img/icons/status_online.png differ diff --git a/www/img/icons/stop.png b/www/img/icons/stop.png new file mode 100755 index 0000000..0cfd585 Binary files /dev/null and b/www/img/icons/stop.png differ diff --git a/www/img/icons/style.png b/www/img/icons/style.png new file mode 100755 index 0000000..81e41de Binary files /dev/null and b/www/img/icons/style.png differ diff --git a/www/img/icons/style_add.png b/www/img/icons/style_add.png new file mode 100755 index 0000000..e0369c6 Binary files /dev/null and b/www/img/icons/style_add.png differ diff --git a/www/img/icons/style_delete.png b/www/img/icons/style_delete.png new file mode 100755 index 0000000..640f187 Binary files /dev/null and b/www/img/icons/style_delete.png differ diff --git a/www/img/icons/style_edit.png b/www/img/icons/style_edit.png new file mode 100755 index 0000000..25bb5b6 Binary files /dev/null and b/www/img/icons/style_edit.png differ diff --git a/www/img/icons/style_go.png b/www/img/icons/style_go.png new file mode 100755 index 0000000..25d6181 Binary files /dev/null and b/www/img/icons/style_go.png differ diff --git a/www/img/icons/sum.png b/www/img/icons/sum.png new file mode 100755 index 0000000..fd7b32e Binary files /dev/null and b/www/img/icons/sum.png differ diff --git a/www/img/icons/tab.png b/www/img/icons/tab.png new file mode 100755 index 0000000..3d8207f Binary files /dev/null and b/www/img/icons/tab.png differ diff --git a/www/img/icons/tab_add.png b/www/img/icons/tab_add.png new file mode 100755 index 0000000..d3b9936 Binary files /dev/null and b/www/img/icons/tab_add.png differ diff --git a/www/img/icons/tab_delete.png b/www/img/icons/tab_delete.png new file mode 100755 index 0000000..100da2f Binary files /dev/null and b/www/img/icons/tab_delete.png differ diff --git a/www/img/icons/tab_edit.png b/www/img/icons/tab_edit.png new file mode 100755 index 0000000..4c09c0f Binary files /dev/null and b/www/img/icons/tab_edit.png differ diff --git a/www/img/icons/tab_go.png b/www/img/icons/tab_go.png new file mode 100755 index 0000000..844ce04 Binary files /dev/null and b/www/img/icons/tab_go.png differ diff --git a/www/img/icons/table.png b/www/img/icons/table.png new file mode 100755 index 0000000..abcd936 Binary files /dev/null and b/www/img/icons/table.png differ diff --git a/www/img/icons/table_add.png b/www/img/icons/table_add.png new file mode 100755 index 0000000..2a3e5c4 Binary files /dev/null and b/www/img/icons/table_add.png differ diff --git a/www/img/icons/table_delete.png b/www/img/icons/table_delete.png new file mode 100755 index 0000000..b85916d Binary files /dev/null and b/www/img/icons/table_delete.png differ diff --git a/www/img/icons/table_edit.png b/www/img/icons/table_edit.png new file mode 100755 index 0000000..bfcb024 Binary files /dev/null and b/www/img/icons/table_edit.png differ diff --git a/www/img/icons/table_error.png b/www/img/icons/table_error.png new file mode 100755 index 0000000..589e92b Binary files /dev/null and b/www/img/icons/table_error.png differ diff --git a/www/img/icons/table_gear.png b/www/img/icons/table_gear.png new file mode 100755 index 0000000..cfc2702 Binary files /dev/null and b/www/img/icons/table_gear.png differ diff --git a/www/img/icons/table_go.png b/www/img/icons/table_go.png new file mode 100755 index 0000000..0528dfa Binary files /dev/null and b/www/img/icons/table_go.png differ diff --git a/www/img/icons/table_key.png b/www/img/icons/table_key.png new file mode 100755 index 0000000..34e23e2 Binary files /dev/null and b/www/img/icons/table_key.png differ diff --git a/www/img/icons/table_lightning.png b/www/img/icons/table_lightning.png new file mode 100755 index 0000000..612612b Binary files /dev/null and b/www/img/icons/table_lightning.png differ diff --git a/www/img/icons/table_link.png b/www/img/icons/table_link.png new file mode 100755 index 0000000..decac8a Binary files /dev/null and b/www/img/icons/table_link.png differ diff --git a/www/img/icons/table_multiple.png b/www/img/icons/table_multiple.png new file mode 100755 index 0000000..d76448e Binary files /dev/null and b/www/img/icons/table_multiple.png differ diff --git a/www/img/icons/table_refresh.png b/www/img/icons/table_refresh.png new file mode 100755 index 0000000..ab92010 Binary files /dev/null and b/www/img/icons/table_refresh.png differ diff --git a/www/img/icons/table_relationship.png b/www/img/icons/table_relationship.png new file mode 100755 index 0000000..28b8505 Binary files /dev/null and b/www/img/icons/table_relationship.png differ diff --git a/www/img/icons/table_row_delete.png b/www/img/icons/table_row_delete.png new file mode 100755 index 0000000..54c6969 Binary files /dev/null and b/www/img/icons/table_row_delete.png differ diff --git a/www/img/icons/table_row_insert.png b/www/img/icons/table_row_insert.png new file mode 100755 index 0000000..ff5925e Binary files /dev/null and b/www/img/icons/table_row_insert.png differ diff --git a/www/img/icons/table_save.png b/www/img/icons/table_save.png new file mode 100755 index 0000000..25b74d1 Binary files /dev/null and b/www/img/icons/table_save.png differ diff --git a/www/img/icons/table_sort.png b/www/img/icons/table_sort.png new file mode 100755 index 0000000..ed6785a Binary files /dev/null and b/www/img/icons/table_sort.png differ diff --git a/www/img/icons/tag.png b/www/img/icons/tag.png new file mode 100755 index 0000000..e093032 Binary files /dev/null and b/www/img/icons/tag.png differ diff --git a/www/img/icons/tag_blue.png b/www/img/icons/tag_blue.png new file mode 100755 index 0000000..9757fc6 Binary files /dev/null and b/www/img/icons/tag_blue.png differ diff --git a/www/img/icons/tag_blue_add.png b/www/img/icons/tag_blue_add.png new file mode 100755 index 0000000..f135248 Binary files /dev/null and b/www/img/icons/tag_blue_add.png differ diff --git a/www/img/icons/tag_blue_delete.png b/www/img/icons/tag_blue_delete.png new file mode 100755 index 0000000..9fbae67 Binary files /dev/null and b/www/img/icons/tag_blue_delete.png differ diff --git a/www/img/icons/tag_blue_edit.png b/www/img/icons/tag_blue_edit.png new file mode 100755 index 0000000..2a9f626 Binary files /dev/null and b/www/img/icons/tag_blue_edit.png differ diff --git a/www/img/icons/tag_green.png b/www/img/icons/tag_green.png new file mode 100755 index 0000000..83ec984 Binary files /dev/null and b/www/img/icons/tag_green.png differ diff --git a/www/img/icons/tag_orange.png b/www/img/icons/tag_orange.png new file mode 100755 index 0000000..454a59f Binary files /dev/null and b/www/img/icons/tag_orange.png differ diff --git a/www/img/icons/tag_pink.png b/www/img/icons/tag_pink.png new file mode 100755 index 0000000..76e2296 Binary files /dev/null and b/www/img/icons/tag_pink.png differ diff --git a/www/img/icons/tag_purple.png b/www/img/icons/tag_purple.png new file mode 100755 index 0000000..ebaf0e8 Binary files /dev/null and b/www/img/icons/tag_purple.png differ diff --git a/www/img/icons/tag_red.png b/www/img/icons/tag_red.png new file mode 100755 index 0000000..6ebb37d Binary files /dev/null and b/www/img/icons/tag_red.png differ diff --git a/www/img/icons/tag_yellow.png b/www/img/icons/tag_yellow.png new file mode 100755 index 0000000..83d1292 Binary files /dev/null and b/www/img/icons/tag_yellow.png differ diff --git a/www/img/icons/telephone.png b/www/img/icons/telephone.png new file mode 100755 index 0000000..cecc436 Binary files /dev/null and b/www/img/icons/telephone.png differ diff --git a/www/img/icons/telephone_add.png b/www/img/icons/telephone_add.png new file mode 100755 index 0000000..5591cfc Binary files /dev/null and b/www/img/icons/telephone_add.png differ diff --git a/www/img/icons/telephone_delete.png b/www/img/icons/telephone_delete.png new file mode 100755 index 0000000..0013268 Binary files /dev/null and b/www/img/icons/telephone_delete.png differ diff --git a/www/img/icons/telephone_edit.png b/www/img/icons/telephone_edit.png new file mode 100755 index 0000000..bcf6d7e Binary files /dev/null and b/www/img/icons/telephone_edit.png differ diff --git a/www/img/icons/telephone_error.png b/www/img/icons/telephone_error.png new file mode 100755 index 0000000..d3ec3a1 Binary files /dev/null and b/www/img/icons/telephone_error.png differ diff --git a/www/img/icons/telephone_go.png b/www/img/icons/telephone_go.png new file mode 100755 index 0000000..395c8fb Binary files /dev/null and b/www/img/icons/telephone_go.png differ diff --git a/www/img/icons/telephone_key.png b/www/img/icons/telephone_key.png new file mode 100755 index 0000000..cef5dec Binary files /dev/null and b/www/img/icons/telephone_key.png differ diff --git a/www/img/icons/telephone_link.png b/www/img/icons/telephone_link.png new file mode 100755 index 0000000..ef1ee5d Binary files /dev/null and b/www/img/icons/telephone_link.png differ diff --git a/www/img/icons/television.png b/www/img/icons/television.png new file mode 100755 index 0000000..1738a4f Binary files /dev/null and b/www/img/icons/television.png differ diff --git a/www/img/icons/television_add.png b/www/img/icons/television_add.png new file mode 100755 index 0000000..2baaad9 Binary files /dev/null and b/www/img/icons/television_add.png differ diff --git a/www/img/icons/television_delete.png b/www/img/icons/television_delete.png new file mode 100755 index 0000000..b9a5860 Binary files /dev/null and b/www/img/icons/television_delete.png differ diff --git a/www/img/icons/text_align_center.png b/www/img/icons/text_align_center.png new file mode 100755 index 0000000..57beb38 Binary files /dev/null and b/www/img/icons/text_align_center.png differ diff --git a/www/img/icons/text_align_justify.png b/www/img/icons/text_align_justify.png new file mode 100755 index 0000000..2fbdd69 Binary files /dev/null and b/www/img/icons/text_align_justify.png differ diff --git a/www/img/icons/text_align_left.png b/www/img/icons/text_align_left.png new file mode 100755 index 0000000..6c8fcc1 Binary files /dev/null and b/www/img/icons/text_align_left.png differ diff --git a/www/img/icons/text_align_right.png b/www/img/icons/text_align_right.png new file mode 100755 index 0000000..a150257 Binary files /dev/null and b/www/img/icons/text_align_right.png differ diff --git a/www/img/icons/text_allcaps.png b/www/img/icons/text_allcaps.png new file mode 100755 index 0000000..280fd44 Binary files /dev/null and b/www/img/icons/text_allcaps.png differ diff --git a/www/img/icons/text_bold.png b/www/img/icons/text_bold.png new file mode 100755 index 0000000..889ae80 Binary files /dev/null and b/www/img/icons/text_bold.png differ diff --git a/www/img/icons/text_columns.png b/www/img/icons/text_columns.png new file mode 100755 index 0000000..97b2e03 Binary files /dev/null and b/www/img/icons/text_columns.png differ diff --git a/www/img/icons/text_dropcaps.png b/www/img/icons/text_dropcaps.png new file mode 100755 index 0000000..dd65786 Binary files /dev/null and b/www/img/icons/text_dropcaps.png differ diff --git a/www/img/icons/text_heading_1.png b/www/img/icons/text_heading_1.png new file mode 100755 index 0000000..9c122e9 Binary files /dev/null and b/www/img/icons/text_heading_1.png differ diff --git a/www/img/icons/text_heading_2.png b/www/img/icons/text_heading_2.png new file mode 100755 index 0000000..fbd8765 Binary files /dev/null and b/www/img/icons/text_heading_2.png differ diff --git a/www/img/icons/text_heading_3.png b/www/img/icons/text_heading_3.png new file mode 100755 index 0000000..c7836cf Binary files /dev/null and b/www/img/icons/text_heading_3.png differ diff --git a/www/img/icons/text_heading_4.png b/www/img/icons/text_heading_4.png new file mode 100755 index 0000000..4e929ea Binary files /dev/null and b/www/img/icons/text_heading_4.png differ diff --git a/www/img/icons/text_heading_5.png b/www/img/icons/text_heading_5.png new file mode 100755 index 0000000..30cabeb Binary files /dev/null and b/www/img/icons/text_heading_5.png differ diff --git a/www/img/icons/text_heading_6.png b/www/img/icons/text_heading_6.png new file mode 100755 index 0000000..058170a Binary files /dev/null and b/www/img/icons/text_heading_6.png differ diff --git a/www/img/icons/text_horizontalrule.png b/www/img/icons/text_horizontalrule.png new file mode 100755 index 0000000..8dd1da1 Binary files /dev/null and b/www/img/icons/text_horizontalrule.png differ diff --git a/www/img/icons/text_indent.png b/www/img/icons/text_indent.png new file mode 100755 index 0000000..9364532 Binary files /dev/null and b/www/img/icons/text_indent.png differ diff --git a/www/img/icons/text_indent_remove.png b/www/img/icons/text_indent_remove.png new file mode 100755 index 0000000..1651b07 Binary files /dev/null and b/www/img/icons/text_indent_remove.png differ diff --git a/www/img/icons/text_italic.png b/www/img/icons/text_italic.png new file mode 100755 index 0000000..8482ac8 Binary files /dev/null and b/www/img/icons/text_italic.png differ diff --git a/www/img/icons/text_kerning.png b/www/img/icons/text_kerning.png new file mode 100755 index 0000000..377def6 Binary files /dev/null and b/www/img/icons/text_kerning.png differ diff --git a/www/img/icons/text_letter_omega.png b/www/img/icons/text_letter_omega.png new file mode 100755 index 0000000..5075ec6 Binary files /dev/null and b/www/img/icons/text_letter_omega.png differ diff --git a/www/img/icons/text_letterspacing.png b/www/img/icons/text_letterspacing.png new file mode 100755 index 0000000..41390f5 Binary files /dev/null and b/www/img/icons/text_letterspacing.png differ diff --git a/www/img/icons/text_linespacing.png b/www/img/icons/text_linespacing.png new file mode 100755 index 0000000..1a91cbd Binary files /dev/null and b/www/img/icons/text_linespacing.png differ diff --git a/www/img/icons/text_list_bullets.png b/www/img/icons/text_list_bullets.png new file mode 100755 index 0000000..4a8672b Binary files /dev/null and b/www/img/icons/text_list_bullets.png differ diff --git a/www/img/icons/text_list_numbers.png b/www/img/icons/text_list_numbers.png new file mode 100755 index 0000000..33b0b8d Binary files /dev/null and b/www/img/icons/text_list_numbers.png differ diff --git a/www/img/icons/text_lowercase.png b/www/img/icons/text_lowercase.png new file mode 100755 index 0000000..382a102 Binary files /dev/null and b/www/img/icons/text_lowercase.png differ diff --git a/www/img/icons/text_padding_bottom.png b/www/img/icons/text_padding_bottom.png new file mode 100755 index 0000000..4880c43 Binary files /dev/null and b/www/img/icons/text_padding_bottom.png differ diff --git a/www/img/icons/text_padding_left.png b/www/img/icons/text_padding_left.png new file mode 100755 index 0000000..b55482e Binary files /dev/null and b/www/img/icons/text_padding_left.png differ diff --git a/www/img/icons/text_padding_right.png b/www/img/icons/text_padding_right.png new file mode 100755 index 0000000..106edae Binary files /dev/null and b/www/img/icons/text_padding_right.png differ diff --git a/www/img/icons/text_padding_top.png b/www/img/icons/text_padding_top.png new file mode 100755 index 0000000..c5c45b2 Binary files /dev/null and b/www/img/icons/text_padding_top.png differ diff --git a/www/img/icons/text_replace.png b/www/img/icons/text_replace.png new file mode 100755 index 0000000..877f82f Binary files /dev/null and b/www/img/icons/text_replace.png differ diff --git a/www/img/icons/text_signature.png b/www/img/icons/text_signature.png new file mode 100755 index 0000000..c72fd80 Binary files /dev/null and b/www/img/icons/text_signature.png differ diff --git a/www/img/icons/text_smallcaps.png b/www/img/icons/text_smallcaps.png new file mode 100755 index 0000000..5b98a6e Binary files /dev/null and b/www/img/icons/text_smallcaps.png differ diff --git a/www/img/icons/text_strikethrough.png b/www/img/icons/text_strikethrough.png new file mode 100755 index 0000000..612058a Binary files /dev/null and b/www/img/icons/text_strikethrough.png differ diff --git a/www/img/icons/text_subscript.png b/www/img/icons/text_subscript.png new file mode 100755 index 0000000..1a2b010 Binary files /dev/null and b/www/img/icons/text_subscript.png differ diff --git a/www/img/icons/text_superscript.png b/www/img/icons/text_superscript.png new file mode 100755 index 0000000..2fb2a7c Binary files /dev/null and b/www/img/icons/text_superscript.png differ diff --git a/www/img/icons/text_underline.png b/www/img/icons/text_underline.png new file mode 100755 index 0000000..90d0df2 Binary files /dev/null and b/www/img/icons/text_underline.png differ diff --git a/www/img/icons/text_uppercase.png b/www/img/icons/text_uppercase.png new file mode 100755 index 0000000..8dcc2db Binary files /dev/null and b/www/img/icons/text_uppercase.png differ diff --git a/www/img/icons/textfield.png b/www/img/icons/textfield.png new file mode 100755 index 0000000..d37e730 Binary files /dev/null and b/www/img/icons/textfield.png differ diff --git a/www/img/icons/textfield_add.png b/www/img/icons/textfield_add.png new file mode 100755 index 0000000..204de72 Binary files /dev/null and b/www/img/icons/textfield_add.png differ diff --git a/www/img/icons/textfield_delete.png b/www/img/icons/textfield_delete.png new file mode 100755 index 0000000..c7bd58b Binary files /dev/null and b/www/img/icons/textfield_delete.png differ diff --git a/www/img/icons/textfield_key.png b/www/img/icons/textfield_key.png new file mode 100755 index 0000000..a9d5e4f Binary files /dev/null and b/www/img/icons/textfield_key.png differ diff --git a/www/img/icons/textfield_rename.png b/www/img/icons/textfield_rename.png new file mode 100755 index 0000000..4e3688e Binary files /dev/null and b/www/img/icons/textfield_rename.png differ diff --git a/www/img/icons/thumb_down.png b/www/img/icons/thumb_down.png new file mode 100755 index 0000000..3c832d4 Binary files /dev/null and b/www/img/icons/thumb_down.png differ diff --git a/www/img/icons/thumb_up.png b/www/img/icons/thumb_up.png new file mode 100755 index 0000000..2bd16cc Binary files /dev/null and b/www/img/icons/thumb_up.png differ diff --git a/www/img/icons/tick.png b/www/img/icons/tick.png new file mode 100755 index 0000000..a9925a0 Binary files /dev/null and b/www/img/icons/tick.png differ diff --git a/www/img/icons/time.png b/www/img/icons/time.png new file mode 100755 index 0000000..911da3f Binary files /dev/null and b/www/img/icons/time.png differ diff --git a/www/img/icons/time_add.png b/www/img/icons/time_add.png new file mode 100755 index 0000000..dcc45cb Binary files /dev/null and b/www/img/icons/time_add.png differ diff --git a/www/img/icons/time_delete.png b/www/img/icons/time_delete.png new file mode 100755 index 0000000..5bf8313 Binary files /dev/null and b/www/img/icons/time_delete.png differ diff --git a/www/img/icons/time_go.png b/www/img/icons/time_go.png new file mode 100755 index 0000000..d451ee0 Binary files /dev/null and b/www/img/icons/time_go.png differ diff --git a/www/img/icons/timeline_marker.png b/www/img/icons/timeline_marker.png new file mode 100755 index 0000000..a3fbddf Binary files /dev/null and b/www/img/icons/timeline_marker.png differ diff --git a/www/img/icons/transmit.png b/www/img/icons/transmit.png new file mode 100755 index 0000000..f54bf73 Binary files /dev/null and b/www/img/icons/transmit.png differ diff --git a/www/img/icons/transmit_add.png b/www/img/icons/transmit_add.png new file mode 100755 index 0000000..b7fd4e6 Binary files /dev/null and b/www/img/icons/transmit_add.png differ diff --git a/www/img/icons/transmit_blue.png b/www/img/icons/transmit_blue.png new file mode 100755 index 0000000..7b1142f Binary files /dev/null and b/www/img/icons/transmit_blue.png differ diff --git a/www/img/icons/transmit_delete.png b/www/img/icons/transmit_delete.png new file mode 100755 index 0000000..3d72be2 Binary files /dev/null and b/www/img/icons/transmit_delete.png differ diff --git a/www/img/icons/transmit_edit.png b/www/img/icons/transmit_edit.png new file mode 100755 index 0000000..eb9a3dd Binary files /dev/null and b/www/img/icons/transmit_edit.png differ diff --git a/www/img/icons/transmit_error.png b/www/img/icons/transmit_error.png new file mode 100755 index 0000000..fd1d449 Binary files /dev/null and b/www/img/icons/transmit_error.png differ diff --git a/www/img/icons/transmit_go.png b/www/img/icons/transmit_go.png new file mode 100755 index 0000000..10137e5 Binary files /dev/null and b/www/img/icons/transmit_go.png differ diff --git a/www/img/icons/tux.png b/www/img/icons/tux.png new file mode 100755 index 0000000..bbefe2e Binary files /dev/null and b/www/img/icons/tux.png differ diff --git a/www/img/icons/user.png b/www/img/icons/user.png new file mode 100755 index 0000000..79f35cc Binary files /dev/null and b/www/img/icons/user.png differ diff --git a/www/img/icons/user_add.png b/www/img/icons/user_add.png new file mode 100755 index 0000000..deae99b Binary files /dev/null and b/www/img/icons/user_add.png differ diff --git a/www/img/icons/user_comment.png b/www/img/icons/user_comment.png new file mode 100755 index 0000000..e54ebeb Binary files /dev/null and b/www/img/icons/user_comment.png differ diff --git a/www/img/icons/user_delete.png b/www/img/icons/user_delete.png new file mode 100755 index 0000000..acbb563 Binary files /dev/null and b/www/img/icons/user_delete.png differ diff --git a/www/img/icons/user_edit.png b/www/img/icons/user_edit.png new file mode 100755 index 0000000..c1974cd Binary files /dev/null and b/www/img/icons/user_edit.png differ diff --git a/www/img/icons/user_female.png b/www/img/icons/user_female.png new file mode 100755 index 0000000..7c71de0 Binary files /dev/null and b/www/img/icons/user_female.png differ diff --git a/www/img/icons/user_go.png b/www/img/icons/user_go.png new file mode 100755 index 0000000..0468cf0 Binary files /dev/null and b/www/img/icons/user_go.png differ diff --git a/www/img/icons/user_gray.png b/www/img/icons/user_gray.png new file mode 100755 index 0000000..8fd539e Binary files /dev/null and b/www/img/icons/user_gray.png differ diff --git a/www/img/icons/user_green.png b/www/img/icons/user_green.png new file mode 100755 index 0000000..30383c2 Binary files /dev/null and b/www/img/icons/user_green.png differ diff --git a/www/img/icons/user_orange.png b/www/img/icons/user_orange.png new file mode 100755 index 0000000..b818127 Binary files /dev/null and b/www/img/icons/user_orange.png differ diff --git a/www/img/icons/user_red.png b/www/img/icons/user_red.png new file mode 100755 index 0000000..c6f66e8 Binary files /dev/null and b/www/img/icons/user_red.png differ diff --git a/www/img/icons/user_suit.png b/www/img/icons/user_suit.png new file mode 100755 index 0000000..b3454e1 Binary files /dev/null and b/www/img/icons/user_suit.png differ diff --git a/www/img/icons/vcard.png b/www/img/icons/vcard.png new file mode 100755 index 0000000..c02f315 Binary files /dev/null and b/www/img/icons/vcard.png differ diff --git a/www/img/icons/vcard_add.png b/www/img/icons/vcard_add.png new file mode 100755 index 0000000..2a68453 Binary files /dev/null and b/www/img/icons/vcard_add.png differ diff --git a/www/img/icons/vcard_delete.png b/www/img/icons/vcard_delete.png new file mode 100755 index 0000000..b194b97 Binary files /dev/null and b/www/img/icons/vcard_delete.png differ diff --git a/www/img/icons/vcard_edit.png b/www/img/icons/vcard_edit.png new file mode 100755 index 0000000..ab0f6e7 Binary files /dev/null and b/www/img/icons/vcard_edit.png differ diff --git a/www/img/icons/vector.png b/www/img/icons/vector.png new file mode 100755 index 0000000..a1291c2 Binary files /dev/null and b/www/img/icons/vector.png differ diff --git a/www/img/icons/vector_add.png b/www/img/icons/vector_add.png new file mode 100755 index 0000000..988770f Binary files /dev/null and b/www/img/icons/vector_add.png differ diff --git a/www/img/icons/vector_delete.png b/www/img/icons/vector_delete.png new file mode 100755 index 0000000..ca139e0 Binary files /dev/null and b/www/img/icons/vector_delete.png differ diff --git a/www/img/icons/wand.png b/www/img/icons/wand.png new file mode 100755 index 0000000..44ccbf8 Binary files /dev/null and b/www/img/icons/wand.png differ diff --git a/www/img/icons/weather_clouds.png b/www/img/icons/weather_clouds.png new file mode 100755 index 0000000..3f73eaa Binary files /dev/null and b/www/img/icons/weather_clouds.png differ diff --git a/www/img/icons/weather_cloudy.png b/www/img/icons/weather_cloudy.png new file mode 100755 index 0000000..5856e1d Binary files /dev/null and b/www/img/icons/weather_cloudy.png differ diff --git a/www/img/icons/weather_lightning.png b/www/img/icons/weather_lightning.png new file mode 100755 index 0000000..1d42b36 Binary files /dev/null and b/www/img/icons/weather_lightning.png differ diff --git a/www/img/icons/weather_rain.png b/www/img/icons/weather_rain.png new file mode 100755 index 0000000..cb3d54d Binary files /dev/null and b/www/img/icons/weather_rain.png differ diff --git a/www/img/icons/weather_snow.png b/www/img/icons/weather_snow.png new file mode 100755 index 0000000..45bbdf1 Binary files /dev/null and b/www/img/icons/weather_snow.png differ diff --git a/www/img/icons/weather_sun.png b/www/img/icons/weather_sun.png new file mode 100755 index 0000000..0156c26 Binary files /dev/null and b/www/img/icons/weather_sun.png differ diff --git a/www/img/icons/webcam.png b/www/img/icons/webcam.png new file mode 100755 index 0000000..af71c30 Binary files /dev/null and b/www/img/icons/webcam.png differ diff --git a/www/img/icons/webcam_add.png b/www/img/icons/webcam_add.png new file mode 100755 index 0000000..f02fcfa Binary files /dev/null and b/www/img/icons/webcam_add.png differ diff --git a/www/img/icons/webcam_delete.png b/www/img/icons/webcam_delete.png new file mode 100755 index 0000000..bd6277f Binary files /dev/null and b/www/img/icons/webcam_delete.png differ diff --git a/www/img/icons/webcam_error.png b/www/img/icons/webcam_error.png new file mode 100755 index 0000000..2faa706 Binary files /dev/null and b/www/img/icons/webcam_error.png differ diff --git a/www/img/icons/world.png b/www/img/icons/world.png new file mode 100755 index 0000000..68f21d3 Binary files /dev/null and b/www/img/icons/world.png differ diff --git a/www/img/icons/world_add.png b/www/img/icons/world_add.png new file mode 100755 index 0000000..6d0d7f7 Binary files /dev/null and b/www/img/icons/world_add.png differ diff --git a/www/img/icons/world_delete.png b/www/img/icons/world_delete.png new file mode 100755 index 0000000..ffcd115 Binary files /dev/null and b/www/img/icons/world_delete.png differ diff --git a/www/img/icons/world_edit.png b/www/img/icons/world_edit.png new file mode 100755 index 0000000..00794d4 Binary files /dev/null and b/www/img/icons/world_edit.png differ diff --git a/www/img/icons/world_go.png b/www/img/icons/world_go.png new file mode 100755 index 0000000..aee9c97 Binary files /dev/null and b/www/img/icons/world_go.png differ diff --git a/www/img/icons/world_link.png b/www/img/icons/world_link.png new file mode 100755 index 0000000..b8edc12 Binary files /dev/null and b/www/img/icons/world_link.png differ diff --git a/www/img/icons/wrench.png b/www/img/icons/wrench.png new file mode 100755 index 0000000..5c8213f Binary files /dev/null and b/www/img/icons/wrench.png differ diff --git a/www/img/icons/wrench_orange.png b/www/img/icons/wrench_orange.png new file mode 100755 index 0000000..565a933 Binary files /dev/null and b/www/img/icons/wrench_orange.png differ diff --git a/www/img/icons/xhtml.png b/www/img/icons/xhtml.png new file mode 100755 index 0000000..da5dbf2 Binary files /dev/null and b/www/img/icons/xhtml.png differ diff --git a/www/img/icons/xhtml_add.png b/www/img/icons/xhtml_add.png new file mode 100755 index 0000000..bbaf784 Binary files /dev/null and b/www/img/icons/xhtml_add.png differ diff --git a/www/img/icons/xhtml_delete.png b/www/img/icons/xhtml_delete.png new file mode 100755 index 0000000..157b520 Binary files /dev/null and b/www/img/icons/xhtml_delete.png differ diff --git a/www/img/icons/xhtml_go.png b/www/img/icons/xhtml_go.png new file mode 100755 index 0000000..43cf814 Binary files /dev/null and b/www/img/icons/xhtml_go.png differ diff --git a/www/img/icons/xhtml_valid.png b/www/img/icons/xhtml_valid.png new file mode 100755 index 0000000..d2e1cfb Binary files /dev/null and b/www/img/icons/xhtml_valid.png differ diff --git a/www/img/icons/zoom.png b/www/img/icons/zoom.png new file mode 100755 index 0000000..908612e Binary files /dev/null and b/www/img/icons/zoom.png differ diff --git a/www/img/icons/zoom_in.png b/www/img/icons/zoom_in.png new file mode 100755 index 0000000..cdf0a52 Binary files /dev/null and b/www/img/icons/zoom_in.png differ diff --git a/www/img/icons/zoom_out.png b/www/img/icons/zoom_out.png new file mode 100755 index 0000000..07bf98a Binary files /dev/null and b/www/img/icons/zoom_out.png differ diff --git a/www/img/loader.gif b/www/img/loader.gif new file mode 100644 index 0000000..30301fa Binary files /dev/null and b/www/img/loader.gif differ diff --git a/www/img/logo.png b/www/img/logo.png new file mode 100755 index 0000000..0100f18 Binary files /dev/null and b/www/img/logo.png differ diff --git a/www/img/logo2.png b/www/img/logo2.png new file mode 100644 index 0000000..804bd95 Binary files /dev/null and b/www/img/logo2.png differ diff --git a/www/img/logo3.png b/www/img/logo3.png new file mode 100644 index 0000000..93b5393 Binary files /dev/null and b/www/img/logo3.png differ diff --git a/www/img/logo4.png b/www/img/logo4.png new file mode 100644 index 0000000..19b81ab Binary files /dev/null and b/www/img/logo4.png differ diff --git a/www/img/marker.svg b/www/img/marker.svg new file mode 100644 index 0000000..9e800c6 --- /dev/null +++ b/www/img/marker.svg @@ -0,0 +1,21 @@ + + + + +]> + + + + + + + + + + + + diff --git a/www/img/order.png b/www/img/order.png new file mode 100755 index 0000000..5eb7e73 Binary files /dev/null and b/www/img/order.png differ diff --git a/www/img/taxo/cat1.png b/www/img/taxo/cat1.png new file mode 100644 index 0000000..2d8a041 Binary files /dev/null and b/www/img/taxo/cat1.png differ diff --git a/www/img/taxo/cat10.png b/www/img/taxo/cat10.png new file mode 100644 index 0000000..ee87ccf Binary files /dev/null and b/www/img/taxo/cat10.png differ diff --git a/www/img/taxo/cat14.png b/www/img/taxo/cat14.png new file mode 100644 index 0000000..e48cbf8 Binary files /dev/null and b/www/img/taxo/cat14.png differ diff --git a/www/img/taxo/cat3.png b/www/img/taxo/cat3.png new file mode 100644 index 0000000..135bf47 Binary files /dev/null and b/www/img/taxo/cat3.png differ diff --git a/www/img/taxo/cat4.png b/www/img/taxo/cat4.png new file mode 100644 index 0000000..0681c48 Binary files /dev/null and b/www/img/taxo/cat4.png differ diff --git a/www/img/taxo/cat5.png b/www/img/taxo/cat5.png new file mode 100644 index 0000000..1c87ca1 Binary files /dev/null and b/www/img/taxo/cat5.png differ diff --git a/www/img/taxo/cat6.png b/www/img/taxo/cat6.png new file mode 100644 index 0000000..6a1ae1e Binary files /dev/null and b/www/img/taxo/cat6.png differ diff --git a/www/img/taxo/cat7.png b/www/img/taxo/cat7.png new file mode 100644 index 0000000..b23844a Binary files /dev/null and b/www/img/taxo/cat7.png differ diff --git a/www/img/taxo/cat8.png b/www/img/taxo/cat8.png new file mode 100644 index 0000000..2d8ed4a Binary files /dev/null and b/www/img/taxo/cat8.png differ diff --git a/www/img/taxo/cat9.png b/www/img/taxo/cat9.png new file mode 100644 index 0000000..9f1642c Binary files /dev/null and b/www/img/taxo/cat9.png differ diff --git a/www/img/tick_no.png b/www/img/tick_no.png new file mode 100755 index 0000000..c389386 Binary files /dev/null and b/www/img/tick_no.png differ diff --git a/www/img/tick_ok.png b/www/img/tick_ok.png new file mode 100755 index 0000000..19473d9 Binary files /dev/null and b/www/img/tick_ok.png differ diff --git a/www/img/tick_ok_me.png b/www/img/tick_ok_me.png new file mode 100755 index 0000000..f3684a9 Binary files /dev/null and b/www/img/tick_ok_me.png differ diff --git a/www/img/unknown.png b/www/img/unknown.png new file mode 100755 index 0000000..14333f9 Binary files /dev/null and b/www/img/unknown.png differ diff --git a/www/img/weight.png b/www/img/weight.png new file mode 100644 index 0000000..ca17d7a Binary files /dev/null and b/www/img/weight.png differ diff --git a/www/js/rgraph/RGraph.bar.js b/www/js/rgraph/RGraph.bar.js new file mode 100755 index 0000000..6e160e7 --- /dev/null +++ b/www/js/rgraph/RGraph.bar.js @@ -0,0 +1,2563 @@ + /** + * o------------------------------------------------------------------------------o + * | This file is part of the RGraph package - you can learn more at: | + * | | + * | http://www.rgraph.net | + * | | + * | This package is licensed under the RGraph license. For all kinds of business | + * | purposes there is a small one-time licensing fee to pay and for non | + * | commercial purposes it is free to use. You can read the full license here: | + * | | + * | http://www.rgraph.net/LICENSE.txt | + * o------------------------------------------------------------------------------o + */ + + if (typeof(RGraph) == 'undefined') RGraph = {}; + + /** + * The bar chart constructor + * + * @param object canvas The canvas object + * @param array data The chart data + */ + RGraph.Bar = function (id, data) + { + // Get the canvas and context objects + this.id = id; + this.canvas = document.getElementById(id); + this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null; + this.canvas.__object__ = this; + this.type = 'bar'; + this.max = 0; + this.stackedOrGrouped = false; + this.isRGraph = true; + this.uid = RGraph.CreateUID(); + this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID(); + this.colorsParsed = false; + + + /** + * Compatibility with older browsers + */ + RGraph.OldBrowserCompat(this.context); + + + // Various config type stuff + this.properties = { + 'chart.background.barcolor1': 'rgba(0,0,0,0)', + 'chart.background.barcolor2': 'rgba(0,0,0,0)', + 'chart.background.grid': true, + 'chart.background.grid.color': '#ddd', + 'chart.background.grid.width': 1, + 'chart.background.grid.hsize': 20, + 'chart.background.grid.vsize': 20, + 'chart.background.grid.vlines': true, + 'chart.background.grid.hlines': true, + 'chart.background.grid.border': true, + 'chart.background.grid.autofit':true, + 'chart.background.grid.autofit.numhlines': 5, + 'chart.background.grid.autofit.numvlines': 20, + 'chart.background.image.stretch': true, + 'chart.background.image.x': null, + 'chart.background.image.y': null, + 'chart.background.image.w': null, + 'chart.background.image.h': null, + 'chart.background.image.align': null, + 'chart.ytickgap': 20, + 'chart.smallyticks': 3, + 'chart.largeyticks': 5, + 'chart.numyticks': 10, + 'chart.hmargin': 5, + 'chart.hmargin.grouped': 1, + 'chart.strokecolor': 'rgba(0,0,0,0)', + 'chart.axis.color': 'black', + 'chart.gutter.top': 25, + 'chart.gutter.bottom': 25, + 'chart.gutter.left': 25, + 'chart.gutter.right': 25, + 'chart.labels': null, + 'chart.labels.colors': [], // Gradients aren't supported for these colors + 'chart.labels.ingraph': null, + 'chart.labels.above': false, + 'chart.labels.above.decimals': 0, + 'chart.labels.above.size': null, + 'chart.labels.above.angle': null, + 'chart.ylabels': true, + 'chart.ylabels.colors': [], // Gradients aren't supported for these colors + 'chart.ylabels.count': 5, + 'chart.ylabels.inside': false, + 'chart.xlabels.offset': 0, + 'chart.xaxispos': 'bottom', + 'chart.yaxispos': 'left', + 'chart.text.color': 'black', // Gradients aren't supported for this color + 'chart.text.size': 10, + 'chart.text.angle': 0, + 'chart.text.font': 'Arial', + 'chart.ymin': 0, + 'chart.ymax': null, + 'chart.title': '', + 'chart.title.font': null, + 'chart.title.background': null, // Gradients aren't supported for this color + 'chart.title.hpos': null, + 'chart.title.vpos': null, + 'chart.title.bold': true, + 'chart.title.xaxis': '', + 'chart.title.xaxis.bold': true, + 'chart.title.xaxis.size': null, + 'chart.title.xaxis.font': null, + 'chart.title.yaxis': '', + 'chart.title.yaxis.bold': true, + 'chart.title.yaxis.size': null, + 'chart.title.yaxis.font': null, + 'chart.title.yaxis.color': null, // Gradients aren't supported for this color + 'chart.title.xaxis.pos': null, + 'chart.title.yaxis.pos': null, + 'chart.colors': ['#01B4FF', '#0f0', '#00f', '#ff0', '#0ff', '#0f0'], + 'chart.colors.sequential': false, + 'chart.colors.reverse': false, + 'chart.grouping': 'grouped', + 'chart.variant': 'bar', + 'chart.variant.sketch.verticals': true, + 'chart.shadow': false, + 'chart.shadow.color': '#999', // Gradients aren't supported for this color + 'chart.shadow.offsetx': 3, + 'chart.shadow.offsety': 3, + 'chart.shadow.blur': 3, + 'chart.tooltips': null, + 'chart.tooltips.effect': 'fade', + 'chart.tooltips.css.class': 'RGraph_tooltip', + 'chart.tooltips.event': 'onclick', + 'chart.tooltips.highlight': true, + 'chart.highlight.stroke': 'rgba(0,0,0,0)', + 'chart.highlight.fill': 'rgba(255,255,255,0.7)', + 'chart.background.hbars': null, + 'chart.key': null, + 'chart.key.background': 'white', + 'chart.key.position': 'graph', + 'chart.key.shadow': false, + 'chart.key.shadow.color': '#666', + 'chart.key.shadow.blur': 3, + 'chart.key.shadow.offsetx': 2, + 'chart.key.shadow.offsety': 2, + 'chart.key.position.gutter.boxed': true, + 'chart.key.position.x': null, + 'chart.key.position.y': null, + 'chart.key.halign': 'right', + 'chart.key.color.shape': 'square', + 'chart.key.rounded': true, + 'chart.key.text.size': 10, + 'chart.key.linewidth': 1, + 'chart.key.colors': null, + 'chart.contextmenu': null, + 'chart.units.pre': '', + 'chart.units.post': '', + 'chart.scale.decimals': 0, + 'chart.scale.point': '.', + 'chart.scale.thousand': ',', + 'chart.crosshairs': false, + 'chart.crosshairs.color': '#333', + 'chart.crosshairs.hline': true, + 'chart.crosshairs.vline': true, + 'chart.linewidth': 1, + 'chart.annotatable': false, + 'chart.annotate.color': 'black', + 'chart.zoom.factor': 1.5, + 'chart.zoom.fade.in': true, + 'chart.zoom.fade.out': true, + 'chart.zoom.hdir': 'right', + 'chart.zoom.vdir': 'down', + 'chart.zoom.frames': 25, + 'chart.zoom.delay': 16.666, + 'chart.zoom.shadow': true, + 'chart.zoom.background': true, + 'chart.resizable': false, + 'chart.resize.handle.adjust': [0,0], + 'chart.resize.handle.background': null, + 'chart.adjustable': false, + 'chart.noaxes': false, + 'chart.noxaxis': false, + 'chart.noyaxis': false, + 'chart.events.click': null, + 'chart.events.mousemove': null, + 'chart.numxticks': null + } + + // Check for support + if (!this.canvas) { + alert('[BAR] No canvas support'); + return; + } + + /** + * Determine whether the chart will contain stacked or grouped bars + */ + for (var i=0; i 0) { + alert('[BAR] The data element with index ' + i + ' should be negative'); + } + } + } + } + + /** + * lineWidth doesn't appear to like a zero setting + */ + if (name.toLowerCase() == 'chart.linewidth' && value == 0) { + value = 0.0001; + } + + this.properties[name] = value; + } + + + /** + * A getter + * + * @param name string The name of the property to get + */ + RGraph.Bar.prototype.Get = function (name) + { + return this.properties[name]; + } + + + + /** + * The function you call to draw the bar chart + */ + RGraph.Bar.prototype.Draw = function () + { + + // MUST be the first thing done! + if (typeof(this.properties['chart.background.image']) == 'string') { + RGraph.DrawBackgroundImage(this); + } + + /** + * Fire the onbeforedraw event + */ + RGraph.FireCustomEvent(this, 'onbeforedraw'); + + + + /** + * Parse the colors. This allows for simple gradient syntax + */ + if (!this.colorsParsed) { + this.parseColors(); + + // Don't want to do this again + this.colorsParsed = true; + } + + + + /** + * This is new in May 2011 and facilitates indiviual gutter settings, + * eg chart.gutter.left + */ + this.gutterLeft = this.properties['chart.gutter.left']; + this.gutterRight = this.properties['chart.gutter.right']; + this.gutterTop = this.properties['chart.gutter.top']; + this.gutterBottom = this.properties['chart.gutter.bottom']; + + /** + * Convert any null values to 0. Won't make any difference to the bar (as opposed to the line chart) + */ + for (var i=0; i 0) { + + alert('[BAR] (' + this.id + ') Sorry, tooltips are not supported with dot or pyramid charts'); + } + + /** + * Stop the coords array from growing uncontrollably + */ + this.coords = []; + + /** + * Work out a few things. They need to be here because they depend on things you can change before you + * call Draw() but after you instantiate the object + */ + this.max = 0; + this.grapharea = this.canvas.height - this.gutterTop - this.gutterBottom; + this.halfgrapharea = this.grapharea / 2; + this.halfTextHeight = this.properties['chart.text.size'] / 2; + + + /** + * This sets the label colors. Doing it here saves lots of if() conditions in the draw method + */ + if (this.properties['chart.labels']) { + if (this.properties['chart.labels.colors'].length < this.properties['chart.labels'].length) { + while (this.properties['chart.labels.colors'].length < this.properties['chart.labels'].length) { + this.properties['chart.labels.colors'].push(this.properties['chart.labels.colors'][this.properties['chart.labels.colors'].length - 1]); + } + } else { + while (this.properties['chart.labels.colors'].length < this.properties['chart.labels'].length) { + this.properties['chart.labels.colors'].push(this.properties['chart.text.color']); + } + } + } + + if (this.properties['chart.ylabels.colors'].length < this.properties['chart.ylabels.count']) { + while (this.properties['chart.ylabels.colors'].length < this.properties['chart.ylabels.count']) { + this.properties['chart.ylabels.colors'].push(this.properties['chart.ylabels.colors'][this.properties['chart.ylabels.colors'].length - 1]); + } + } else { + while (this.properties['chart.ylabels.colors'].length < this.properties['chart.ylabels'].count) { + this.properties['chart.ylabels.colors'].push(this.properties['chart.text.color']); + } + } + + + // Progressively Draw the chart + RGraph.background.Draw(this); + + + + + //If it's a sketch chart variant, draw the axes first + if (this.properties['chart.variant'] == 'sketch') { + this.DrawAxes(); + this.Drawbars(); + } else { + this.Drawbars(); + this.DrawAxes(); + } + + this.DrawLabels(); + + + // Draw the key if necessary + if (this.properties['chart.key'] && this.properties['chart.key'].length) { + RGraph.DrawKey(this, this.properties['chart.key'], this.properties['chart.colors']); + } + + + /** + * Setup the context menu if required + */ + if (this.properties['chart.contextmenu']) { + RGraph.ShowContext(this); + } + + + /** + * Is a line is defined, draw it + * + var line = this.Get('chart.line'); + + if (line) { + + line.__bar__ = this; + + // Check the X axis positions + if (this.Get('chart.xaxispos') != line.Get('chart.xaxispos')) { + alert("[BAR] Using different X axis positions when combining the Bar and Line is not advised"); + } + + line.Set('chart.gutter.left', this.Get('chart.gutter.left')); + line.Set('chart.gutter.right', this.Get('chart.gutter.right')); + line.Set('chart.gutter.top', this.Get('chart.gutter.top')); + line.Set('chart.gutter.bottom', this.Get('chart.gutter.bottom')); + + line.Set('chart.hmargin', (this.canvas.width - this.gutterLeft - this.gutterRight) / (line.original_data[0].length * 2)); + + // If a BAR custom yMax is set, use that + if (this.Get('chart.ymax') && !line.Get('chart.ymax')) { + line.Set('chart.ymax', this.Get('chart.ymax')); + + } else if (line.Get('chart.ymax')) { + line.Set('chart.ymax', line.Get('chart.ymax')); + } + + // The boolean is used to specify that the Line chart .Draw() method is being called by the Bar chart + line.Draw(true); + } +*/ + + + /** + * Draw "in graph" labels + */ + if (this.properties['chart.labels.ingraph']) { + RGraph.DrawInGraphLabels(this); + } + + + /** + * This function enables resizing + */ + if (this.properties['chart.resizable']) { + RGraph.AllowResizing(this); + } + + + /** + * This installs the event listeners + */ + RGraph.InstallEventListeners(this); + + + /** + * Fire the RGraph ondraw event + */ + RGraph.FireCustomEvent(this, 'ondraw'); + } + + + + /** + * Draws the charts axes + */ + RGraph.Bar.prototype.DrawAxes = function () + { + if (this.properties['chart.noaxes']) { + return; + } + + var xaxispos = this.properties['chart.xaxispos']; + var yaxispos = this.properties['chart.yaxispos']; + var isSketch = this.properties['chart.variant'] == 'sketch'; + + this.context.beginPath(); + this.context.strokeStyle = this.properties['chart.axis.color']; + this.context.lineWidth = 1; + + + // Draw the Y axis + if (this.properties['chart.noyaxis'] == false) { + if (yaxispos == 'right') { + this.context.moveTo(AA(this, this.canvas.width - this.gutterRight + (isSketch ? 3 : 0)), this.gutterTop - (isSketch ? 3 : 0)); + this.context.lineTo(AA(this, this.canvas.width - this.gutterRight) - (isSketch ? 2 : 0), this.canvas.height - this.gutterBottom + (isSketch ? 5 : 0)); + } else { + this.context.moveTo(AA(this, this.gutterLeft - (isSketch ? 2 : 0)), this.gutterTop - (isSketch ? 5 : 0)); + this.context.lineTo(AA(this, this.gutterLeft - (isSketch ? 1 : 0)), this.canvas.height - this.gutterBottom + (isSketch ? 5 : 0)); + } + } + + // Draw the X axis + if (this.properties['chart.noxaxis'] == false) { + if (xaxispos == 'center') { + this.context.moveTo(this.gutterLeft - (isSketch ? 5 : 0), AA(this, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop + (isSketch ? 2 : 0))); + this.context.lineTo(this.canvas.width - this.gutterRight + (isSketch ? 5 : 0), AA(this, ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - (isSketch ? 2 : 0))); + } else if (xaxispos == 'top') { + this.context.moveTo(this.gutterLeft - (isSketch ? 3 : 0), AA(this, this.gutterTop) - (isSketch ? 3 : 0)); + this.context.lineTo(this.canvas.width - this.gutterRight + (isSketch ? 5 : 0), AA(this, this.gutterTop + (isSketch ? 2 : 0))); + } else { + this.context.moveTo(this.gutterLeft - (isSketch ? 5 : 0), AA(this, this.canvas.height - this.gutterBottom) - (isSketch ? 2 : 0)); + this.context.lineTo(this.canvas.width - this.gutterRight + (isSketch ? 8 : 0), AA(this, this.canvas.height - this.gutterBottom + (isSketch ? 2 : 0))); + } + } + + var numYTicks = this.properties['chart.numyticks']; + + // Draw the Y tickmarks + if (this.properties['chart.noyaxis'] == false && !isSketch) { + var yTickGap = (this.canvas.height - this.gutterTop - this.gutterBottom) / numYTicks; + var xpos = yaxispos == 'left' ? this.gutterLeft : this.canvas.width - this.gutterRight; + + if (this.properties['chart.numyticks'] > 0) { + for (y=this.gutterTop; + xaxispos == 'center' ? y <= (this.canvas.height - this.gutterBottom) : y < (this.canvas.height - this.gutterBottom + (xaxispos == 'top' ? 1 : 0)); + y += yTickGap) { + + if (xaxispos == 'center' && y == (this.gutterTop + (this.grapharea/ 2))) continue; + + // X axis at the top + if (xaxispos == 'top' && y == this.gutterTop) continue; + + this.context.moveTo(xpos, AA(this, y)); + this.context.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), AA(this, y)); + } + } + + /** + * If the X axis is not being shown, draw an extra tick + */ + if (this.properties['chart.noxaxis']) { + if (xaxispos == 'center') { + this.context.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), AA(this, this.canvas.height / 2)); + this.context.lineTo(xpos, AA(this, this.canvas.height / 2)); + } else if (xaxispos == 'top') { + this.context.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), AA(this, this.gutterTop)); + this.context.lineTo(xpos, AA(this, this.gutterTop)); + } else { + this.context.moveTo(xpos + (yaxispos == 'left' ? -3 : 3), AA(this, this.canvas.height - this.gutterBottom)); + this.context.lineTo(xpos, AA(this, this.canvas.height - this.gutterBottom)); + } + } + } + + + // Draw the X tickmarks + if (this.properties['chart.noxaxis'] == false && !isSketch) { + + xTickGap = (this.canvas.width - this.gutterLeft - this.gutterRight) / this.data.length; + + if (typeof(this.properties['chart.numxticks']) == 'number') { + xTickGap = (this.canvas.width - this.gutterLeft - this.gutterRight) / this.properties['chart.numxticks']; + } + + if (xaxispos == 'bottom') { + yStart = this.canvas.height - this.gutterBottom; + yEnd = (this.canvas.height - this.gutterBottom) + 3; + } else if (xaxispos == 'top') { + yStart = this.gutterTop - 3; + yEnd = this.gutterTop; + } else if (xaxispos == 'center') { + yStart = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop + 3; + yEnd = ((this.canvas.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop - 3; + } + + yStart = AA(this, yStart); + yEnd = AA(this, yEnd); + + //////////////// X TICKS //////////////// + var noEndXTick = this.properties['chart.noendxtick']; + + for (x=this.gutterLeft + (yaxispos == 'left' ? xTickGap : 0); x this.gutterLeft) { + this.context.moveTo(AA(this, x), yStart); + this.context.lineTo(AA(this, x), yEnd); + + } else if (yaxispos == 'left' && noEndXTick && x > this.gutterLeft && x < (this.canvas.width - this.gutterRight) ) { + this.context.moveTo(AA(this, x), yStart); + this.context.lineTo(AA(this, x), yEnd); + + } else if (yaxispos == 'right' && x < (this.canvas.width - this.gutterRight) && !noEndXTick) { + this.context.moveTo(AA(this, x), yStart); + this.context.lineTo(AA(this, x), yEnd); + + } else if (yaxispos == 'right' && x < (this.canvas.width - this.gutterRight) && x > (this.gutterLeft) && noEndXTick) { + this.context.moveTo(AA(this, x), yStart); + this.context.lineTo(AA(this, x), yEnd); + } + } + + if (this.properties['chart.noyaxis'] || this.properties['chart.numxticks'] == null) { + if (typeof(this.properties['chart.numxticks']) == 'number' && this.properties['chart.numxticks'] > 0) { + this.context.moveTo(AA(this, this.gutterLeft), yStart); + this.context.lineTo(AA(this, this.gutterLeft), yEnd); + } + } + + //////////////// X TICKS //////////////// + } + + /** + * If the Y axis is not being shown, draw an extra tick + */ + if (this.properties['chart.noyaxis'] && this.properties['chart.noxaxis'] == false && this.properties['chart.numxticks'] == null) { + if (xaxispos == 'center') { + this.context.moveTo(AA(this, this.gutterLeft), (this.canvas.height / 2) - 3); + this.context.lineTo(AA(this, this.gutterLeft), (this.canvas.height / 2) + 3); + } else { + this.context.moveTo(AA(this, this.gutterLeft), this.canvas.height - this.gutterBottom); + this.context.lineTo(AA(this, this.gutterLeft), this.canvas.height - this.gutterBottom + 3); + } + } + + this.context.stroke(); + } + + + /** + * Draws the bars + */ + RGraph.Bar.prototype.Drawbars = function () + { + this.context.lineWidth = this.properties['chart.linewidth']; + this.context.strokeStyle = this.properties['chart.strokecolor']; + this.context.fillStyle = this.properties['chart.colors'][0]; + var prevX = 0; + var prevY = 0; + var decimals = this.properties['chart.scale.decimals']; + + /** + * Work out the max value + */ + if (this.properties['chart.ymax']) { + + this.max = this.properties['chart.ymax']; + this.min = this.properties['chart.ymin']; + + this.scale = [ + (((this.max - this.min) * (1/5)) + this.min).toFixed(decimals), + (((this.max - this.min) * (2/5)) + this.min).toFixed(decimals), + (((this.max - this.min) * (3/5)) + this.min).toFixed(decimals), + (((this.max - this.min) * (4/5)) + this.min).toFixed(decimals), + (((this.max - this.min) * (5/5) + this.min)).toFixed(decimals) + ]; + } else { + + this.min = this.properties['chart.ymin']; + + for (i=0; i 0) { + RGraph.DrawBars(this); + } + + var variant = this.properties['chart.variant']; + + /** + * Draw the 3D axes is necessary + */ + if (variant == '3d') { + RGraph.Draw3DAxes(this); + } + + /** + * Get the variant once, and draw the bars, be they regular, stacked or grouped + */ + + // Get these variables outside of the loop + var xaxispos = this.properties['chart.xaxispos']; + var width = (this.canvas.width - this.gutterLeft - this.gutterRight ) / this.data.length; + var orig_height = height; + var hmargin = this.properties['chart.hmargin']; + var shadow = this.properties['chart.shadow']; + var shadowColor = this.properties['chart.shadow.color']; + var shadowBlur = this.properties['chart.shadow.blur']; + var shadowOffsetX = this.properties['chart.shadow.offsetx']; + var shadowOffsetY = this.properties['chart.shadow.offsety']; + var strokeStyle = this.properties['chart.strokecolor']; + var colors = this.properties['chart.colors']; + var sequentialColorIndex = 0; + + for (i=0; i 0.4 ? -1 : 3) - (r * width),y - 1); + this.context.lineTo(x + hmargin + width - (r > 0.4 ? 1 : -1) - (r * width), y + height + (r == 0.2 ? 1 : -2)); + } + } + + this.context.stroke(); + + // Regular bar + } else if (variant == 'bar' || variant == '3d' || variant == 'glass' || variant == 'bevel') { + + if (RGraph.isOld() && shadow) { + this.DrawIEShadow([x + hmargin, y, barWidth, height]); + } + + if (variant == 'glass') { + RGraph.filledCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0); + RGraph.strokedCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0); + } else { + this.context.strokeRect(x + hmargin, y, barWidth, height); + this.context.fillRect(x + hmargin, y, barWidth, height); + } + + // 3D effect + if (variant == '3d') { + + var prevStrokeStyle = this.context.strokeStyle; + var prevFillStyle = this.context.fillStyle; + + // Draw the top + this.context.beginPath(); + this.context.moveTo(x + hmargin, y); + this.context.lineTo(x + hmargin + 10, y - 5); + this.context.lineTo(x + hmargin + 10 + barWidth, y - 5); + this.context.lineTo(x + hmargin + barWidth, y); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + // Draw the right hand side + this.context.beginPath(); + this.context.moveTo(x + hmargin + barWidth, y); + this.context.lineTo(x + hmargin + barWidth + 10, y - 5); + this.context.lineTo(x + hmargin + barWidth + 10, y + height - 5); + this.context.lineTo(x + hmargin + barWidth, y + height); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + // Draw the darker top section + this.context.beginPath(); + this.context.fillStyle = 'rgba(255,255,255,0.3)'; + this.context.moveTo(x + hmargin, y); + this.context.lineTo(x + hmargin + 10, y - 5); + this.context.lineTo(x + hmargin + 10 + barWidth, y - 5); + this.context.lineTo(x + hmargin + barWidth, y); + this.context.lineTo(x + hmargin, y); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + // Draw the darker right side section + this.context.beginPath(); + this.context.fillStyle = 'rgba(0,0,0,0.4)'; + this.context.moveTo(x + hmargin + barWidth, y); + this.context.lineTo(x + hmargin + barWidth + 10, y - 5); + this.context.lineTo(x + hmargin + barWidth + 10, y - 5 + height); + this.context.lineTo(x + hmargin + barWidth, y + height); + this.context.lineTo(x + hmargin + barWidth, y); + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + this.context.strokeStyle = prevStrokeStyle; + this.context.fillStyle = prevFillStyle; + + // Glass variant + } else if (variant == 'glass') { + + var grad = this.context.createLinearGradient( + x + hmargin, + y, + x + hmargin + (barWidth / 2), + y + ); + grad.addColorStop(0, 'rgba(255,255,255,0.9)'); + grad.addColorStop(1, 'rgba(255,255,255,0.5)'); + + this.context.beginPath(); + this.context.fillStyle = grad; + this.context.fillRect(x + hmargin + 2,y + (this.data[i] > 0 ? 2 : 0),(barWidth / 2) - 2,height - 2); + this.context.fill(); + } + + // This bit draws the text labels that appear above the bars if requested + if (this.properties['chart.labels.above']) { + + // Turn off any shadow + if (shadow) { + RGraph.NoShadow(this); + } + + var yPos = y - 3; + + // Account for negative bars + if (this.data[i] < 0) { + yPos += height + 6 + (this.properties['chart.text.size'] - 4); + } + + // Account for chart.xaxispos=top + if (this.properties['chart.xaxispos'] == 'top') { + yPos = this.gutterTop + height + 6 + (typeof(this.properties['chart.labels.above.size']) == 'number' ? this.properties['chart.labels.above.size'] : this.properties['chart.text.size'] - 4); + } + + // Account for chart.variant=3d + if (this.properties['chart.variant'] == '3d') { + yPos -= 5; + } + + this.context.fillStyle = this.properties['chart.text.color']; + + RGraph.Text(this.context, + this.properties['chart.text.font'], + typeof(this.properties['chart.labels.above.size']) == 'number' ? this.properties['chart.labels.above.size'] : this.properties['chart.text.size'] - 3, + x + hmargin + (barWidth / 2), + yPos, + RGraph.number_format(this, Number(this.data[i]).toFixed(this.properties['chart.labels.above.decimals']),this.properties['chart.units.pre'],this.properties['chart.units.post']), + this.properties['chart.labels.above.angle'] ? 'bottom' : null, + this.properties['chart.labels.above.angle'] ? (this.properties['chart.labels.above.angle'] > 0 && (this.properties['chart.xaxispos'] != 'top') ? 'right' : 'left') : 'center', + null, + this.properties['chart.labels.above.angle'] + ); + } + + // Dot chart + } else if (variant == 'dot') { + + this.context.beginPath(); + this.context.moveTo(x + (width / 2), y); + this.context.lineTo(x + (width / 2), y + height); + this.context.stroke(); + + this.context.beginPath(); + this.context.fillStyle = this.properties['chart.colors'][i]; + this.context.arc(x + (width / 2), y + (this.data[i] > 0 ? 0 : height), 2, 0, 6.28, 0); + + // Set the colour for the dots + this.context.fillStyle = this.properties['chart.colors'][0]; + + /** + * Sequential colors + */ + if (this.properties['chart.colors.sequential']) { + this.context.fillStyle = colors[i]; + } + + this.context.stroke(); + this.context.fill(); + + // Pyramid chart + } else if (variant == 'pyramid') { + + this.context.beginPath(); + var startY = (this.properties['chart.xaxispos'] == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.gutterBottom)); + + this.context.moveTo(x + hmargin, startY); + this.context.lineTo( + x + hmargin + (barWidth / 2), + y + (this.properties['chart.xaxispos'] == 'center' && (this.data[i] < 0) ? height : 0) + ); + this.context.lineTo(x + hmargin + barWidth, startY); + + this.context.closePath(); + + this.context.stroke(); + this.context.fill(); + + // Arrow chart + } else if (variant == 'arrow') { + + var startY = (this.properties['chart.xaxispos'] == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.gutterBottom)); + + this.context.lineWidth = this.properties['chart.linewidth'] ? this.properties['chart.linewidth'] : 1; + this.context.lineCap = 'round'; + + this.context.beginPath(); + this.context.moveTo(AA(this, x + hmargin + (barWidth / 2)), startY); + this.context.lineTo(AA(this, x + hmargin + (barWidth / 2)), y + (this.properties['chart.xaxispos'] == 'center' && (this.data[i] < 0) ? height : 0)); + this.context.arc(x + hmargin + (barWidth / 2), + y + (this.properties['chart.xaxispos'] == 'center' && (this.data[i] < 0) ? height : 0), + 5, + this.data[i] > 0 ? 0.78 : 5.6, + this.data[i] > 0 ? 0.79 : 5.48, + this.data[i] < 0); + + this.context.moveTo(AA(this, x + hmargin + (barWidth / 2)), y + (this.properties['chart.xaxispos'] == 'center' && (this.data[i] < 0) ? height : 0)); + this.context.arc(x + hmargin + (barWidth / 2), + y + (this.properties['chart.xaxispos'] == 'center' && (this.data[i] < 0) ? height : 0), + 5, + this.data[i] > 0 ? 2.355 : 4, + this.data[i] > 0 ? 2.4 : 3.925, + this.data[i] < 0); + + this.context.stroke(); + + this.context.lineWidth = 1; + + // Unknown variant type + } else { + alert('[BAR] Warning! Unknown chart.variant: ' + variant); + } + + this.coords.push([x + hmargin, y, width - (2 * hmargin), height]); + + + /** + * Stacked bar + */ + } else if (typeof(this.data[i]) == 'object' && this.properties['chart.grouping'] == 'stacked') { + + if (this.min) { + alert("[ERROR] Stacked Bar charts with a Y min are not supported"); + } + + var barWidth = width - (2 * hmargin); + var redrawCoords = [];// Necessary to draw if the shadow is enabled + var startY = 0; + var dataset = this.data[i]; + + /** + * Check for a negative bar width + */ + if (barWidth < 0) { + alert('[RGRAPH] Warning: you have a negative bar width. This may be caused by the chart.hmargin being too high or the width of the canvas not being sufficient.'); + } + + for (j=0; j 0 ? 'right' : 'left') : 'center', + null, + this.properties['chart.labels.above.angle']); + + // Turn any shadow back on + if (shadow) { + this.context.shadowColor = shadowColor; + this.context.shadowBlur = shadowBlur; + this.context.shadowOffsetX = shadowOffsetX; + this.context.shadowOffsetY = shadowOffsetY; + } + } + + + /** + * Redraw the bars if the shadow is enabled due to hem being drawn from the bottom up, and the + * shadow spilling over to higher up bars + */ + if (shadow) { + + RGraph.NoShadow(this); + + for (k=0; k 0 ? 'right' : 'left') : 'center', + null, + this.properties['chart.labels.above.angle']); + + // Turn any shadow back on + if (shadow) { + this.context.shadowColor = shadowColor; + this.context.shadowBlur = shadowBlur; + this.context.shadowOffsetX = shadowOffsetX; + this.context.shadowOffsetY = shadowOffsetY; + } +} + } + + /** + * Redraw the bar if shadows are going to the left + */ + if (redrawCoords.length) { + + RGraph.NoShadow(this); + + this.context.lineWidth = this.properties['chart.linewidth']; + + this.context.beginPath(); + for (var j=0; j 0) { + angle = -1 * text_angle; + halign = 'right'; + yOffset -= 5; + + if (this.properties['chart.xaxispos'] == 'top') { + halign = 'left'; + yOffset += 5; + } + } + + // Draw the X axis labels + context.fillStyle = this.properties['chart.text.color']; + + // How wide is each bar + var barWidth = (this.canvas.width - this.gutterRight - this.gutterLeft) / labels.length; + + // Reset the xTickGap + xTickGap = (this.canvas.width - this.gutterRight - this.gutterLeft) / labels.length + + // Draw the X tickmarks + var i=0; + var font = this.properties['chart.text.font']; + + for (x=this.gutterLeft + (xTickGap / 2); x<=this.canvas.width - this.gutterRight; x+=xTickGap) { + this.context.fillStyle = this.properties['chart.labels.colors'][i]; + + RGraph.Text(context, font, + text_size, + x + (this.properties['chart.text.angle'] == 90 ? 0 : 0), + this.properties['chart.xaxispos'] == 'top' ? this.gutterTop - yOffset + text_size - 1 : (this.canvas.height - this.gutterBottom) + yOffset, + String(labels[i++]), + (this.properties['chart.text.angle'] == 90 ? 'center' : 'top'), + halign, + null, + angle); + } + } + + /** + * If chart.labels.above.specific is specified, draw them + */ + if (this.properties['chart.labels.above.specific']) { + + var labels = this.properties['chart.labels.above.specific']; + + for (var i=0; i= 0 ? 'bottom' : 'top'; + var halign = 'center'; + + // Horizontal alignment + if (typeof(this.properties['chart.labels.above.angle']) == 'number') { + + if (this.properties['chart.labels.above.angle'] >= 0) { + if (this.data[i] >= 0) { + halign = 'right'; + } else { + halign = 'left'; + } + } else { + if (this.data[i] >= 0) { + halign = 'left'; + } else { + halign = 'right'; + } + } + } + + RGraph.Text(context, + this.properties['chart.text.font'], + this.properties['chart.labels.above.size'] ? this.properties['chart.labels.above.size'] : this.properties['chart.text.size'], + coords[0] + (coords[2] / 2), + value >= 0 ? coords[1] - 5 : coords[1] + coords[3] + 3, + String(labels[i]), + valign, + halign, + null, + angle); + } + } + } + + /** + * Draws the X axis at the top + */ + RGraph.Bar.prototype.Drawlabels_top = function () + { + this.context.beginPath(); + this.context.fillStyle = this.properties['chart.text.color']; + this.context.strokeStyle = 'black'; + + if (this.properties['chart.xaxispos'] == 'top') { + + var context = this.context; + var interval = (this.grapharea * (1/5) ); + var text_size = this.properties['chart.text.size']; + var units_pre = this.properties['chart.units.pre']; + var units_post = this.properties['chart.units.post']; + var align = this.properties['chart.yaxispos'] == 'left' ? 'right' : 'left'; + var font = this.properties['chart.text.font']; + var numYLabels = this.properties['chart.ylabels.count']; + + if (this.properties['chart.ylabels.inside'] == true) { + var xpos = this.properties['chart.yaxispos'] == 'left' ? this.gutterLeft + 5 : this.canvas.width - this.gutterRight - 5; + var align = this.properties['chart.yaxispos'] == 'left' ? 'left' : 'right'; + var boxed = true; + } else { + var xpos = this.properties['chart.yaxispos'] == 'left' ? this.gutterLeft - 5 : this.canvas.width - this.gutterRight + 5; + var boxed = false; + } + + /** + * Draw specific Y labels here so that the local variables can be reused + */ + if (typeof(this.properties['chart.ylabels.specific']) == 'object' && this.properties['chart.ylabels.specific']) { + + var labels = RGraph.array_reverse(this.properties['chart.ylabels.specific']); + var grapharea = this.canvas.height - this.gutterTop - this.gutterBottom; + + for (var i=0; i=0; --i) { + var y = this.gutterTop + (grapharea * ( (i+1) / (labels.length * 2) )) + (grapharea / 2); + + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][i + labels.length]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][i + labels.length];} + RGraph.Text(context, font, text_size, xpos, y, labels[labels.length - i - 1], 'center', align, boxed); + } + + return; + } + + + + + + + + + + + + + if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) { + + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][0]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][0];} + RGraph.Text(context, font, text_size, xpos, this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed); + + if (numYLabels == 5) { + + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][1]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][1];} + RGraph.Text(context, font, text_size, xpos, (1*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed); + + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][3]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][3];} + RGraph.Text(context, font, text_size, xpos, (3*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed); + } + + if (numYLabels == 3 || numYLabels == 5) { + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][4]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][4];} + RGraph.Text(context, font, text_size, xpos, (4*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed); + + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][2]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][2];} + RGraph.Text(context, font, text_size, xpos, (2*interval) + this.gutterTop + this.halfTextHeight, RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed); + } + } else if (numYLabels == 10) { + // 10 Y labels + interval = (this.grapharea / 10) / 2; + + for (var i=0; i<10; ++i) { + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][i]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][i];} + RGraph.Text(context, font, text_size, xpos,this.gutterTop + ((this.grapharea / (10 * 2)) * i),RGraph.number_format(this, ((this.scale[4] / 10) * (10 - i)).toFixed((this.properties['chart.scale.decimals'])), units_pre, units_post), 'center', align, boxed); + } + } + + /////////////////////////////////////////////////////////////////////////////////// + + /** + * Draw the bottom halves labels + */ + var interval = (this.grapharea) / 10; + + if (numYLabels == 1 || numYLabels == 3 || numYLabels == 5) { + if (numYLabels == 3 || numYLabels == 5) { + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][5]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][5];} + RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - (4 * interval), '-' + RGraph.number_format(this, this.scale[0], units_pre, units_post), null, align, boxed); + + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][7]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][7];} + RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - (2 * interval), '-' + RGraph.number_format(this, this.scale[2], units_pre, units_post), null, align, boxed); + } + + if (numYLabels == 5) { + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][6]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][6];} + RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - (3 * interval), '-' + RGraph.number_format(this, this.scale[1], units_pre, units_post), null, align, boxed); + + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][8]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][8];} + RGraph.Text(context, font, text_size, xpos, (this.grapharea + this.gutterTop + this.halfTextHeight) - interval, '-' + RGraph.number_format(this, this.scale[3], units_pre, units_post), null, align, boxed); + } + + // Color + if (typeof(this.properties['chart.ylabels.colors']) == 'object' && this.properties['chart.ylabels.colors'] && this.properties['chart.ylabels.colors'][9]) {this.context.fillStyle = this.properties['chart.ylabels.colors'][9];} + RGraph.Text(context, font, text_size, xpos, this.grapharea + this.gutterTop + this.halfTextHeight, '-' + RGraph.number_format(this, this.scale[4], units_pre, units_post), null, align, boxed); + + } else if (numYLabels == 10) { + + // Arbitrary number of Y labels + interval = (this.grapharea / numYLabels) / 2; + + for (var i=0; i= left && mouseX <= (left + width) && mouseY >= top && mouseY <= (top + height)) { + + if (this.properties['chart.tooltips']) { + var tooltip = RGraph.parseTooltipText ? RGraph.parseTooltipText(this.properties['chart.tooltips'], i) : this.properties['chart.tooltips'][i]; + } + + // Work out the dataset + var dataset = 0; + var idx = i; + + while (idx >= (typeof(this.data[dataset]) == 'object' ? this.data[dataset].length : 1)) { + if (typeof(this.data[dataset]) == 'number') { + idx -= 1; + } else { + idx -= this.data[dataset].length; + } + + dataset++; + } + + if (typeof(this.data[dataset]) == 'number') { + idx = null; + } + + + return { + 0: obj, 1: left, 2: top, 3: width, 4: height, 5: i, + 'object': obj, 'x': left, 'y': top, 'width': width, 'height': height, 'index': i, 'tooltip': tooltip, 'index_adjusted': idx, 'dataset': dataset + }; + } + } + + return null; + } + + + /** + * This retrives the bar based on the X coordinate only. + * + * @param object e The event object + * @param object OPTIONAL You can pass in the bar object instead of the + * function using "this" + */ + RGraph.Bar.prototype.getShapeByX = function (e) + { + var canvas = e.target; + var mouseCoords = RGraph.getMouseXY(e); + + + // This facilitates you being able to pass in the bar object as a parameter instead of + // the function getting it from itself + var obj = arguments[1] ? arguments[1] : this; + + + /** + * Loop through the bars determining if the mouse is over a bar + */ + for (var i=0; i= left && mouseX <= (left + width)) { + + if (this.properties['chart.tooltips']) { + var tooltip = RGraph.parseTooltipText ? RGraph.parseTooltipText(this.properties['chart.tooltips'], i) : this.properties['chart.tooltips'][i]; + } + + + + return { + 0: obj, 1: left, 2: top, 3: width, 4: height, 5: i, + 'object': obj, 'x': left, 'y': top, 'width': width, 'height': height, 'index': i, 'tooltip': tooltip + }; + } + } + + return null; + } + + + /** + * When you click on the chart, this method can return the Y value at that point. It works for any point on the + * chart (that is inside the gutters) - not just points within the Bars. + * + * EITHER: + * + * @param object arg The event object + * + * OR: + * + * @param object arg A two element array containing the X and Y coordinates + */ + RGraph.Bar.prototype.getValue = function (arg) + { + if (arg.length == 2) { + var mouseX = arg[0]; + var mouseY = arg[1]; + } else { + var mouseCoords = RGraph.getMouseXY(arg); + var mouseX = mouseCoords[0]; + var mouseY = mouseCoords[1]; + } + + if ( mouseY < this.properties['chart.gutter.top'] + || mouseY > (this.canvas.height - this.properties['chart.gutter.bottom']) + || mouseX < this.properties['chart.gutter.left'] + || mouseX > (this.canvas.width - this.properties['chart.gutter.right']) + ) { + return null; + } + + if (this.properties['chart.xaxispos'] == 'center') { + var value = (((this.grapharea / 2) - (mouseY - this.properties['chart.gutter.top'])) / this.grapharea) * (this.max - this.min) + value *= 2; + + if (value >= 0) { + value += this.min; + } else { + value -= this.min; + } + + } else if (this.properties['chart.xaxispos'] == 'top') { + var value = ((this.grapharea - (mouseY - this.properties['chart.gutter.top'])) / this.grapharea) * (this.max - this.min) + value = this.max - value; + value = Math.abs(value) * -1; + } else { + var value = ((this.grapharea - (mouseY - this.properties['chart.gutter.top'])) / this.grapharea) * (this.max - this.min) + value += this.min; + } + + return value; + } + + + /** + * This function can be used when the canvas is clicked on (or similar - depending on the event) + * to retrieve the relevant Y coordinate for a particular value. + * + * @param int value The value to get the Y coordinate for + */ + RGraph.Bar.prototype.getYCoord = function (value) + { + if (value > this.max) { + return null; + } + + var y; + var xaxispos = this.properties['chart.xaxispos']; + + if (xaxispos == 'top') { + + // Account for negative numbers + if (value < 0) { + value = Math.abs(value); + } + + y = ((value - this.min) / (this.max - this.min)) * this.grapharea; + y = y + this.gutterTop + + } else if (xaxispos == 'center') { + + y = ((value - this.min) / (this.max - this.min)) * (this.grapharea / 2); + y = (this.grapharea / 2) - y; + y += this.gutterTop; + + } else { + + if (value < this.min) { + value = this.min; + } + + y = ((value - this.min) / (this.max - this.min)) * this.grapharea; + + y = this.canvas.height - this.gutterBottom - y; + } + + return y; + } + + + + /** + * Each object type has its own Highlight() function which highlights the appropriate shape + * + * @param object shape The shape to highlight + */ + RGraph.Bar.prototype.Highlight = function (shape) + { + // Add the new highlight + RGraph.Highlight.Rect(this, shape); + } + + + + /** + * The getObjectByXY() worker method + */ + RGraph.Bar.prototype.getObjectByXY = function (e) + { + var mouseXY = RGraph.getMouseXY(e); + + if ( + mouseXY[0] >= this.properties['chart.gutter.left'] + && mouseXY[0] <= (this.canvas.width - this.properties['chart.gutter.right']) + && mouseXY[1] >= this.properties['chart.gutter.top'] + && mouseXY[1] <= (this.canvas.height - this.properties['chart.gutter.bottom']) + ) { + + return this; + } + } + + + + /** + * This method handles the adjusting calculation for when the mouse is moved + * + * @param object e The event object + */ + RGraph.Bar.prototype.Adjusting_mousemove = function (e) + { + /** + * Handle adjusting for the Bar + */ + if (RGraph.Registry.Get('chart.adjusting') && RGraph.Registry.Get('chart.adjusting').uid == this.uid) { + + // Rounding the value to the given number of decimals make the chart step + var value = Number(this.getValue(e));//.toFixed(this.Get('chart.scale.decimals')); + var shape = this.getShapeByX(e); + + if (shape) { + + RGraph.Registry.Set('chart.adjusting.shape', shape); + + this.data[shape['index']] = Number(value); + RGraph.RedrawCanvas(e.target); + + RGraph.FireCustomEvent(this, 'onadjust'); + } + } + } + + + /********************************************************************************************************* + * This is the combined bar and Line class which makes creating bar/line combo charts a little bit easier * + /*********************************************************************************************************/ + + + + RGraph.CombinedChart = function () + { + /** + * Create a default empty array for the objects + */ + this.objects = []; + + for (var i=0; i

    /gi,""),b(/<\/p>/gi,"\n"),b(/ |\u00a0/gi," "),b(/"/gi,'"'),b(/</gi,"<"),b(/>/gi,">"),b(/&/gi,"&"),a},_punbb_bbcode2html:function(a){function b(b,c){a=a.replace(b,c)}return a=tinymce.trim(a),b(/\n/gi,"
    "),b(/\[b\]/gi,""),b(/\[\/b\]/gi,""),b(/\[i\]/gi,""),b(/\[\/i\]/gi,""),b(/\[u\]/gi,""),b(/\[\/u\]/gi,""),b(/\[url=([^\]]+)\](.*?)\[\/url\]/gi,'
    $2'),b(/\[url\](.*?)\[\/url\]/gi,'$1'),b(/\[img\](.*?)\[\/img\]/gi,''),b(/\[color=(.*?)\](.*?)\[\/color\]/gi,'$2'),b(/\[code\](.*?)\[\/code\]/gi,'$1 '),b(/\[quote.*?\](.*?)\[\/quote\]/gi,'$1 '),a}}),tinymce.PluginManager.add("bbcode",tinymce.plugins.BBCodePlugin)}(); \ No newline at end of file diff --git a/www/js/tinymce/plugins/charmap/plugin.min.js b/www/js/tinymce/plugins/charmap/plugin.min.js new file mode 100644 index 0000000..9bf1e9f --- /dev/null +++ b/www/js/tinymce/plugins/charmap/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("charmap",function(a){function b(){return[["160","no-break space"],["173","soft hyphen"],["34","quotation mark"],["162","cent sign"],["8364","euro sign"],["163","pound sign"],["165","yen sign"],["169","copyright sign"],["174","registered sign"],["8482","trade mark sign"],["8240","per mille sign"],["181","micro sign"],["183","middle dot"],["8226","bullet"],["8230","three dot leader"],["8242","minutes / feet"],["8243","seconds / inches"],["167","section sign"],["182","paragraph sign"],["223","sharp s / ess-zed"],["8249","single left-pointing angle quotation mark"],["8250","single right-pointing angle quotation mark"],["171","left pointing guillemet"],["187","right pointing guillemet"],["8216","left single quotation mark"],["8217","right single quotation mark"],["8220","left double quotation mark"],["8221","right double quotation mark"],["8218","single low-9 quotation mark"],["8222","double low-9 quotation mark"],["60","less-than sign"],["62","greater-than sign"],["8804","less-than or equal to"],["8805","greater-than or equal to"],["8211","en dash"],["8212","em dash"],["175","macron"],["8254","overline"],["164","currency sign"],["166","broken bar"],["168","diaeresis"],["161","inverted exclamation mark"],["191","turned question mark"],["710","circumflex accent"],["732","small tilde"],["176","degree sign"],["8722","minus sign"],["177","plus-minus sign"],["247","division sign"],["8260","fraction slash"],["215","multiplication sign"],["185","superscript one"],["178","superscript two"],["179","superscript three"],["188","fraction one quarter"],["189","fraction one half"],["190","fraction three quarters"],["402","function / florin"],["8747","integral"],["8721","n-ary sumation"],["8734","infinity"],["8730","square root"],["8764","similar to"],["8773","approximately equal to"],["8776","almost equal to"],["8800","not equal to"],["8801","identical to"],["8712","element of"],["8713","not an element of"],["8715","contains as member"],["8719","n-ary product"],["8743","logical and"],["8744","logical or"],["172","not sign"],["8745","intersection"],["8746","union"],["8706","partial differential"],["8704","for all"],["8707","there exists"],["8709","diameter"],["8711","backward difference"],["8727","asterisk operator"],["8733","proportional to"],["8736","angle"],["180","acute accent"],["184","cedilla"],["170","feminine ordinal indicator"],["186","masculine ordinal indicator"],["8224","dagger"],["8225","double dagger"],["192","A - grave"],["193","A - acute"],["194","A - circumflex"],["195","A - tilde"],["196","A - diaeresis"],["197","A - ring above"],["256","A - macron"],["198","ligature AE"],["199","C - cedilla"],["200","E - grave"],["201","E - acute"],["202","E - circumflex"],["203","E - diaeresis"],["274","E - macron"],["204","I - grave"],["205","I - acute"],["206","I - circumflex"],["207","I - diaeresis"],["298","I - macron"],["208","ETH"],["209","N - tilde"],["210","O - grave"],["211","O - acute"],["212","O - circumflex"],["213","O - tilde"],["214","O - diaeresis"],["216","O - slash"],["332","O - macron"],["338","ligature OE"],["352","S - caron"],["217","U - grave"],["218","U - acute"],["219","U - circumflex"],["220","U - diaeresis"],["362","U - macron"],["221","Y - acute"],["376","Y - diaeresis"],["562","Y - macron"],["222","THORN"],["224","a - grave"],["225","a - acute"],["226","a - circumflex"],["227","a - tilde"],["228","a - diaeresis"],["229","a - ring above"],["257","a - macron"],["230","ligature ae"],["231","c - cedilla"],["232","e - grave"],["233","e - acute"],["234","e - circumflex"],["235","e - diaeresis"],["275","e - macron"],["236","i - grave"],["237","i - acute"],["238","i - circumflex"],["239","i - diaeresis"],["299","i - macron"],["240","eth"],["241","n - tilde"],["242","o - grave"],["243","o - acute"],["244","o - circumflex"],["245","o - tilde"],["246","o - diaeresis"],["248","o slash"],["333","o macron"],["339","ligature oe"],["353","s - caron"],["249","u - grave"],["250","u - acute"],["251","u - circumflex"],["252","u - diaeresis"],["363","u - macron"],["253","y - acute"],["254","thorn"],["255","y - diaeresis"],["563","y - macron"],["913","Alpha"],["914","Beta"],["915","Gamma"],["916","Delta"],["917","Epsilon"],["918","Zeta"],["919","Eta"],["920","Theta"],["921","Iota"],["922","Kappa"],["923","Lambda"],["924","Mu"],["925","Nu"],["926","Xi"],["927","Omicron"],["928","Pi"],["929","Rho"],["931","Sigma"],["932","Tau"],["933","Upsilon"],["934","Phi"],["935","Chi"],["936","Psi"],["937","Omega"],["945","alpha"],["946","beta"],["947","gamma"],["948","delta"],["949","epsilon"],["950","zeta"],["951","eta"],["952","theta"],["953","iota"],["954","kappa"],["955","lambda"],["956","mu"],["957","nu"],["958","xi"],["959","omicron"],["960","pi"],["961","rho"],["962","final sigma"],["963","sigma"],["964","tau"],["965","upsilon"],["966","phi"],["967","chi"],["968","psi"],["969","omega"],["8501","alef symbol"],["982","pi symbol"],["8476","real part symbol"],["978","upsilon - hook symbol"],["8472","Weierstrass p"],["8465","imaginary part"],["8592","leftwards arrow"],["8593","upwards arrow"],["8594","rightwards arrow"],["8595","downwards arrow"],["8596","left right arrow"],["8629","carriage return"],["8656","leftwards double arrow"],["8657","upwards double arrow"],["8658","rightwards double arrow"],["8659","downwards double arrow"],["8660","left right double arrow"],["8756","therefore"],["8834","subset of"],["8835","superset of"],["8836","not a subset of"],["8838","subset of or equal to"],["8839","superset of or equal to"],["8853","circled plus"],["8855","circled times"],["8869","perpendicular"],["8901","dot operator"],["8968","left ceiling"],["8969","right ceiling"],["8970","left floor"],["8971","right floor"],["9001","left-pointing angle bracket"],["9002","right-pointing angle bracket"],["9674","lozenge"],["9824","black spade suit"],["9827","black club suit"],["9829","black heart suit"],["9830","black diamond suit"],["8194","en space"],["8195","em space"],["8201","thin space"],["8204","zero width non-joiner"],["8205","zero width joiner"],["8206","left-to-right mark"],["8207","right-to-left mark"]]}function c(a){return tinymce.util.Tools.grep(a,function(a){return i(a)&&2==a.length})}function d(a){return i(a)?[].concat(c(a)):"function"==typeof a?a():[]}function e(b){var c=a.settings;return c.charmap&&(b=d(c.charmap)),c.charmap_append?[].concat(b).concat(d(c.charmap_append)):b}function f(){return e(b())}function g(b){a.fire("insertCustomChar",{chr:b}).chr,a.execCommand("mceInsertContent",!1,b)}function h(){function b(a){for(;a;){if("TD"==a.nodeName)return a;a=a.parentNode}}var c,d,e,h;c='';var i=f(),j=Math.min(i.length,25),k=Math.ceil(i.length/j);for(e=0;k>e;e++){for(c+="",d=0;j>d;d++){var l=e*j+d;if(l
    '+(m?String.fromCharCode(parseInt(m[0],10)):" ")+"
    "}else c+="
    "}c+="";var n={type:"container",html:c,onclick:function(a){var c=a.target;/^(TD|DIV)$/.test(c.nodeName)&&b(c).firstChild&&(g(tinymce.trim(c.innerText||c.textContent)),a.ctrlKey||h.close())},onmouseover:function(a){var c=b(a.target);c&&c.firstChild?(h.find("#preview").text(c.firstChild.firstChild.data),h.find("#previewTitle").text(c.title)):(h.find("#preview").text(" "),h.find("#previewTitle").text(" "))}};h=a.windowManager.open({title:"Special character",spacing:10,padding:10,items:[n,{type:"container",layout:"flex",direction:"column",align:"center",spacing:5,minWidth:160,minHeight:160,items:[{type:"label",name:"preview",text:" ",style:"font-size: 40px; text-align: center",border:1,minWidth:140,minHeight:80},{type:"label",name:"previewTitle",text:" ",style:"text-align: center",border:1,minWidth:140,minHeight:80}]}],buttons:[{text:"Close",onclick:function(){h.close()}}]})}var i=tinymce.util.Tools.isArray;return a.addCommand("mceShowCharmap",h),a.addButton("charmap",{icon:"charmap",tooltip:"Special character",cmd:"mceShowCharmap"}),a.addMenuItem("charmap",{icon:"charmap",text:"Special character",cmd:"mceShowCharmap",context:"insert"}),{getCharMap:f,insertChar:g}}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/code/plugin.min.js b/www/js/tinymce/plugins/code/plugin.min.js new file mode 100644 index 0000000..d6331f8 --- /dev/null +++ b/www/js/tinymce/plugins/code/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("code",function(a){function b(){var b=a.windowManager.open({title:"Source code",body:{type:"textbox",name:"code",multiline:!0,minWidth:a.getParam("code_dialog_width",600),minHeight:a.getParam("code_dialog_height",Math.min(tinymce.DOM.getViewPort().h-200,500)),spellcheck:!1,style:"direction: ltr; text-align: left"},onSubmit:function(b){a.focus(),a.undoManager.transact(function(){a.setContent(b.data.code)}),a.selection.setCursorLocation(),a.nodeChanged()}});b.find("#code").value(a.getContent({source_view:!0}))}a.addCommand("mceCodeEditor",b),a.addButton("code",{icon:"code",tooltip:"Source code",onclick:b}),a.addMenuItem("code",{icon:"code",text:"Source code",context:"tools",onclick:b})}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/codesample/css/prism.css b/www/js/tinymce/plugins/codesample/css/prism.css new file mode 100644 index 0000000..28622b5 --- /dev/null +++ b/www/js/tinymce/plugins/codesample/css/prism.css @@ -0,0 +1,138 @@ +/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + color: black; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + direction: ltr; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #a67f59; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + diff --git a/www/js/tinymce/plugins/codesample/plugin.min.js b/www/js/tinymce/plugins/codesample/plugin.min.js new file mode 100644 index 0000000..6cdbf9b --- /dev/null +++ b/www/js/tinymce/plugins/codesample/plugin.min.js @@ -0,0 +1 @@ +!function(a,b){"use strict";function c(a,b){for(var c,d=[],f=0;fa.length)break a;if(!(q instanceof e)){k.lastIndex=0;var r=k.exec(q);if(r){m&&(n=r[1].length);var s=r.index-1+n,r=r[0].slice(n),t=r.length,u=s+t,v=q.slice(0,s+1),w=q.slice(u+1),x=[p,1];v&&x.push(v);var y=new e(h,l?c.tokenize(r,l):r,o);x.push(y),w&&x.push(w),Array.prototype.splice.apply(f,x)}}}}}return f},hooks:{all:{},add:function(a,b){var d=c.hooks.all;d[a]=d[a]||[],d[a].push(b)},run:function(a,b){var d=c.hooks.all[a];if(d&&d.length)for(var e,f=0;e=d[f++];)e(b)}}},d=c.Token=function(a,b,c){this.type=a,this.content=b,this.alias=c};return d.stringify=function(a,b,e){if("string"==typeof a)return a;if("Array"===c.util.type(a))return a.map(function(c){return d.stringify(c,b,a)}).join("");var f={type:a.type,content:d.stringify(a.content,b,e),tag:"span",classes:["token",a.type],attributes:{},language:b,parent:e};if("comment"==f.type&&(f.attributes.spellcheck="true"),a.alias){var g="Array"===c.util.type(a.alias)?a.alias:[a.alias];Array.prototype.push.apply(f.classes,g)}c.hooks.run("wrap",f);var h="";for(var i in f.attributes)h+=(h?" ":"")+i+'="'+(f.attributes[i]||"")+'"';return"<"+f.tag+' class="'+f.classes.join(" ")+'" '+h+">"+f.content+""},b.document?void 0:b.addEventListener?(b.addEventListener("message",function(a){var d=JSON.parse(a.data),e=d.language,f=d.code,g=d.immediateClose;b.postMessage(c.highlight(f,c.languages[e],e)),g&&b.close()},!1),b.Prism):b.Prism}();return"undefined"!=typeof module&&module.exports&&(module.exports=c),"undefined"!=typeof global&&(global.Prism=c),c.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[^\s>\/=.]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},c.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),c.languages.xml=c.languages.markup,c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,c.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},c.languages.css.atrule.inside.rest=c.util.clone(c.languages.css),c.languages.markup&&(c.languages.insertBefore("markup","tag",{style:{pattern:/[\w\W]*?<\/style>/i,inside:{tag:{pattern:/|<\/style>/i,inside:c.languages.markup.tag.inside},rest:c.languages.css},alias:"language-css"}}),c.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:c.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:c.languages.css}},alias:"language-css"}},c.languages.markup.tag)),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),c.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),c.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/i,inside:{tag:{pattern:/|<\/script>/i,inside:c.languages.markup.tag.inside},rest:c.languages.javascript},alias:"language-javascript"}}),c.languages.js=c.languages.javascript,c.languages.c=c.languages.extend("clike",{keyword:/\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,operator:/\-[>-]?|\+\+?|!=?|<>?=?|==?|&&?|\|?\||[~^%?*\/]/,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)[ful]*\b/i}),c.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+([^\r\n\\]|\\.|\\(?:\r\n?|\n))*/im,lookbehind:!0,alias:"property",inside:{string:{pattern:/(#\s*include\s*)(<.+?>|("|')(\\?.)+?\3)/,lookbehind:!0}}}}),delete c.languages.c["class-name"],delete c.languages.c["boolean"],c.languages.csharp=c.languages.extend("clike",{keyword:/\b(abstract|as|async|await|base|bool|break|byte|case|catch|char|checked|class|const|continue|decimal|default|delegate|do|double|else|enum|event|explicit|extern|false|finally|fixed|float|for|foreach|goto|if|implicit|in|int|interface|internal|is|lock|long|namespace|new|null|object|operator|out|override|params|private|protected|public|readonly|ref|return|sbyte|sealed|short|sizeof|stackalloc|static|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|unsafe|ushort|using|virtual|void|volatile|while|add|alias|ascending|async|await|descending|dynamic|from|get|global|group|into|join|let|orderby|partial|remove|select|set|value|var|where|yield)\b/,string:[/@("|')(\1\1|\\\1|\\?(?!\1)[\s\S])*\1/,/("|')(\\?.)*?\1/],number:/\b-?(0x[\da-f]+|\d*\.?\d+)\b/i}),c.languages.insertBefore("csharp","keyword",{preprocessor:{pattern:/(^\s*)#.*/m,lookbehind:!0}}),c.languages.cpp=c.languages.extend("c",{keyword:/\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,"boolean":/\b(true|false)\b/,operator:/[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|&{1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/}),c.languages.insertBefore("cpp","keyword",{"class-name":{pattern:/(class\s+)[a-z0-9_]+/i,lookbehind:!0}}),c.languages.java=c.languages.extend("clike",{keyword:/\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/,number:/\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+(?:e[+-]?\d+)?[df]?\b/i,operator:{pattern:/(^|[^.])(?:\+[+=]?|-[-=]?|!=?|<>?>?=?|==?|&[&=]?|\|[|=]?|\*=?|\/=?|%=?|\^=?|[?:~])/m,lookbehind:!0}}),c.languages.php=c.languages.extend("clike",{keyword:/\b(and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|private|protected|parent|throw|null|echo|print|trait|namespace|final|yield|goto|instanceof|finally|try|catch)\b/i,constant:/\b[A-Z0-9_]{2,}\b/,comment:{pattern:/(^|[^\\])(?:\/\*[\w\W]*?\*\/|\/\/.*)/,lookbehind:!0}}),c.languages.insertBefore("php","class-name",{"shell-comment":{pattern:/(^|[^\\])#.*/,lookbehind:!0,alias:"comment"}}),c.languages.insertBefore("php","keyword",{delimiter:/\?>|<\?(?:php)?/i,variable:/\$\w+\b/i,"package":{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/,lookbehind:!0,inside:{punctuation:/\\/}}}),c.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/,lookbehind:!0}}),c.languages.markup&&(c.hooks.add("before-highlight",function(a){"php"===a.language&&(a.tokenStack=[],a.backupCode=a.code,a.code=a.code.replace(/(?:<\?php|<\?)[\w\W]*?(?:\?>)/gi,function(b){return a.tokenStack.push(b),"{{{PHP"+a.tokenStack.length+"}}}"}))}),c.hooks.add("before-insert",function(a){"php"===a.language&&(a.code=a.backupCode,delete a.backupCode)}),c.hooks.add("after-highlight",function(a){if("php"===a.language){for(var b,d=0;b=a.tokenStack[d];d++)a.highlightedCode=a.highlightedCode.replace("{{{PHP"+(d+1)+"}}}",c.highlight(b,a.grammar,"php").replace(/\$/g,"$$$$"));a.element.innerHTML=a.highlightedCode}}),c.hooks.add("wrap",function(a){"php"===a.language&&"markup"===a.type&&(a.content=a.content.replace(/(\{\{\{PHP[0-9]+\}\}\})/g,'$1'))}),c.languages.insertBefore("php","comment",{markup:{pattern:/<[^?]\/?(.*?)>/,inside:c.languages.markup},php:/\{\{\{PHP[0-9]+\}\}\}/})),c.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},string:/"""[\s\S]+?"""|'''[\s\S]+?'''|("|')(?:\\?.)*?\1/,"function":{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_][a-zA-Z0-9_]*(?=\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)[a-z0-9_]+/i,lookbehind:!0},keyword:/\b(?:as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/,"boolean":/\b(?:True|False)\b/,number:/\b-?(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/,punctuation:/[{}[\];(),.:]/},function(a){a.languages.ruby=a.languages.extend("clike",{comment:/#(?!\{[^\r\n]*?\}).*/,keyword:/\b(alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|false|for|if|in|module|new|next|nil|not|or|raise|redo|require|rescue|retry|return|self|super|then|throw|true|undef|unless|until|when|while|yield)\b/});var b={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:a.util.clone(a.languages.ruby)}};a.languages.insertBefore("ruby","keyword",{regex:[{pattern:/%r([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/,inside:{interpolation:b}},{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}],variable:/[@$]+[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/,symbol:/:[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/}),a.languages.insertBefore("ruby","number",{builtin:/\b(Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|File|Fixnum|Fload|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z][a-zA-Z_0-9]*(?:[?!]|\b)/}),a.languages.ruby.string=[{pattern:/%[qQiIwWxs]?([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/,inside:{interpolation:b}},{pattern:/("|')(#\{[^}]+\}|\\(?:\r?\n|\r)|\\?.)*?\1/,inside:{interpolation:b}}]}(c),c}),d("tinymce/codesampleplugin/Utils",[],function(){function a(a){return a&&"PRE"==a.nodeName&&-1!==a.className.indexOf("language-")}function b(a){return function(b,c){return a(c)}}return{isCodeSample:a,trimArg:b}}),d("tinymce/codesampleplugin/Dialog",["tinymce/dom/DOMUtils","tinymce/codesampleplugin/Utils","tinymce/codesampleplugin/Prism"],function(a,b,c){function d(a){var b=[{text:"HTML/XML",value:"markup"},{text:"JavaScript",value:"javascript"},{text:"CSS",value:"css"},{text:"PHP",value:"php"},{text:"Ruby",value:"ruby"},{text:"Python",value:"python"},{text:"Java",value:"java"},{text:"C",value:"c"},{text:"C#",value:"csharp"},{text:"C++",value:"cpp"}],c=a.settings.codesample_languages;return c?c:b}function e(a,b,d){a.undoManager.transact(function(){var e=f(a);d=i.encode(d),e?(a.dom.setAttrib(e,"class","language-"+b),e.innerHTML=d,c.highlightElement(e),a.selection.select(e)):(a.insertContent('

    '+d+"
    "),a.selection.select(a.$("#__new").removeAttr("id")[0]))})}function f(a){var c=a.selection.getNode();return b.isCodeSample(c)?c:null}function g(a){var b=f(a);return b?b.textContent:""}function h(a){var b,c=f(a);return c?(b=c.className.match(/language-(\w+)/),b?b[1]:""):""}var i=a.DOM;return{open:function(a){a.windowManager.open({title:"Insert/Edit code sample",minWidth:Math.min(i.getViewPort().w,a.getParam("codesample_dialog_width",800)),minHeight:Math.min(i.getViewPort().h,a.getParam("codesample_dialog_height",650)),layout:"flex",direction:"column",align:"stretch",body:[{type:"listbox",name:"language",label:"Language",maxWidth:200,value:h(a),values:d(a)},{type:"textbox",name:"code",multiline:!0,spellcheck:!1,ariaLabel:"Code view",flex:1,style:"direction: ltr; text-align: left",classes:"monospace",value:g(a),autofocus:!0}],onSubmit:function(b){e(a,b.data.language,b.data.code)}})}}}),d("tinymce/codesampleplugin/Plugin",["tinymce/Env","tinymce/PluginManager","tinymce/codesampleplugin/Prism","tinymce/codesampleplugin/Dialog","tinymce/codesampleplugin/Utils"],function(a,b,c,d,e){var f,g=e.trimArg;b.add("codesample",function(b,h){function i(){var a;b.inline&&f||!b.inline&&j||(b.inline?f=!0:j=!0,a=b.dom.create("link",{rel:"stylesheet",href:h+"/css/prism.css"}),b.getDoc().getElementsByTagName("head")[0].appendChild(a))}var j,k=b.$;a.ceFalse&&(b.on("PreProcess",function(a){k("pre[contenteditable=false]",a.node).filter(g(e.isCodeSample)).each(function(a,b){var c=k(b),d=b.textContent;c.attr("class",k.trim(c.attr("class"))),c.removeAttr("contentEditable"),c.empty().append(k("").each(function(){this.textContent=d}))})}),b.on("SetContent",function(){var a=k("pre").filter(g(e.isCodeSample)).filter(function(a,b){return"false"!==b.contentEditable});a.length&&b.undoManager.transact(function(){a.each(function(a,d){k(d).find("br").each(function(a,c){c.parentNode.replaceChild(b.getDoc().createTextNode("\n"),c)}),d.contentEditable=!1,d.innerHTML=b.dom.encode(d.textContent),c.highlightElement(d),d.className=k.trim(d.className)})})}),b.addCommand("codesample",function(){d.open(b)}),b.addButton("codesample",{cmd:"codesample",title:"Insert/Edit code sample"}),b.on("init",i))})}),f(["tinymce/codesampleplugin/Prism","tinymce/codesampleplugin/Utils","tinymce/codesampleplugin/Dialog","tinymce/codesampleplugin/Plugin"])}(this); \ No newline at end of file diff --git a/www/js/tinymce/plugins/colorpicker/plugin.min.js b/www/js/tinymce/plugins/colorpicker/plugin.min.js new file mode 100644 index 0000000..66ea69c --- /dev/null +++ b/www/js/tinymce/plugins/colorpicker/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("colorpicker",function(a){function b(b,c){function d(a){var b=new tinymce.util.Color(a),c=b.toRgb();f.fromJSON({r:c.r,g:c.g,b:c.b,hex:b.toHex().substr(1)}),e(b.toHex())}function e(a){f.find("#preview")[0].getEl().style.background=a}var f=a.windowManager.open({title:"Color",items:{type:"container",layout:"flex",direction:"row",align:"stretch",padding:5,spacing:10,items:[{type:"colorpicker",value:c,onchange:function(){var a=this.rgb();f&&(f.find("#r").value(a.r),f.find("#g").value(a.g),f.find("#b").value(a.b),f.find("#hex").value(this.value().substr(1)),e(this.value()))}},{type:"form",padding:0,labelGap:5,defaults:{type:"textbox",size:7,value:"0",flex:1,spellcheck:!1,onchange:function(){var a,b,c=f.find("colorpicker")[0];return a=this.name(),b=this.value(),"hex"==a?(b="#"+b,d(b),void c.value(b)):(b={r:f.find("#r").value(),g:f.find("#g").value(),b:f.find("#b").value()},c.value(b),void d(b))}},items:[{name:"r",label:"R",autofocus:1},{name:"g",label:"G"},{name:"b",label:"B"},{name:"hex",label:"#",value:"000000"},{name:"preview",type:"container",border:1}]}]},onSubmit:function(){b("#"+this.toJSON().hex)}});d(c)}a.settings.color_picker_callback||(a.settings.color_picker_callback=b)}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/contextmenu/plugin.min.js b/www/js/tinymce/plugins/contextmenu/plugin.min.js new file mode 100644 index 0000000..13cd3d2 --- /dev/null +++ b/www/js/tinymce/plugins/contextmenu/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("contextmenu",function(a){var b,c=a.settings.contextmenu_never_use_native,d=function(a){return a.ctrlKey&&!c},e=function(){return tinymce.Env.mac&&tinymce.Env.webkit};a.on("mousedown",function(b){e()&&2===b.button&&!d(b)&&a.selection.isCollapsed()&&a.once("contextmenu",function(b){a.selection.placeCaretAt(b.clientX,b.clientY)})}),a.on("contextmenu",function(c){var e;if(!d(c)){if(c.preventDefault(),e=a.settings.contextmenu||"link image inserttable | cell row column deletetable",b)b.show();else{var f=[];tinymce.each(e.split(/[ ,]/),function(b){var c=a.menuItems[b];"|"==b&&(c={text:b}),c&&(c.shortcut="",f.push(c))});for(var g=0;g
    "}var d=[["cool","cry","embarassed","foot-in-mouth"],["frown","innocent","kiss","laughing"],["money-mouth","sealed","smile","surprised"],["tongue-out","undecided","wink","yell"]];a.addButton("emoticons",{type:"panelbutton",panel:{role:"application",autohide:!0,html:c,onclick:function(b){var c=a.dom.getParent(b.target,"a");c&&(a.insertContent(''+c.getAttribute('),this.hide())}},tooltip:"Emoticons"})}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/example/dialog.html b/www/js/tinymce/plugins/example/dialog.html new file mode 100644 index 0000000..565f06f --- /dev/null +++ b/www/js/tinymce/plugins/example/dialog.html @@ -0,0 +1,8 @@ + + + +

    Custom dialog

    + Input some text: + + + \ No newline at end of file diff --git a/www/js/tinymce/plugins/example/plugin.min.js b/www/js/tinymce/plugins/example/plugin.min.js new file mode 100644 index 0000000..88687c7 --- /dev/null +++ b/www/js/tinymce/plugins/example/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("example",function(a,b){a.addButton("example",{text:"My button",icon:!1,onclick:function(){a.windowManager.open({title:"Example plugin",body:[{type:"textbox",name:"title",label:"Title"}],onsubmit:function(b){a.insertContent("Title: "+b.data.title)}})}}),a.addMenuItem("example",{text:"Example plugin",context:"tools",onclick:function(){a.windowManager.open({title:"TinyMCE site",url:b+"/dialog.html",width:600,height:400,buttons:[{text:"Insert",onclick:function(){var b=a.windowManager.getWindows()[0];a.insertContent(b.getContentWindow().document.getElementById("content").value),b.close()}},{text:"Close",onclick:"close"}]})}})}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/example_dependency/plugin.min.js b/www/js/tinymce/plugins/example_dependency/plugin.min.js new file mode 100644 index 0000000..e61bf47 --- /dev/null +++ b/www/js/tinymce/plugins/example_dependency/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("example_dependency",function(){},["example"]); \ No newline at end of file diff --git a/www/js/tinymce/plugins/fullpage/plugin.min.js b/www/js/tinymce/plugins/fullpage/plugin.min.js new file mode 100644 index 0000000..1ea5c36 --- /dev/null +++ b/www/js/tinymce/plugins/fullpage/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("fullpage",function(a){function b(){var b=c();a.windowManager.open({title:"Document properties",data:b,defaults:{type:"textbox",size:40},body:[{name:"title",label:"Title"},{name:"keywords",label:"Keywords"},{name:"description",label:"Description"},{name:"robots",label:"Robots"},{name:"author",label:"Author"},{name:"docencoding",label:"Encoding"}],onSubmit:function(a){d(tinymce.extend(b,a.data))}})}function c(){function b(a,b){var c=a.attr(b);return c||""}var c,d,f=e(),g={};return g.fontface=a.getParam("fullpage_default_fontface",""),g.fontsize=a.getParam("fullpage_default_fontsize",""),c=f.firstChild,7==c.type&&(g.xml_pi=!0,d=/encoding="([^"]+)"/.exec(c.value),d&&(g.docencoding=d[1])),c=f.getAll("#doctype")[0],c&&(g.doctype=""),c=f.getAll("title")[0],c&&c.firstChild&&(g.title=c.firstChild.value),k(f.getAll("meta"),function(a){var b,c=a.attr("name"),d=a.attr("http-equiv");c?g[c.toLowerCase()]=a.attr("content"):"Content-Type"==d&&(b=/charset\s*=\s*(.*)\s*/gi.exec(a.attr("content")),b&&(g.docencoding=b[1]))}),c=f.getAll("html")[0],c&&(g.langcode=b(c,"lang")||b(c,"xml:lang")),g.stylesheets=[],tinymce.each(f.getAll("link"),function(a){"stylesheet"==a.attr("rel")&&g.stylesheets.push(a.attr("href"))}),c=f.getAll("body")[0],c&&(g.langdir=b(c,"dir"),g.style=b(c,"style"),g.visited_color=b(c,"vlink"),g.link_color=b(c,"link"),g.active_color=b(c,"alink")),g}function d(b){function c(a,b,c){a.attr(b,c?c:void 0)}function d(a){g.firstChild?g.insert(a,g.firstChild):g.append(a)}var f,g,h,j,m,n=a.dom;f=e(),g=f.getAll("head")[0],g||(j=f.getAll("html")[0],g=new l("head",1),j.firstChild?j.insert(g,j.firstChild,!0):j.append(g)),j=f.firstChild,b.xml_pi?(m='version="1.0"',b.docencoding&&(m+=' encoding="'+b.docencoding+'"'),7!=j.type&&(j=new l("xml",7),f.insert(j,f.firstChild,!0)),j.value=m):j&&7==j.type&&j.remove(),j=f.getAll("#doctype")[0],b.doctype?(j||(j=new l("#doctype",10),b.xml_pi?f.insert(j,f.firstChild):d(j)),j.value=b.doctype.substring(9,b.doctype.length-1)):j&&j.remove(),j=null,k(f.getAll("meta"),function(a){"Content-Type"==a.attr("http-equiv")&&(j=a)}),b.docencoding?(j||(j=new l("meta",1),j.attr("http-equiv","Content-Type"),j.shortEnded=!0,d(j)),j.attr("content","text/html; charset="+b.docencoding)):j&&j.remove(),j=f.getAll("title")[0],b.title?(j?j.empty():(j=new l("title",1),d(j)),j.append(new l("#text",3)).value=b.title):j&&j.remove(),k("keywords,description,author,copyright,robots".split(","),function(a){var c,e,g=f.getAll("meta"),h=b[a];for(c=0;c"))}function e(){return new tinymce.html.DomParser({validate:!1,root_name:"#document"}).parse(i)}function f(b){function c(a){return a.replace(/<\/?[A-Z]+/g,function(a){return a.toLowerCase()})}var d,f,h,l,m=b.content,n="",o=a.dom;if(!b.selection&&!("raw"==b.format&&i||b.source_view&&a.getParam("fullpage_hide_in_source_view"))){0!==m.length||b.source_view||(m=tinymce.trim(i)+"\n"+tinymce.trim(m)+"\n"+tinymce.trim(j)),m=m.replace(/<(\/?)BODY/gi,"<$1body"),d=m.indexOf("",d),i=c(m.substring(0,d+1)),f=m.indexOf("\n"),h=e(),k(h.getAll("style"),function(a){a.firstChild&&(n+=a.firstChild.value)}),l=h.getAll("body")[0],l&&o.setAttribs(a.getBody(),{style:l.attr("style")||"",dir:l.attr("dir")||"",vLink:l.attr("vlink")||"",link:l.attr("link")||"",aLink:l.attr("alink")||""}),o.remove("fullpage_styles");var p=a.getDoc().getElementsByTagName("head")[0];n&&(o.add(p,"style",{id:"fullpage_styles"},n),l=o.get("fullpage_styles"),l.styleSheet&&(l.styleSheet.cssText=n));var q={};tinymce.each(p.getElementsByTagName("link"),function(a){"stylesheet"==a.rel&&a.getAttribute("data-mce-fullpage")&&(q[a.href]=a)}),tinymce.each(h.getAll("link"),function(a){var b=a.attr("href");q[b]||"stylesheet"!=a.attr("rel")||o.add(p,"link",{rel:"stylesheet",text:"text/css",href:b,"data-mce-fullpage":"1"}),delete q[b]}),tinymce.each(q,function(a){a.parentNode.removeChild(a)})}}function g(){var b,c="",d="";return a.getParam("fullpage_default_xml_pi")&&(c+='\n'),c+=a.getParam("fullpage_default_doctype",""),c+="\n\n\n",(b=a.getParam("fullpage_default_title"))&&(c+=""+b+"\n"),(b=a.getParam("fullpage_default_encoding"))&&(c+='\n'),(b=a.getParam("fullpage_default_font_family"))&&(d+="font-family: "+b+";"),(b=a.getParam("fullpage_default_font_size"))&&(d+="font-size: "+b+";"),(b=a.getParam("fullpage_default_text_color"))&&(d+="color: "+b+";"),c+="\n\n"}function h(b){b.selection||b.source_view&&a.getParam("fullpage_hide_in_source_view")||(b.content=tinymce.trim(i)+"\n"+tinymce.trim(b.content)+"\n"+tinymce.trim(j))}var i,j,k=tinymce.each,l=tinymce.html.Node;a.addCommand("mceFullPageProperties",b),a.addButton("fullpage",{title:"Document properties",cmd:"mceFullPageProperties"}),a.addMenuItem("fullpage",{text:"Document properties",cmd:"mceFullPageProperties",context:"file"}),a.on("BeforeSetContent",f),a.on("GetContent",h)}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/fullscreen/plugin.min.js b/www/js/tinymce/plugins/fullscreen/plugin.min.js new file mode 100644 index 0000000..8edbe13 --- /dev/null +++ b/www/js/tinymce/plugins/fullscreen/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("fullscreen",function(a){function b(){var a,b,c=window,d=document,e=d.body;return e.offsetWidth&&(a=e.offsetWidth,b=e.offsetHeight),c.innerWidth&&c.innerHeight&&(a=c.innerWidth,b=c.innerHeight),{w:a,h:b}}function c(){var a=tinymce.DOM.getViewPort();return{x:a.x,y:a.y}}function d(a){scrollTo(a.x,a.y)}function e(){function e(){m.setStyle(p,"height",b().h-(o.clientHeight-p.clientHeight))}var n,o,p,q,r=document.body,s=document.documentElement;l=!l,o=a.getContainer(),n=o.style,p=a.getContentAreaContainer().firstChild,q=p.style,l?(k=c(),f=q.width,g=q.height,q.width=q.height="100%",i=n.width,j=n.height,n.width=n.height="",m.addClass(r,"mce-fullscreen"),m.addClass(s,"mce-fullscreen"),m.addClass(o,"mce-fullscreen"),m.bind(window,"resize",e),e(),h=e):(q.width=f,q.height=g,i&&(n.width=i),j&&(n.height=j),m.removeClass(r,"mce-fullscreen"),m.removeClass(s,"mce-fullscreen"),m.removeClass(o,"mce-fullscreen"),m.unbind(window,"resize",h),d(k)),a.fire("FullscreenStateChanged",{state:l})}var f,g,h,i,j,k,l=!1,m=tinymce.DOM;return a.settings.inline?void 0:(a.on("init",function(){a.addShortcut("Ctrl+Shift+F","",e)}),a.on("remove",function(){h&&m.unbind(window,"resize",h)}),a.addCommand("mceFullScreen",e),a.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Ctrl+Shift+F",selectable:!0,onClick:function(){e(),a.focus()},onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})},context:"view"}),a.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Ctrl+Shift+F",onClick:e,onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})}}),{isFullscreen:function(){return l}})}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/hr/plugin.min.js b/www/js/tinymce/plugins/hr/plugin.min.js new file mode 100644 index 0000000..25abb0c --- /dev/null +++ b/www/js/tinymce/plugins/hr/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("hr",function(a){a.addCommand("InsertHorizontalRule",function(){a.execCommand("mceInsertContent",!1,"
    ")}),a.addButton("hr",{icon:"hr",tooltip:"Horizontal line",cmd:"InsertHorizontalRule"}),a.addMenuItem("hr",{icon:"hr",text:"Horizontal line",cmd:"InsertHorizontalRule",context:"insert"})}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/image/plugin.min.js b/www/js/tinymce/plugins/image/plugin.min.js new file mode 100644 index 0000000..90de1a4 --- /dev/null +++ b/www/js/tinymce/plugins/image/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("image",function(a){function b(a,b){function c(a,c){d.parentNode&&d.parentNode.removeChild(d),b({width:a,height:c})}var d=document.createElement("img");d.onload=function(){c(Math.max(d.width,d.clientWidth),Math.max(d.height,d.clientHeight))},d.onerror=function(){c()};var e=d.style;e.visibility="hidden",e.position="fixed",e.bottom=e.left=0,e.width=e.height="auto",document.body.appendChild(d),d.src=a}function c(a,b,c){function d(a,c){return c=c||[],tinymce.each(a,function(a){var e={text:a.text||a.title};a.menu?e.menu=d(a.menu):(e.value=a.value,b(e)),c.push(e)}),c}return d(a,c||[])}function d(b){return function(){var c=a.settings.image_list;"string"==typeof c?tinymce.util.XHR.send({url:c,success:function(a){b(tinymce.util.JSON.parse(a))}}):"function"==typeof c?c(b):b(c)}}function e(d){function e(){var a,b,c,d;a=l.find("#width")[0],b=l.find("#height")[0],a&&b&&(c=a.value(),d=b.value(),l.find("#constrain")[0].checked()&&o&&p&&c&&d&&(o!=c?(d=Math.round(c/o*d),isNaN(d)||b.value(d)):(c=Math.round(d/p*c),isNaN(c)||a.value(c))),o=c,p=d)}function f(){function b(b){function c(){b.onload=b.onerror=null,a.selection&&(a.selection.select(b),a.nodeChanged())}b.onload=function(){s.width||s.height||!u||t.setAttribs(b,{width:b.clientWidth,height:b.clientHeight}),c()},b.onerror=c}var c,d;j(),e(),s=tinymce.extend(s,l.toJSON()),s.alt||(s.alt=""),s.title||(s.title=""),""===s.width&&(s.width=null),""===s.height&&(s.height=null),s.style||(s.style=null),s={src:s.src,alt:s.alt,title:s.title,width:s.width,height:s.height,style:s.style,caption:s.caption,"class":s["class"]},a.undoManager.transact(function(){function e(b){return a.schema.getTextBlockElements()[b.nodeName]}if(!s.src)return void(m&&(t.remove(m),a.focus(),a.nodeChanged()));if(""===s.title&&(s.title=null),m?t.setAttribs(m,s):(s.id="__mcenew",a.focus(),a.selection.setContent(t.createHTML("img",s)),m=t.get("__mcenew"),t.setAttrib(m,"id",null)),a.editorUpload.uploadImagesAuto(),s.caption===!1&&t.is(m.parentNode,"figure.image")&&(c=m.parentNode,t.insertAfter(m,c),t.remove(c)),s.caption!==!0)b(m);else if(!t.is(m.parentNode,"figure.image")){d=m,m=m.cloneNode(!0),c=t.create("figure",{"class":"image"}),c.appendChild(m),c.appendChild(t.create("figcaption",{contentEditable:!0},"Caption")),c.contentEditable=!1;var f=t.getParent(d,e);f?t.split(f,d,c):t.replace(c,d),a.selection.select(c)}})}function g(a){return a&&(a=a.replace(/px$/,"")),a}function h(c){var d,e,f,g=c.meta||{};q&&q.value(a.convertURL(this.value(),"src")),tinymce.each(g,function(a,b){l.find("#"+b).value(a)}),g.width||g.height||(d=a.convertURL(this.value(),"src"),e=a.settings.image_prepend_url,f=new RegExp("^(?:[a-z]+:)?//","i"),e&&!f.test(d)&&d.substring(0,e.length)!==e&&(d=e+d),this.value(d),b(a.documentBaseURI.toAbsolute(this.value()),function(a){a.width&&a.height&&u&&(o=a.width,p=a.height,l.find("#width").value(o),l.find("#height").value(p))}))}function i(a){if(a.margin){var b=a.margin.split(" ");switch(b.length){case 1:a["margin-top"]=a["margin-top"]||b[0],a["margin-right"]=a["margin-right"]||b[0],a["margin-bottom"]=a["margin-bottom"]||b[0],a["margin-left"]=a["margin-left"]||b[0];break;case 2:a["margin-top"]=a["margin-top"]||b[0],a["margin-right"]=a["margin-right"]||b[1],a["margin-bottom"]=a["margin-bottom"]||b[0],a["margin-left"]=a["margin-left"]||b[1];break;case 3:a["margin-top"]=a["margin-top"]||b[0],a["margin-right"]=a["margin-right"]||b[1],a["margin-bottom"]=a["margin-bottom"]||b[2],a["margin-left"]=a["margin-left"]||b[1];break;case 4:a["margin-top"]=a["margin-top"]||b[0],a["margin-right"]=a["margin-right"]||b[1],a["margin-bottom"]=a["margin-bottom"]||b[2],a["margin-left"]=a["margin-left"]||b[3]}delete a.margin}return a}function j(){function b(a){return a.length>0&&/^[0-9]+$/.test(a)&&(a+="px"),a}if(a.settings.image_advtab){var c=l.toJSON(),d=t.parseStyle(c.style);d=i(d),c.vspace&&(d["margin-top"]=d["margin-bottom"]=b(c.vspace)),c.hspace&&(d["margin-left"]=d["margin-right"]=b(c.hspace)),c.border&&(d["border-width"]=b(c.border)),l.find("#style").value(t.serializeStyle(t.parseStyle(t.serializeStyle(d))))}}function k(){if(a.settings.image_advtab){var b=l.toJSON(),c=t.parseStyle(b.style);l.find("#vspace").value(""),l.find("#hspace").value(""),c=i(c),(c["margin-top"]&&c["margin-bottom"]||c["margin-right"]&&c["margin-left"])&&(c["margin-top"]===c["margin-bottom"]?l.find("#vspace").value(g(c["margin-top"])):l.find("#vspace").value(""),c["margin-right"]===c["margin-left"]?l.find("#hspace").value(g(c["margin-right"])):l.find("#hspace").value("")),c["border-width"]&&l.find("#border").value(g(c["border-width"])),l.find("#style").value(t.serializeStyle(t.parseStyle(t.serializeStyle(c))))}}var l,m,n,o,p,q,r,s={},t=a.dom,u=a.settings.image_dimensions!==!1;m=a.selection.getNode(),n=t.getParent(m,"figure.image"),n&&(m=t.select("img",n)[0]),m&&("IMG"!=m.nodeName||m.getAttribute("data-mce-object")||m.getAttribute("data-mce-placeholder"))&&(m=null),m&&(o=t.getAttrib(m,"width"),p=t.getAttrib(m,"height"),s={src:t.getAttrib(m,"src"),alt:t.getAttrib(m,"alt"),title:t.getAttrib(m,"title"),"class":t.getAttrib(m,"class"),width:o,height:p,caption:!!n}),d&&(q={type:"listbox",label:"Image list",values:c(d,function(b){b.value=a.convertURL(b.value||b.url,"src")},[{text:"None",value:""}]),value:s.src&&a.convertURL(s.src,"src"),onselect:function(a){var b=l.find("#alt");(!b.value()||a.lastControl&&b.value()==a.lastControl.text())&&b.value(a.control.text()),l.find("#src").value(a.control.value()).fire("change")},onPostRender:function(){q=this}}),a.settings.image_class_list&&(r={name:"class",type:"listbox",label:"Class",values:c(a.settings.image_class_list,function(b){b.value&&(b.textStyle=function(){return a.formatter.getCssText({inline:"img",classes:[b.value]})})})});var v=[{name:"src",type:"filepicker",filetype:"image",label:"Source",autofocus:!0,onchange:h},q];a.settings.image_description!==!1&&v.push({name:"alt",type:"textbox",label:"Image description"}),a.settings.image_title&&v.push({name:"title",type:"textbox",label:"Image Title"}),u&&v.push({type:"container",label:"Dimensions",layout:"flex",direction:"row",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:5,size:3,onchange:e,ariaLabel:"Width"},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:5,size:3,onchange:e,ariaLabel:"Height"},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}),v.push(r),a.settings.image_caption&&tinymce.Env.ceFalse&&v.push({name:"caption",type:"checkbox",label:"Caption"}),a.settings.image_advtab?(m&&(m.style.marginLeft&&m.style.marginRight&&m.style.marginLeft===m.style.marginRight&&(s.hspace=g(m.style.marginLeft)),m.style.marginTop&&m.style.marginBottom&&m.style.marginTop===m.style.marginBottom&&(s.vspace=g(m.style.marginTop)),m.style.borderWidth&&(s.border=g(m.style.borderWidth)),s.style=a.dom.serializeStyle(a.dom.parseStyle(a.dom.getAttrib(m,"style")))),l=a.windowManager.open({title:"Insert/edit image",data:s,bodyType:"tabpanel",body:[{title:"General",type:"form",items:v},{title:"Advanced",type:"form",pack:"start",items:[{label:"Style",name:"style",type:"textbox",onchange:k},{type:"form",layout:"grid",packV:"start",columns:2,padding:0,alignH:["left","right"],defaults:{type:"textbox",maxWidth:50,onchange:j},items:[{label:"Vertical space",name:"vspace"},{label:"Horizontal space",name:"hspace"},{label:"Border",name:"border"}]}]}],onSubmit:f})):l=a.windowManager.open({title:"Insert/edit image",data:s,body:v,onSubmit:f})}a.on("preInit",function(){function b(a){var b=a.attr("class");return b&&/\bimage\b/.test(b)}function c(a){return function(c){function d(b){b.attr("contenteditable",a?"true":null)}for(var e,f=c.length;f--;)e=c[f],b(e)&&(e.attr("contenteditable",a?"false":null),tinymce.each(e.getAll("figcaption"),d))}}a.parser.addNodeFilter("figure",c(!0)),a.serializer.addNodeFilter("figure",c(!1))}),a.addButton("image",{icon:"image",tooltip:"Insert/edit image",onclick:d(e),stateSelector:"img:not([data-mce-object],[data-mce-placeholder]),figure.image"}),a.addMenuItem("image",{icon:"image",text:"Insert/edit image",onclick:d(e),context:"insert",prependToContext:!0}),a.addCommand("mceImage",d(e))}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/imagetools/plugin.min.js b/www/js/tinymce/plugins/imagetools/plugin.min.js new file mode 100644 index 0000000..4d2ee3d --- /dev/null +++ b/www/js/tinymce/plugins/imagetools/plugin.min.js @@ -0,0 +1 @@ +!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;g>i;++i)h[i]=d(e[i]);var j=f.apply(null,h);if(void 0===j)throw"module ["+b+"] returned undefined";c.instance=j},c=function(b,c,d){if("string"!=typeof b)throw"module id must be a string";if(void 0===c)throw"no dependencies for "+b;if(void 0===d)throw"no definition function for "+b;a[b]={deps:c,defn:d,instance:void 0}},d=function(c){var d=a[c];if(void 0===d)throw"module ["+c+"] was undefined";return void 0===d.instance&&b(c),d.instance},e=function(a,b){for(var c=a.length,e=new Array(c),f=0;c>f;++f)e.push(d(a[f]));b.apply(null,b)},f={};f.bolt={module:{api:{define:c,require:e,demand:d}}};var g=c,h=function(a,b){g(a,[],function(){return b})};h("1",tinymce.PluginManager),h("2",tinymce.Env),h("3",tinymce.util.Promise),h("4",tinymce.util.URI),h("5",tinymce.util.Tools),h("6",tinymce.util.Delay),g("m",[],function(){function a(a,b){return d(document.createElement("canvas"),a,b)}function b(a){return a.getContext("2d")}function c(a){var b=null;try{b=a.getContext("webgl")||a.getContext("experimental-webgl")}catch(c){}return b||(b=null),b}function d(a,b,c){return a.width=b,a.height=c,a}return{create:a,resize:d,get2dContext:b,get3dContext:c}}),g("n",[],function(){function a(a){return a.naturalWidth||a.width}function b(a){return a.naturalHeight||a.height}return{getWidth:a,getHeight:b}}),g("o",[],function(){function a(a,b){return function(){a.apply(b,arguments)}}function b(b){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof b)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],h(b,a(d,this),a(e,this))}function c(a){var b=this;return null===this._state?void this._deferreds.push(a):void i(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function d(b){try{if(b===this)throw new TypeError("A promise cannot be resolved with itself.");if(b&&("object"==typeof b||"function"==typeof b)){var c=b.then;if("function"==typeof c)return void h(a(c,b),a(d,this),a(e,this))}this._state=!0,this._value=b,f.call(this)}catch(g){e.call(this,g)}}function e(a){this._state=!1,this._value=a,f.call(this)}function f(){for(var a=0,b=this._deferreds.length;b>a;a++)c.call(this,this._deferreds[a]);this._deferreds=null}function g(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function h(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}if(window.Promise)return window.Promise;var i=b.immediateFn||"function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},j=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};return b.prototype["catch"]=function(a){return this.then(null,a)},b.prototype.then=function(a,d){var e=this;return new b(function(b,f){c.call(e,new g(a,d,b,f))})},b.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&j(arguments[0])?arguments[0]:arguments);return new b(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;fd;d++)a[d].then(b,c)})},b}),g("p",[],function(){function a(a){var b=document.createElement("a");return b.href=a,b.pathname}function b(b){var c=a(b).split("."),d=c[c.length-1],e={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png"};return d&&(d=d.toLowerCase()),e[d]}return{guessMimeType:b}}),g("e",["o","m","p","n"],function(a,b,c,d){function e(b){return new a(function(a){function c(){b.removeEventListener("load",c),a(b)}b.complete?a(b):b.addEventListener("load",c)})}function f(a){return e(a).then(function(a){var c,e;return e=b.create(d.getWidth(a),d.getHeight(a)),c=b.get2dContext(e),c.drawImage(a,0,0),e})}function g(a){return e(a).then(function(a){var b=a.src;return 0===b.indexOf("blob:")?i(b):0===b.indexOf("data:")?j(b):f(a).then(function(a){return j(a.toDataURL(c.guessMimeType(b)))})})}function h(b){return new a(function(a){function c(){d.removeEventListener("load",c),a(d)}var d=new Image;d.addEventListener("load",c),d.src=URL.createObjectURL(b),d.complete&&c()})}function i(b){return new a(function(a){var c=new XMLHttpRequest;c.open("GET",b,!0),c.responseType="blob",c.onload=function(){200==this.status&&a(this.response)},c.send()})}function j(b){return new a(function(a){var c,d,e,f,g,h;if(b=b.split(","),f=/data:([^;]+)/.exec(b[0]),f&&(g=f[1]),c=atob(b[1]),window.WebKitBlobBuilder){for(h=new WebKitBlobBuilder,d=new ArrayBuffer(c.length),e=0;ec?a=c:b>a&&(a=b),a}function b(){return[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1]}function c(a,b){var c,d,e,f,g=[],h=new Array(10);for(c=0;5>c;c++){for(d=0;5>d;d++)g[d]=b[d+5*c];for(d=0;5>d;d++){for(f=0,e=0;5>e;e++)f+=a[d+5*e]*g[e];h[d+5*c]=f}}return h}function d(b,c){return c=a(c,0,1),b.map(function(b,d){return d%6===0?b=1-(1-b)*c:b*=c,a(b,0,1)})}function e(b,d){var e;return d=a(d,-1,1),d*=100,0>d?e=127+d/100*127:(e=d%1,e=0===e?l[d]:l[Math.floor(d)]*(1-e)+l[Math.floor(d)+1]*e,e=127*e+127),c(b,[e/127,0,0,0,.5*(127-e),0,e/127,0,0,.5*(127-e),0,0,e/127,0,.5*(127-e),0,0,0,1,0,0,0,0,0,1])}function f(b,d){var e,f,g,h;return d=a(d,-1,1),e=1+(d>0?3*d:d),f=.3086,g=.6094,h=.082,c(b,[f*(1-e)+e,g*(1-e),h*(1-e),0,0,f*(1-e),g*(1-e)+e,h*(1-e),0,0,f*(1-e),g*(1-e),h*(1-e)+e,0,0,0,0,0,1,0,0,0,0,0,1])}function g(b,d){var e,f,g,h,i;return d=a(d,-180,180)/180*Math.PI,e=Math.cos(d),f=Math.sin(d),g=.213,h=.715,i=.072,c(b,[g+e*(1-g)+f*-g,h+e*-h+f*-h,i+e*-i+f*(1-i),0,0,g+e*-g+.143*f,h+e*(1-h)+.14*f,i+e*-i+f*-.283,0,0,g+e*-g+f*-(1-g),h+e*-h+f*h,i+e*(1-i)+f*i,0,0,0,0,0,1,0,0,0,0,0,1])}function h(b,d){return d=a(255*d,-255,255),c(b,[1,0,0,0,d,0,1,0,0,d,0,0,1,0,d,0,0,0,1,0,0,0,0,0,1])}function i(b,d,e,f){return d=a(d,0,2),e=a(e,0,2),f=a(f,0,2),c(b,[d,0,0,0,0,0,e,0,0,0,0,0,f,0,0,0,0,0,1,0,0,0,0,0,1])}function j(b,e){return e=a(e,0,1),c(b,d([.393,.769,.189,0,0,.349,.686,.168,0,0,.272,.534,.131,0,0,0,0,0,1,0,0,0,0,0,1],e))}function k(b,e){return e=a(e,0,1),c(b,d([.33,.34,.33,0,0,.33,.34,.33,0,0,.33,.34,.33,0,0,0,0,0,1,0,0,0,0,0,1],e))}var l=[0,.01,.02,.04,.05,.06,.07,.08,.1,.11,.12,.14,.15,.16,.17,.18,.2,.21,.22,.24,.25,.27,.28,.3,.32,.34,.36,.38,.4,.42,.44,.46,.48,.5,.53,.56,.59,.62,.65,.68,.71,.74,.77,.8,.83,.86,.89,.92,.95,.98,1,1.06,1.12,1.18,1.24,1.3,1.36,1.42,1.48,1.54,1.6,1.66,1.72,1.78,1.84,1.9,1.96,2,2.12,2.25,2.37,2.5,2.62,2.75,2.87,3,3.2,3.4,3.6,3.8,4,4.3,4.7,4.9,5,5.5,6,6.5,6.8,7,7.3,7.5,7.8,8,8.4,8.7,9,9.4,9.6,9.8,10];return{identity:b,adjust:d,multiply:c,adjustContrast:e,adjustBrightness:h,adjustSaturation:f,adjustHue:g,adjustColors:i,adjustSepia:j,adjustGrayscale:k}}),g("c",["m","n","e","q"],function(a,b,c,d){function e(d,e){return c.blobToImage(d).then(function(d){function f(a,b){var c,d,e,f,g,h=a.data,i=b[0],j=b[1],k=b[2],l=b[3],m=b[4],n=b[5],o=b[6],p=b[7],q=b[8],r=b[9],s=b[10],t=b[11],u=b[12],v=b[13],w=b[14],x=b[15],y=b[16],z=b[17],A=b[18],B=b[19];for(g=0;gc?a=c:b>a&&(a=b),a}var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;for(g=Math.round(Math.sqrt(c.length)),h=Math.floor(g/2),e=a.data,f=b.data,t=a.width,u=a.height,j=0;u>j;j++)for(i=0;t>i;i++){for(k=l=m=0,o=0;g>o;o++)for(n=0;g>n;n++)p=d(i+n-h,0,t-1),q=d(j+o-h,0,u-1),r=4*(q*t+p),s=c[o*g+n],k+=e[r]*s,l+=e[r+1]*s,m+=e[r+2]*s;r=4*(j*t+i),f[r]=d(k,0,255),f[r+1]=d(l,0,255),f[r+2]=d(m,0,255)}return b}var g,h,i=a.create(b.getWidth(d),b.getHeight(d)),j=a.get2dContext(i);return j.drawImage(d,0,0),k(d),g=j.getImageData(0,0,i.width,i.height),h=j.getImageData(0,0,i.width,i.height),h=f(g,h,e),j.putImageData(h,0,0),c.canvasToBlob(i)})}function g(d){return function(e,f){return c.blobToImage(e).then(function(e){function g(a,b){var c,d=a.data;for(c=0;ci||i>2)&&(i=.5>i?.5:2,k=!0),(.5>j||j>2)&&(j=.5>j?.5:2,k=!0);var l=f(a,i,j);return k?l.then(function(a){return e(a,b,c)}):l}function f(b,e,f){return new a(function(a){var g=d.getWidth(b),h=d.getHeight(b),i=Math.floor(g*e),j=Math.floor(h*f),k=c.create(i,j),l=c.get2dContext(k);l.drawImage(b,0,0,g,h,0,0,i,j),a(k)})}return{scale:e}}),g("d",["e","m","n","r"],function(a,b,c,d){function e(d,e){return a.blobToImage(d).then(function(f){var g=b.create(c.getWidth(f),c.getHeight(f)),h=b.get2dContext(g),j=0,k=0;return e=0>e?360+e:e,90!=e&&270!=e||b.resize(g,g.height,g.width),90!=e&&180!=e||(j=g.width),270!=e&&180!=e||(k=g.height),h.translate(j,k),h.rotate(e*Math.PI/180),h.drawImage(f,0,0),i(f),a.canvasToBlob(g,d.type)})}function f(d,e){return a.blobToImage(d).then(function(d){var f=b.create(c.getWidth(d),c.getHeight(d)),g=b.get2dContext(f);return"v"==e?(g.scale(1,-1),g.drawImage(d,0,-f.height)):(g.scale(-1,1),g.drawImage(d,-f.width,0)),i(d),a.canvasToBlob(f)})}function g(c,d,e,f,g){return a.blobToImage(c).then(function(c){var h=b.create(f,g),j=b.get2dContext(h);return j.drawImage(c,-d,-e),i(c),a.canvasToBlob(h)})}function h(b,c,e){return a.blobToImage(b).then(function(f){var g;return g=d.scale(f,c,e).then(function(c){return a.canvasToBlob(c,b.type)}).then(j(f))["catch"](j(f))})}var i=a.revokeImageUrl,j=function(a){return function(b){return i(a),b}};return{rotate:e,flip:f,crop:g,resize:h}}),g("7",["c","d"],function(a,b){var c=function(b){return a.invert(b)},d=function(b){return a.sharpen(b)},e=function(b){return a.emboss(b)},f=function(b,c){return a.gamma(b,c)},g=function(b,c){return a.exposure(b,c)},h=function(b,c,d,e){return a.colorize(b,c,d,e)},i=function(b,c){return a.brightness(b,c)},j=function(b,c){return a.hue(b,c)},k=function(b,c){return a.saturate(b,c)},l=function(b,c){return a.contrast(b,c)},m=function(b,c){return a.grayscale(b,c)},n=function(b,c){return a.sepia(b,c)},o=function(a,c){return b.flip(a,c)},p=function(a,c,d,e,f){return b.crop(a,c,d,e,f)},q=function(a,c,d){return b.resize(a,c,d)},r=function(a,c){return b.rotate(a,c)};return{invert:c,sharpen:d,emboss:e,brightness:i,hue:j,saturate:k,contrast:l,grayscale:m,sepia:n,colorize:h,gamma:f,exposure:g,flip:o,crop:p,resize:q,rotate:r}}),g("8",["e"],function(a){var b=function(b){return a.blobToImage(b)},c=function(b){return a.imageToBlob(b)},d=function(b){return a.blobToDataUri(b)},e=function(b){return a.blobToBase64(b)};return{blobToImage:b,imageToBlob:c,blobToDataUri:d,blobToBase64:e}}),h("f",tinymce.dom.DOMUtils),h("g",tinymce.ui.Factory),h("h",tinymce.ui.Form),h("i",tinymce.ui.Container),h("s",tinymce.ui.Control),h("t",tinymce.ui.DragHelper),h("u",tinymce.geom.Rect),h("w",tinymce.dom.DomQuery),h("x",tinymce.util.Observable),h("y",tinymce.util.VK),g("v",["w","t","u","5","x","y"],function(a,b,c,d,e,f){var g=0;return function(h,i,j,k,l){function m(a,b){return{x:b.x+a.x,y:b.y+a.y,w:b.w,h:b.h}}function n(a,b){return{x:b.x-a.x,y:b.y-a.y,w:b.w,h:b.h}}function o(){return n(j,h)}function p(a,b,d,e){var f,g,i,k,l;f=b.x,g=b.y,i=b.w,k=b.h,f+=d*a.deltaX,g+=e*a.deltaY,i+=d*a.deltaW,k+=e*a.deltaH,20>i&&(i=20),20>k&&(k=20),l=h=c.clamp({x:f,y:g,w:i,h:k},j,"move"==a.name),l=n(j,l),y.fire("updateRect",{rect:l}),v(l)}function q(){function c(a){var c;return new b(D,{document:k.ownerDocument,handle:D+"-"+a.name,start:function(){c=h},drag:function(b){p(a,c,b.deltaX,b.deltaY)}})}a('
    ').appendTo(k),d.each(B,function(b){a("#"+D,k).append('
    "+(h.ie&&h.ie<10?" ":"
    ")+"
    ",f.undoManager.transact(function(){f.insertContent(e),g=f.dom.get("__mce"),f.dom.setAttrib(g,"id",null),f.$("tr",g).each(function(a,b){f.fire("newrow",{node:b}),f.$("th,td",b).each(function(a,b){f.fire("newcell",{node:b})})}),f.dom.setAttribs(g,f.settings.table_default_attributes||{}),f.dom.setStyles(g,f.settings.table_default_styles||{})}),g}function j(a,b,c){function d(){var d,e,g,h={},i=0;e=f.dom.select("td[data-mce-selected],th[data-mce-selected]"),d=e[0],d||(d=f.selection.getStart()),c&&e.length>0?(k(e,function(a){return h[a.parentNode.parentNode.nodeName]=1}),k(h,function(a){i+=a}),g=1!==i):g=!f.dom.getParent(d,b),a.disabled(g),f.selection.selectorChanged(b,function(b){a.disabled(!b)})}f.initialized?d():f.on("init",d)}function l(){j(this,"table")}function m(){j(this,"td,th")}function n(){j(this,"td,th",!0)}function o(){var a="";a='';for(var b=0;10>b;b++){a+="";for(var c=0;10>c;c++)a+='';a+=""}return a+="
    ",a+=''}function p(a,b,c){var d,e,g,h,i,j=c.getEl().getElementsByTagName("table")[0],k=c.isRtl()||"tl-tr"==c.parent().rel;for(j.nextSibling.innerHTML=a+1+" x "+(b+1),k&&(a=9-a),e=0;10>e;e++)for(d=0;10>d;d++)h=j.rows[e].childNodes[d].firstChild,i=(k?d>=a:a>=d)&&b>=e,f.dom.toggleClass(h,"mce-active",i),i&&(g=h);return g.parentNode}function q(){f.addButton("tableprops",{title:"Table properties",onclick:y.tableProps,icon:"table"}),f.addButton("tabledelete",{title:"Delete table",onclick:g("mceTableDelete")}),f.addButton("tablecellprops",{title:"Cell properties",onclick:g("mceTableCellProps")}),f.addButton("tablemergecells",{title:"Merge cells",onclick:g("mceTableMergeCells")}),f.addButton("tablesplitcells",{title:"Split cell",onclick:g("mceTableSplitCells")}),f.addButton("tableinsertrowbefore",{title:"Insert row before",onclick:g("mceTableInsertRowBefore")}),f.addButton("tableinsertrowafter",{title:"Insert row after",onclick:g("mceTableInsertRowAfter")}),f.addButton("tabledeleterow",{title:"Delete row",onclick:g("mceTableDeleteRow")}),f.addButton("tablerowprops",{title:"Row properties",onclick:g("mceTableRowProps")}),f.addButton("tablecutrow",{title:"Cut row",onclick:g("mceTableCutRow")}),f.addButton("tablecopyrow",{title:"Copy row",onclick:g("mceTableCopyRow")}),f.addButton("tablepasterowbefore",{title:"Paste row before",onclick:g("mceTablePasteRowBefore")}),f.addButton("tablepasterowafter",{title:"Paste row after",onclick:g("mceTablePasteRowAfter")}),f.addButton("tableinsertcolbefore",{title:"Insert column before",onclick:g("mceTableInsertColBefore")}),f.addButton("tableinsertcolafter",{title:"Insert column after",onclick:g("mceTableInsertColAfter")}),f.addButton("tabledeletecol",{title:"Delete column",onclick:g("mceTableDeleteCol")})}function r(a){var b=f.dom.is(a,"table")&&f.getBody().contains(a);return b}function s(){var a=f.settings.table_toolbar;""!==a&&a!==!1&&(a||(a="tableprops tabledelete | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol"),f.addContextToolbar(r,a))}function t(){return v}function u(a){v=a}var v,w,x=this,y=new d(f);!f.settings.object_resizing||f.settings.table_resize_bars===!1||f.settings.object_resizing!==!0&&"table"!==f.settings.object_resizing||(w=e(f)),f.settings.table_grid===!1?f.addMenuItem("inserttable",{text:"Insert table",icon:"table",context:"table",onclick:y.table}):f.addMenuItem("inserttable",{text:"Insert table",icon:"table",context:"table",ariaHideMenu:!0,onclick:function(a){a.aria&&(this.parent().hideAll(),a.stopImmediatePropagation(),y.table())},onshow:function(){p(0,0,this.menu.items()[0])},onhide:function(){var a=this.menu.items()[0].getEl().getElementsByTagName("a");f.dom.removeClass(a,"mce-active"),f.dom.addClass(a[0],"mce-active")},menu:[{type:"container",html:o(),onPostRender:function(){this.lastX=this.lastY=0},onmousemove:function(a){var b,c,d=a.target;"A"==d.tagName.toUpperCase()&&(b=parseInt(d.getAttribute("data-mce-x"),10),c=parseInt(d.getAttribute("data-mce-y"),10),(this.isRtl()||"tl-tr"==this.parent().rel)&&(b=9-b),b===this.lastX&&c===this.lastY||(p(b,c,a.control),this.lastX=b,this.lastY=c))},onclick:function(a){var b=this;"A"==a.target.tagName.toUpperCase()&&(a.preventDefault(),a.stopPropagation(),b.parent().cancel(),f.undoManager.transact(function(){i(b.lastX+1,b.lastY+1)}),f.addVisual())}}]}),f.addMenuItem("tableprops",{text:"Table properties",context:"table",onPostRender:l,onclick:y.tableProps}),f.addMenuItem("deletetable",{text:"Delete table",context:"table",onPostRender:l,cmd:"mceTableDelete"}),f.addMenuItem("cell",{separator:"before",text:"Cell",context:"table",menu:[{text:"Cell properties",onclick:g("mceTableCellProps"),onPostRender:m},{text:"Merge cells",onclick:g("mceTableMergeCells"),onPostRender:n},{text:"Split cell",onclick:g("mceTableSplitCells"),onPostRender:m}]}),f.addMenuItem("row",{text:"Row",context:"table",menu:[{text:"Insert row before",onclick:g("mceTableInsertRowBefore"),onPostRender:m},{text:"Insert row after",onclick:g("mceTableInsertRowAfter"),onPostRender:m},{text:"Delete row",onclick:g("mceTableDeleteRow"),onPostRender:m},{text:"Row properties",onclick:g("mceTableRowProps"),onPostRender:m},{text:"-"},{text:"Cut row",onclick:g("mceTableCutRow"),onPostRender:m},{text:"Copy row",onclick:g("mceTableCopyRow"),onPostRender:m},{text:"Paste row before",onclick:g("mceTablePasteRowBefore"),onPostRender:m},{text:"Paste row after",onclick:g("mceTablePasteRowAfter"),onPostRender:m}]}),f.addMenuItem("column",{text:"Column",context:"table",menu:[{text:"Insert column before",onclick:g("mceTableInsertColBefore"),onPostRender:m},{text:"Insert column after",onclick:g("mceTableInsertColAfter"),onPostRender:m},{text:"Delete column",onclick:g("mceTableDeleteCol"),onPostRender:m}]});var z=[];k("inserttable tableprops deletetable | cell row column".split(" "),function(a){"|"==a?z.push({text:"-"}):z.push(f.menuItems[a])}),f.addButton("table",{type:"menubutton",title:"Table",menu:z}),h.isIE||f.on("click",function(a){a=a.target,"TABLE"===a.nodeName&&(f.selection.select(a),f.nodeChanged())}),x.quirks=new b(f),f.on("Init",function(){x.cellSelection=new c(f,function(a){a&&w.clearBars()}),x.resizeBars=w}),f.on("PreInit",function(){f.serializer.addAttributeFilter("data-mce-cell-padding,data-mce-border,data-mce-border-color",function(a,b){for(var c=a.length;c--;)a[c].attr(b,null)})}),k({mceTableSplitCells:function(a){a.split()},mceTableMergeCells:function(a){var b;b=f.dom.getParent(f.selection.getStart(),"th,td"),f.dom.select("td[data-mce-selected],th[data-mce-selected]").length?a.merge():y.merge(a,b)},mceTableInsertRowBefore:function(a){a.insertRow(!0)},mceTableInsertRowAfter:function(a){a.insertRow()},mceTableInsertColBefore:function(a){a.insertCol(!0)},mceTableInsertColAfter:function(a){a.insertCol()},mceTableDeleteCol:function(a){a.deleteCols()},mceTableDeleteRow:function(a){a.deleteRows()},mceTableCutRow:function(a){v=a.cutRows()},mceTableCopyRow:function(a){v=a.copyRows()},mceTablePasteRowBefore:function(a){a.pasteRows(v,!0)},mceTablePasteRowAfter:function(a){a.pasteRows(v)},mceSplitColsBefore:function(a){a.splitCols(!0)},mceSplitColsAfter:function(a){a.splitCols(!1)},mceTableDelete:function(a){w&&w.clearBars(),a.deleteTable()}},function(b,c){f.addCommand(c,function(){var c=new a(f);c&&(b(c),f.execCommand("mceRepaint"),x.cellSelection.clear())})}),k({mceInsertTable:y.table,mceTableProps:function(){y.table(!0)},mceTableRowProps:y.row,mceTableCellProps:y.cell},function(a,b){f.addCommand(b,function(b,c){a(c)})}),q(),s(),f.settings.table_tab_navigation!==!1&&f.on("keydown",function(b){var c,d,e;9==b.keyCode&&(c=f.dom.getParent(f.selection.getStart(),"th,td"),c&&(b.preventDefault(),d=new a(f),e=b.shiftKey?-1:1,f.undoManager.transact(function(){!d.moveRelIdx(c,e)&&e>0&&(d.insertRow(),d.refresh(),d.moveRelIdx(c,e))})))}),x.insertTable=i,x.setClipboardRows=u,x.getClipboardRows=t}var k=f.each;i.add("table",j)})}(this); \ No newline at end of file diff --git a/www/js/tinymce/plugins/template/plugin.min.js b/www/js/tinymce/plugins/template/plugin.min.js new file mode 100644 index 0000000..183201b --- /dev/null +++ b/www/js/tinymce/plugins/template/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("template",function(a){function b(b){return function(){var c=a.settings.templates;return"function"==typeof c?void c(b):void("string"==typeof c?tinymce.util.XHR.send({url:c,success:function(a){b(tinymce.util.JSON.parse(a))}}):b(c))}}function c(b){function c(b){function c(b){if(-1==b.indexOf("")){var c="";tinymce.each(a.contentCSS,function(b){c+=''});var e=a.settings.body_class||"";-1!=e.indexOf("=")&&(e=a.getParam("body_class","","hash"),e=e[a.id]||""),b=""+c+''+b+""}b=f(b,"template_preview_replace_values");var g=d.find("iframe")[0].getEl().contentWindow.document;g.open(),g.write(b),g.close()}var g=b.control.value();g.url?tinymce.util.XHR.send({url:g.url,success:function(a){e=a,c(e)}}):(e=g.content,c(e)),d.find("#description")[0].text(b.control.value().description)}var d,e,h=[];if(!b||0===b.length){var i=a.translate("No templates defined.");return void a.notificationManager.open({text:i,type:"info"})}tinymce.each(b,function(a){h.push({selected:!h.length,text:a.title,value:{url:a.url,content:a.content,description:a.description}})}),d=a.windowManager.open({title:"Insert template",layout:"flex",direction:"column",align:"stretch",padding:15,spacing:10,items:[{type:"form",flex:0,padding:0,items:[{type:"container",label:"Templates",items:{type:"listbox",label:"Templates",name:"template",values:h,onselect:c}}]},{type:"label",name:"description",label:"Description",text:"\xa0"},{type:"iframe",flex:1,border:1}],onsubmit:function(){g(!1,e)},minWidth:Math.min(tinymce.DOM.getViewPort().w,a.getParam("template_popup_width",600)),minHeight:Math.min(tinymce.DOM.getViewPort().h,a.getParam("template_popup_height",500))}),d.find("listbox")[0].fire("select")}function d(b,c){function d(a,b){if(a=""+a,a.length0&&(i=k.create("div",null),i.appendChild(j[0].cloneNode(!0))),h(k.select("*",i),function(b){g(b,a.getParam("template_cdate_classes","cdate").replace(/\s+/g,"|"))&&(b.innerHTML=d(a.getParam("template_cdate_format",a.getLang("template.cdate_format")))),g(b,a.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(b.innerHTML=d(a.getParam("template_mdate_format",a.getLang("template.mdate_format")))),g(b,a.getParam("template_selected_content_classes","selcontent").replace(/\s+/g,"|"))&&(b.innerHTML=l)}),e(i),a.execCommand("mceInsertContent",!1,i.innerHTML),a.addVisual()}var h=tinymce.each;a.addCommand("mceInsertTemplate",g),a.addButton("template",{title:"Insert template",onclick:b(c)}),a.addMenuItem("template",{text:"Insert template",onclick:b(c),context:"insert"}),a.on("PreProcess",function(b){var c=a.dom;h(c.select("div",b.node),function(b){c.hasClass(b,"mceTmpl")&&(h(c.select("*",b),function(b){c.hasClass(b,a.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(b.innerHTML=d(a.getParam("template_mdate_format",a.getLang("template.mdate_format"))))}),e(b))})})}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/textcolor/plugin.min.js b/www/js/tinymce/plugins/textcolor/plugin.min.js new file mode 100644 index 0000000..889c5c4 --- /dev/null +++ b/www/js/tinymce/plugins/textcolor/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("textcolor",function(a){function b(b){var c;return a.dom.getParents(a.selection.getStart(),function(a){var d;(d=a.style["forecolor"==b?"color":"background-color"])&&(c=d)}),c}function c(b){var c,d,e=[];for(d=["000000","Black","993300","Burnt orange","333300","Dark olive","003300","Dark green","003366","Dark azure","000080","Navy Blue","333399","Indigo","333333","Very dark gray","800000","Maroon","FF6600","Orange","808000","Olive","008000","Green","008080","Teal","0000FF","Blue","666699","Grayish blue","808080","Gray","FF0000","Red","FF9900","Amber","99CC00","Yellow green","339966","Sea green","33CCCC","Turquoise","3366FF","Royal blue","800080","Purple","999999","Medium gray","FF00FF","Magenta","FFCC00","Gold","FFFF00","Yellow","00FF00","Lime","00FFFF","Aqua","00CCFF","Sky blue","993366","Red violet","FFFFFF","White","FF99CC","Pink","FFCC99","Peach","FFFF99","Light yellow","CCFFCC","Pale green","CCFFFF","Pale cyan","99CCFF","Light sky blue","CC99FF","Plum"],d=a.settings.textcolor_map||d,d=a.settings[b+"_map"]||d,c=0;c
    '+(c?"×":"")+"
    ',g=d.length-1,k=0;k",h=0;hg?f+="":(e=d[l],f+=b(e.color,e.text));f+=""}if(a.settings.color_picker_callback){for(f+='",f+="",h=0;hb.start.length?-1:a.start.length'+a+""}function f(){var a,b="";for(a in n)b+=a;return new RegExp("["+b+"]","g")}function g(){var a,b="";for(a in n)b&&(b+=","),b+="span.mce-"+n[a];return b}var h,i,j,k,l,m,n,o,p=a.getBody(),q=a.selection;if(n={"\xa0":"nbsp","\xad":"shy"},d=!d,e.state=d,a.fire("VisualChars",{state:d}),o=f(),b&&(m=q.getBookmark()),d)for(i=[],tinymce.walk(p,function(a){3==a.nodeType&&a.nodeValue&&o.test(a.nodeValue)&&i.push(a)},"childNodes"),j=0;j=0;j--)a.dom.remove(i[j],1);q.moveToBookmark(m)}function c(){var b=this;a.on("VisualChars",function(a){b.active(a.state)})}var d,e=this;a.addCommand("mceVisualChars",b),a.addButton("visualchars",{title:"Show invisible characters",cmd:"mceVisualChars",onPostRender:c}),a.addMenuItem("visualchars",{text:"Show invisible characters",cmd:"mceVisualChars",onPostRender:c,selectable:!0,context:"view",prependToContext:!0}),a.on("beforegetcontent",function(a){d&&"raw"!=a.format&&!a.draft&&(d=!0,b(!1))})}); \ No newline at end of file diff --git a/www/js/tinymce/plugins/wordcount/plugin.min.js b/www/js/tinymce/plugins/wordcount/plugin.min.js new file mode 100644 index 0000000..f20e765 --- /dev/null +++ b/www/js/tinymce/plugins/wordcount/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("wordcount",function(a){function b(){a.theme.panel.find("#wordcount").text(["Words: {0}",e.getCount()])}var c,d,e=this;c=a.getParam("wordcount_countregex",/[\w\u2019\x27\-\u00C0-\u1FFF]+/g),d=a.getParam("wordcount_cleanregex",/[0-9.(),;:!?%#$?\x27\x22_+=\\\/\-]*/g),a.on("init",function(){var c=a.theme.panel&&a.theme.panel.find("#statusbar")[0];c&&tinymce.util.Delay.setEditorTimeout(a,function(){c.insert({type:"label",name:"wordcount",text:["Words: {0}",e.getCount()],classes:"wordcount",disabled:a.settings.readonly},0),a.on("setcontent beforeaddundo",b),a.on("keyup",function(a){32==a.keyCode&&b()})},0)}),e.getCount=function(){var b=a.getContent({format:"raw"}),e=0;if(b){b=b.replace(/\.\.\./g," "),b=b.replace(/<.[^<>]*?>/g," ").replace(/ | /gi," "),b=b.replace(/(\w+)(&#?[a-z0-9]+;)+(\w+)/i,"$1$3").replace(/&.+?;/g," "),b=b.replace(d,"");var f=b.match(c);f&&(e=f.length)}return e}}); \ No newline at end of file diff --git a/www/js/tinymce/skins/lightgray/content.inline.min.css b/www/js/tinymce/skins/lightgray/content.inline.min.css new file mode 100644 index 0000000..1030094 --- /dev/null +++ b/www/js/tinymce/skins/lightgray/content.inline.min.css @@ -0,0 +1 @@ +.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3a3a3a;background:#d5d5d5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3a3a3a;background:#d5d5d5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}hr{cursor:default}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-spellchecker-word{border-bottom:2px solid #f00;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #bbb}td[data-mce-selected],th[data-mce-selected]{background-color:#39f !important}.mce-edit-focus{outline:1px dotted #333}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2d8ac7}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #7acaff}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2d8ac7}.mce-resize-bar-dragging{background-color:blue;opacity:.25;filter:alpha(opacity=25);zoom:1} \ No newline at end of file diff --git a/www/js/tinymce/skins/lightgray/content.min.css b/www/js/tinymce/skins/lightgray/content.min.css new file mode 100644 index 0000000..af85f74 --- /dev/null +++ b/www/js/tinymce/skins/lightgray/content.min.css @@ -0,0 +1 @@ +body{background-color:#fff;color:#000;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:11px;scrollbar-3dlight-color:#f0f0ee;scrollbar-arrow-color:#676662;scrollbar-base-color:#f0f0ee;scrollbar-darkshadow-color:#ddd;scrollbar-face-color:#e0e0dd;scrollbar-highlight-color:#f0f0ee;scrollbar-shadow-color:#f0f0ee;scrollbar-track-color:#f5f5f5}td,th{font-family:Verdana,Arial,Helvetica,sans-serif;font-size:11px}.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3a3a3a;background:#d5d5d5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3a3a3a;background:#d5d5d5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}hr{cursor:default}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-spellchecker-word{border-bottom:2px solid #f00;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #bbb}td[data-mce-selected],th[data-mce-selected]{background-color:#39f !important}.mce-edit-focus{outline:1px dotted #333}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2d8ac7}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #7acaff}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2d8ac7}.mce-resize-bar-dragging{background-color:blue;opacity:.25;filter:alpha(opacity=25);zoom:1} \ No newline at end of file diff --git a/www/js/tinymce/skins/lightgray/fonts/tinymce-small.eot b/www/js/tinymce/skins/lightgray/fonts/tinymce-small.eot new file mode 100644 index 0000000..b144ba0 Binary files /dev/null and b/www/js/tinymce/skins/lightgray/fonts/tinymce-small.eot differ diff --git a/www/js/tinymce/skins/lightgray/fonts/tinymce-small.svg b/www/js/tinymce/skins/lightgray/fonts/tinymce-small.svg new file mode 100644 index 0000000..b4ee6f4 --- /dev/null +++ b/www/js/tinymce/skins/lightgray/fonts/tinymce-small.svg @@ -0,0 +1,63 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/js/tinymce/skins/lightgray/fonts/tinymce-small.ttf b/www/js/tinymce/skins/lightgray/fonts/tinymce-small.ttf new file mode 100644 index 0000000..a983e2d Binary files /dev/null and b/www/js/tinymce/skins/lightgray/fonts/tinymce-small.ttf differ diff --git a/www/js/tinymce/skins/lightgray/fonts/tinymce-small.woff b/www/js/tinymce/skins/lightgray/fonts/tinymce-small.woff new file mode 100644 index 0000000..d8962df Binary files /dev/null and b/www/js/tinymce/skins/lightgray/fonts/tinymce-small.woff differ diff --git a/www/js/tinymce/skins/lightgray/fonts/tinymce.eot b/www/js/tinymce/skins/lightgray/fonts/tinymce.eot new file mode 100644 index 0000000..09fd441 Binary files /dev/null and b/www/js/tinymce/skins/lightgray/fonts/tinymce.eot differ diff --git a/www/js/tinymce/skins/lightgray/fonts/tinymce.svg b/www/js/tinymce/skins/lightgray/fonts/tinymce.svg new file mode 100644 index 0000000..8688c1c --- /dev/null +++ b/www/js/tinymce/skins/lightgray/fonts/tinymce.svg @@ -0,0 +1,129 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/js/tinymce/skins/lightgray/fonts/tinymce.ttf b/www/js/tinymce/skins/lightgray/fonts/tinymce.ttf new file mode 100644 index 0000000..bf22ca6 Binary files /dev/null and b/www/js/tinymce/skins/lightgray/fonts/tinymce.ttf differ diff --git a/www/js/tinymce/skins/lightgray/fonts/tinymce.woff b/www/js/tinymce/skins/lightgray/fonts/tinymce.woff new file mode 100644 index 0000000..61cbafb Binary files /dev/null and b/www/js/tinymce/skins/lightgray/fonts/tinymce.woff differ diff --git a/www/js/tinymce/skins/lightgray/img/anchor.gif b/www/js/tinymce/skins/lightgray/img/anchor.gif new file mode 100644 index 0000000..606348c Binary files /dev/null and b/www/js/tinymce/skins/lightgray/img/anchor.gif differ diff --git a/www/js/tinymce/skins/lightgray/img/loader.gif b/www/js/tinymce/skins/lightgray/img/loader.gif new file mode 100644 index 0000000..c69e937 Binary files /dev/null and b/www/js/tinymce/skins/lightgray/img/loader.gif differ diff --git a/www/js/tinymce/skins/lightgray/img/object.gif b/www/js/tinymce/skins/lightgray/img/object.gif new file mode 100644 index 0000000..cccd7f0 Binary files /dev/null and b/www/js/tinymce/skins/lightgray/img/object.gif differ diff --git a/www/js/tinymce/skins/lightgray/img/trans.gif b/www/js/tinymce/skins/lightgray/img/trans.gif new file mode 100644 index 0000000..3884865 Binary files /dev/null and b/www/js/tinymce/skins/lightgray/img/trans.gif differ diff --git a/www/js/tinymce/skins/lightgray/skin.ie7.min.css b/www/js/tinymce/skins/lightgray/skin.ie7.min.css new file mode 100644 index 0000000..2e64b89 --- /dev/null +++ b/www/js/tinymce/skins/lightgray/skin.ie7.min.css @@ -0,0 +1 @@ +.mce-container,.mce-container *,.mce-widget,.mce-widget *,.mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-widget button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:inherit !important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}div.mce-edit-area{background:#fff;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid rgba(0,0,0,0.2);width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#d9d9d9}.mce-grid td.mce-grid-cell div{border:1px solid #d6d6d6;width:15px;height:15px;margin:0;cursor:pointer}.mce-grid td.mce-grid-cell div:focus{border-color:#3498db}.mce-grid td.mce-grid-cell div[disabled]{cursor:not-allowed}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover,.mce-grid a:focus{border-color:#3498db}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#d6d6d6;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#3498db;background:#3498db}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%}.mce-colorbtn-trans div{text-align:center;vertical-align:middle;font-weight:bold;font-size:20px;line-height:16px;color:#707070}.mce-monospace{font-family:"Courier New",Courier,monospace}.mce-toolbar-grp{padding:2px 0}.mce-toolbar-grp .mce-flow-layout-item{margin-bottom:0}.mce-rtl .mce-wordcount{left:0;right:auto}.mce-croprect-container{position:absolute;top:0;left:0}.mce-croprect-handle{position:absolute;top:0;left:0;width:20px;height:20px;border:2px solid white}.mce-croprect-handle-nw{border-width:2px 0 0 2px;margin:-2px 0 0 -2px;cursor:nw-resize;top:100px;left:100px}.mce-croprect-handle-ne{border-width:2px 2px 0 0;margin:-2px 0 0 -20px;cursor:ne-resize;top:100px;left:200px}.mce-croprect-handle-sw{border-width:0 0 2px 2px;margin:-20px 2px 0 -2px;cursor:sw-resize;top:200px;left:100px}.mce-croprect-handle-se{border-width:0 2px 2px 0;margin:-20px 0 0 -20px;cursor:se-resize;top:200px;left:200px}.mce-croprect-handle-move{position:absolute;cursor:move;border:0}.mce-croprect-block{opacity:.3;filter:alpha(opacity=30);zoom:1;position:absolute;background:black}.mce-croprect-handle:focus{border-color:#3498db}.mce-croprect-handle-move:focus{outline:1px solid #3498db}.mce-imagepanel{overflow:auto;background:black}.mce-imagepanel img{position:absolute}.mce-imagetool.mce-btn .mce-ico{display:block;width:20px;height:20px;text-align:center;line-height:20px;font-size:20px;padding:5px}.mce-arrow-up{margin-top:12px}.mce-arrow-down{margin-top:-12px}.mce-arrow:before,.mce-arrow:after{position:absolute;left:50%;display:block;width:0;height:0;border-style:solid;border-color:transparent;content:""}.mce-arrow.mce-arrow-up:before{top:-9px;border-bottom-color:rgba(0,0,0,0.2);border-width:0 9px 9px;margin-left:-9px}.mce-arrow.mce-arrow-down:before{bottom:-9px;border-top-color:rgba(0,0,0,0.2);border-width:9px 9px 0;margin-left:-9px}.mce-arrow.mce-arrow-up:after{top:-8px;border-bottom-color:#f0f0f0;border-width:0 8px 8px;margin-left:-8px}.mce-arrow.mce-arrow-down:after{bottom:-8px;border-top-color:#f0f0f0;border-width:8px 8px 0;margin-left:-8px}.mce-arrow.mce-arrow-left:before,.mce-arrow.mce-arrow-left:after{margin:0}.mce-arrow.mce-arrow-left:before{left:8px}.mce-arrow.mce-arrow-left:after{left:9px}.mce-arrow.mce-arrow-right:before,.mce-arrow.mce-arrow-right:after{left:auto;margin:0}.mce-arrow.mce-arrow-right:before{right:8px}.mce-arrow.mce-arrow-right:after{right:9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left:before{left:-9px;top:50%;border-right-color:rgba(0,0,0,0.2);border-width:9px 9px 9px 0;margin-top:-9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left:after{left:-8px;top:50%;border-right-color:#f0f0f0;border-width:8px 8px 8px 0;margin-top:-8px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left{margin-left:12px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right:before{right:-9px;top:50%;border-left-color:rgba(0,0,0,0.2);border-width:9px 0 9px 9px;margin-top:-9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right:after{right:-8px;top:50%;border-left-color:#f0f0f0;border-width:8px 0 8px 8px;margin-top:-8px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right{margin-left:-14px}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#aaa;opacity:.6;filter:alpha(opacity=60);zoom:1}.mce-scroll{position:relative}.mce-panel{border:0 solid #cacaca;border:0 solid rgba(0,0,0,0.2);background-color:#f0f0f0}.mce-floatpanel{position:absolute}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;top:0;left:0;background:#fff;border:1px solid rgba(0,0,0,0.2);border:1px solid rgba(0,0,0,0.25)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px;*margin-top:0}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:rgba(0,0,0,0.2);border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#fff;position:fixed;top:0;left:0;opacity:0;transform:scale(.1);transition:transform 100ms ease-in,opacity 150ms ease-in}.mce-window.mce-in{transform:scale(1);opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #c5c5c5;position:relative}.mce-window-head .mce-close{position:absolute;right:0;top:0;height:38px;width:38px;text-align:center;cursor:pointer}.mce-window-head .mce-close i{color:#858585}.mce-close:hover i{color:#adadad}.mce-window-head .mce-title{line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:20px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:#fff;border-top:1px solid #c5c5c5}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window-body .mce-listbox{border-color:#ccc}.mce-rtl .mce-window-head .mce-close{position:absolute;right:auto;left:15px}.mce-rtl .mce-window-head .mce-dragh{left:auto;right:0}.mce-rtl .mce-window-head .mce-title{direction:rtl;text-align:right}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:white;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-n .mce-tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:none;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-progress{display:inline-block;position:relative;height:20px}.mce-progress .mce-bar-container{display:inline-block;width:100px;height:100%;margin-right:8px;border:1px solid #ccc;overflow:hidden}.mce-progress .mce-text{display:inline-block;margin-top:auto;margin-bottom:auto;font-size:14px;width:40px;color:#333}.mce-bar{display:block;width:0;height:100%;background-color:#d7d7d7;-webkit-transition:width .2s ease;transition:width .2s ease}.mce-notification{position:absolute;background-color:#f0f0f0;padding:5px;margin-top:5px;border-width:1px;border-style:solid;border-color:#ccc;transition:transform 100ms ease-in,opacity 150ms ease-in;opacity:0}.mce-notification.mce-in{opacity:1}.mce-notification-success{background-color:#dff0d8;border-color:#d6e9c6}.mce-notification-info{background-color:#d9edf7;border-color:#779ecb}.mce-notification-warning{background-color:#fcf8e3;border-color:#faebcc}.mce-notification-error{background-color:#f2dede;border-color:#ebccd1}.mce-notification.mce-has-close{padding-right:15px}.mce-notification .mce-ico{margin-top:5px}.mce-notification-inner{display:inline-block;font-size:14px;margin:5px 8px 4px 8px;text-align:center;white-space:normal;color:#31708f}.mce-notification-inner a{text-decoration:underline;cursor:pointer}.mce-notification .mce-progress{margin-right:8px}.mce-notification .mce-progress .mce-text{margin-top:5px}.mce-notification *,.mce-notification .mce-progress .mce-text{color:#333}.mce-notification .mce-progress .mce-bar-container{border-color:#ccc}.mce-notification .mce-progress .mce-bar-container .mce-bar{background-color:#333}.mce-notification-success *,.mce-notification-success .mce-progress .mce-text{color:#3c763d}.mce-notification-success .mce-progress .mce-bar-container{border-color:#d6e9c6}.mce-notification-success .mce-progress .mce-bar-container .mce-bar{background-color:#3c763d}.mce-notification-info *,.mce-notification-info .mce-progress .mce-text{color:#31708f}.mce-notification-info .mce-progress .mce-bar-container{border-color:#779ecb}.mce-notification-info .mce-progress .mce-bar-container .mce-bar{background-color:#31708f}.mce-notification-warning *,.mce-notification-warning .mce-progress .mce-text{color:#8a6d3b}.mce-notification-warning .mce-progress .mce-bar-container{border-color:#faebcc}.mce-notification-warning .mce-progress .mce-bar-container .mce-bar{background-color:#8a6d3b}.mce-notification-error *,.mce-notification-error .mce-progress .mce-text{color:#a94442}.mce-notification-error .mce-progress .mce-bar-container{border-color:#ebccd1}.mce-notification-error .mce-progress .mce-bar-container .mce-bar{background-color:#a94442}.mce-notification .mce-close{position:absolute;top:6px;right:8px;font-size:20px;font-weight:bold;line-height:20px;color:#858585;cursor:pointer;height:20px;overflow:hidden}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-btn{border:1px solid #b1b1b1;border-color:transparent transparent transparent transparent;position:relative;text-shadow:0 1px 1px rgba(255,255,255,0.75);display:inline-block;*display:inline;*zoom:1;background-color:#f0f0f0}.mce-btn:hover,.mce-btn:focus{color:#333;background-color:#e3e3e3;border-color:#ccc}.mce-btn.mce-disabled button,.mce-btn.mce-disabled:hover button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{background-color:#dbdbdb;border-color:#ccc}.mce-btn:active{background-color:#e0e0e0;border-color:#ccc}.mce-btn button{padding:4px 8px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#333;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px none}.mce-primary.mce-btn-has-text{min-width:50px}.mce-primary{color:#fff;border:1px solid transparent;border-color:transparent;background-color:#2d8ac7}.mce-primary:hover,.mce-primary:focus{background-color:#257cb6;border-color:transparent}.mce-primary.mce-disabled button,.mce-primary.mce-disabled:hover button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-primary.mce-active,.mce-primary.mce-active:hover,.mce-primary:not(.mce-disabled):active{background-color:#206ea1}.mce-primary button,.mce-primary button i{color:#fff;text-shadow:1px 1px none}.mce-btn .mce-txt{font-size:inherit;line-height:inherit;color:inherit}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:1px 5px;font-size:12px;*padding-bottom:2px}.mce-btn-small i{line-height:20px;vertical-align:top;*line-height:18px}.mce-btn .mce-caret{margin-top:8px;margin-left:0}.mce-btn-small .mce-caret{margin-top:8px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #333;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#aaa}.mce-caret.mce-up{border-bottom:4px solid #333;border-top:0}.mce-btn-flat{border:0;background:transparent;filter:none}.mce-btn-flat:hover,.mce-btn-flat.mce-active,.mce-btn-flat:focus,.mce-btn-flat:active{border:0;background:#e6e6e6;filter:none}.mce-btn-has-text .mce-ico{padding-right:5px}.mce-rtl .mce-btn button{direction:rtl}.mce-btn-group .mce-btn{border-width:1px;margin:0;margin-left:2px}.mce-btn-group:not(:first-child){border-left:1px solid #d9d9d9;padding-left:3px;margin-left:3px}.mce-btn-group .mce-first{margin-left:0}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-rtl .mce-btn-group .mce-btn{margin-left:0;margin-right:2px}.mce-rtl .mce-btn-group .mce-first{margin-right:0}.mce-rtl .mce-btn-group:not(:first-child){border-left:none;border-right:1px solid #d9d9d9;padding-right:4px;margin-right:4px}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;background-color:#f0f0f0;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0;overflow:hidden}.mce-checked i.mce-i-checkbox{color:#333;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox,.mce-checkbox.mce-focus i.mce-i-checkbox{border:1px solid rgba(82,168,236,0.8)}.mce-checkbox.mce-disabled .mce-label,.mce-checkbox.mce-disabled i.mce-i-checkbox{color:#acacac}.mce-checkbox .mce-label{vertical-align:middle}.mce-rtl .mce-checkbox{direction:rtl;text-align:right}.mce-rtl i.mce-i-checkbox{margin:0 0 0 3px}.mce-combobox{display:inline-block;*display:inline;*zoom:1;*height:32px}.mce-combobox input{border:1px solid #c5c5c5;border-right-color:#c5c5c5;height:28px}.mce-combobox.mce-disabled input{color:#adadad}.mce-combobox .mce-btn{border:1px solid #c5c5c5;border-left:0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox.mce-disabled .mce-btn button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-colorbox i{border:1px solid #c5c5c5;width:14px;height:14px}.mce-colorbutton .mce-ico{position:relative}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:6px;padding-left:6px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-17px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-16px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;padding-right:4px;border-left:1px solid transparent}.mce-colorbutton:hover .mce-open{border-color:#ccc}.mce-colorbutton.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-colorbutton{direction:rtl}.mce-rtl .mce-colorbutton .mce-preview{margin-left:0;padding-right:0;padding-left:3px}.mce-rtl .mce-colorbutton.mce-btn-small .mce-preview{margin-left:0;padding-right:0;padding-left:2px}.mce-rtl .mce-colorbutton .mce-open{padding-left:4px;padding-right:4px;border-left:0}.mce-colorpicker{position:relative;width:250px;height:220px}.mce-colorpicker-sv{position:absolute;top:0;left:0;width:90%;height:100%;border:1px solid #c5c5c5;cursor:crosshair;overflow:hidden}.mce-colorpicker-h-chunk{width:100%}.mce-colorpicker-overlay1,.mce-colorpicker-overlay2{width:100%;height:100%;position:absolute;top:0;left:0}.mce-colorpicker-overlay1{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr='#ffffff', endColorstr='#00ffffff');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')";background:linear-gradient(to right, #fff, rgba(255,255,255,0))}.mce-colorpicker-overlay2{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#00000000', endColorstr='#000000');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')";background:linear-gradient(to bottom, rgba(0,0,0,0), #000)}.mce-colorpicker-selector1{background:none;position:absolute;width:12px;height:12px;margin:-8px 0 0 -8px;border:1px solid black;border-radius:50%}.mce-colorpicker-selector2{position:absolute;width:10px;height:10px;border:1px solid white;border-radius:50%}.mce-colorpicker-h{position:absolute;top:0;right:0;width:6.5%;height:100%;border:1px solid #c5c5c5;cursor:crosshair}.mce-colorpicker-h-marker{margin-top:-4px;position:absolute;top:0;left:-1px;width:100%;border:1px solid #333;background:#fff;height:4px;z-index:100}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#333}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:#666;color:#fff}.mce-path .mce-divider{display:inline}.mce-disabled .mce-path-item{color:#aaa}.mce-rtl .mce-path{direction:rtl}.mce-fieldset{border:0 solid #9e9e9e}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-rtl .mce-flow-layout{text-align:right;direction:rtl}.mce-rtl .mce-flow-layout-item{margin:2px 2px 2px 0}.mce-rtl .mce-flow-layout-item.mce-last{margin-left:2px}.mce-iframe{border:0 solid rgba(0,0,0,0.2);width:100%;height:100%}.mce-infobox{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);overflow:hidden;border:1px solid red}.mce-infobox div{display:block;margin:5px}.mce-infobox div button{position:absolute;top:50%;right:4px;cursor:pointer;margin-top:-8px;display:none}.mce-infobox div button:focus{outline:2px solid #ccc}.mce-infobox.mce-has-help div{margin-right:25px}.mce-infobox.mce-has-help button{display:block}.mce-infobox.mce-success{background:#dff0d8;border-color:#d6e9c6}.mce-infobox.mce-success div{color:#3c763d}.mce-infobox.mce-warning{background:#fcf8e3;border-color:#faebcc}.mce-infobox.mce-warning div{color:#8a6d3b}.mce-infobox.mce-error{background:#f2dede;border-color:#ebccd1}.mce-infobox.mce-error div{color:#a94442}.mce-rtl .mce-infobox div{text-align:right;direction:rtl}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label.mce-disabled{color:#aaa}.mce-label.mce-multiline{white-space:pre-wrap}.mce-label.mce-success{color:#468847}.mce-label.mce-warning{color:#c09853}.mce-label.mce-error{color:#b94a48}.mce-rtl .mce-label{text-align:right;direction:rtl}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;filter:none}.mce-menubar .mce-menubtn button{color:#333}.mce-menubar{border:1px solid rgba(217,217,217,0.52)}.mce-menubar .mce-menubtn button span{color:#333}.mce-menubar .mce-caret{border-top-color:#333}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:#ccc;background:#fff;filter:none}.mce-menubtn button{color:#333}.mce-menubtn.mce-btn-small span{font-size:12px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-menubtn .mce-caret{*margin-top:6px}.mce-rtl .mce-menubtn button{direction:rtl;text-align:right}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item .mce-ico,.mce-menu-item .mce-text{color:#333}.mce-menu-item.mce-disabled .mce-text,.mce-menu-item.mce-disabled .mce-ico{color:#adadad}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text,.mce-menu-item:focus .mce-text{color:white}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:white}.mce-menu-item.mce-disabled:hover{background:#ccc}.mce-menu-shortcut{display:inline-block;color:#adadad}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 15px 0 20px}.mce-menu-item:hover .mce-menu-shortcut,.mce-menu-item.mce-selected .mce-menu-shortcut,.mce-menu-item:focus .mce-menu-shortcut{color:white}.mce-menu-item .mce-caret{margin-top:4px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #333}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret,.mce-menu-item:hover .mce-caret{border-left-color:white}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item-normal.mce-active{background-color:#3498db}.mce-menu-item-preview.mce-active{border-left:5px solid #aaa}.mce-menu-item-normal.mce-active .mce-text{color:white}.mce-menu-item-normal.mce-active:hover .mce-text,.mce-menu-item-normal.mce-active:hover .mce-ico{color:white}.mce-menu-item-normal.mce-active:focus .mce-text,.mce-menu-item-normal.mce-active:focus .mce-ico{color:white}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:white;background-color:#2d8ac7}div.mce-menu .mce-menu-item-sep,.mce-menu-item-sep:hover{border:0;padding:0;height:1px;margin:9px 1px;overflow:hidden;background:transparent;border-bottom:1px solid rgba(0,0,0,0.1);cursor:default;filter:none}.mce-menu.mce-rtl{direction:rtl}.mce-rtl .mce-menu-item{text-align:right;direction:rtl;padding:6px 12px 6px 15px}.mce-menu-align.mce-rtl .mce-menu-shortcut,.mce-menu-align.mce-rtl .mce-caret{right:auto;left:0}.mce-rtl .mce-menu-item .mce-caret{margin-left:6px;margin-right:0;border-right:4px solid #333;border-left:0}.mce-rtl .mce-menu-item.mce-selected .mce-caret,.mce-rtl .mce-menu-item:focus .mce-caret,.mce-rtl .mce-menu-item:hover .mce-caret{border-left-color:transparent;border-right-color:white}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}.mce-throbber-inline{position:static;height:50px}.mce-menu .mce-throbber-inline{height:25px;background-size:contain}.mce-menu{position:absolute;left:0;top:0;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:-1px 0 0;min-width:160px;background:#fff;border:1px solid #989898;border:1px solid rgba(0,0,0,0.2);z-index:1002;max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-rtl .mce-listbox .mce-caret{right:auto;left:8px}.mce-rtl .mce-listbox button{padding-right:10px;padding-left:20px}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#333}.mce-selectbox{background:#fff;border:1px solid #c5c5c5}.mce-slider{border:1px solid #aaa;background:#eee;width:100px;height:10px;position:relative;display:block}.mce-slider.mce-vertical{width:10px;height:100px}.mce-slider-handle{border:1px solid #bbb;background:#ddd;display:block;width:13px;height:13px;position:absolute;top:0;left:0;margin-left:-1px;margin-top:-2px}.mce-slider-handle:focus{background:#bbb}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#ccc}.mce-splitbtn button{padding-right:6px;padding-left:6px}.mce-splitbtn .mce-open{padding-right:4px;padding-left:4px}.mce-splitbtn .mce-open.mce-active{background-color:#dbdbdb;outline:1px solid #ccc}.mce-splitbtn.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-splitbtn{direction:rtl;text-align:right}.mce-rtl .mce-splitbtn button{padding-right:4px;padding-left:4px}.mce-rtl .mce-splitbtn .mce-open{border-left:0}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #c5c5c5}.mce-tabs,.mce-tabs+.mce-container-body{background:#fff}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #c5c5c5;border-width:0 1px 0 0;background:#fff;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-rtl .mce-tabs{text-align:right;direction:rtl}.mce-rtl .mce-tab{border-width:0 0 0 1px}.mce-textbox{background:#fff;border:1px solid #c5c5c5;display:inline-block;-webkit-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#333}.mce-textbox:focus,.mce-textbox.mce-focus{border-color:#3498db}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px;height:auto}.mce-textbox.mce-disabled{color:#adadad}.mce-rtl .mce-textbox{text-align:right;direction:rtl}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce';font-style:normal;font-weight:normal;font-size:16px;line-height:16px;vertical-align:text-top;-webkit-font-smoothing:antialiased;display:inline-block;background:transparent center center;width:16px;height:16px;color:#333;-ie7-icon:' '}.mce-btn-small .mce-ico{font-family:'tinymce-small'}.mce-ico,i.mce-i-checkbox{zoom:expression(this.runtimeStyle['zoom'] = '1', this.innerHTML = this.currentStyle['-ie7-icon'].substr(1, 1) + ' ')}.mce-i-save{-ie7-icon:"\e000"}.mce-i-newdocument{-ie7-icon:"\e001"}.mce-i-fullpage{-ie7-icon:"\e002"}.mce-i-alignleft{-ie7-icon:"\e003"}.mce-i-aligncenter{-ie7-icon:"\e004"}.mce-i-alignright{-ie7-icon:"\e005"}.mce-i-alignjustify{-ie7-icon:"\e006"}.mce-i-alignnone{-ie7-icon:"\e003"}.mce-i-cut{-ie7-icon:"\e007"}.mce-i-paste{-ie7-icon:"\e008"}.mce-i-searchreplace{-ie7-icon:"\e009"}.mce-i-bullist{-ie7-icon:"\e00a"}.mce-i-numlist{-ie7-icon:"\e00b"}.mce-i-indent{-ie7-icon:"\e00c"}.mce-i-outdent{-ie7-icon:"\e00d"}.mce-i-blockquote{-ie7-icon:"\e00e"}.mce-i-undo{-ie7-icon:"\e00f"}.mce-i-redo{-ie7-icon:"\e010"}.mce-i-link{-ie7-icon:"\e011"}.mce-i-unlink{-ie7-icon:"\e012"}.mce-i-anchor{-ie7-icon:"\e013"}.mce-i-image{-ie7-icon:"\e014"}.mce-i-media{-ie7-icon:"\e015"}.mce-i-help{-ie7-icon:"\e016"}.mce-i-code{-ie7-icon:"\e017"}.mce-i-insertdatetime{-ie7-icon:"\e018"}.mce-i-preview{-ie7-icon:"\e019"}.mce-i-forecolor{-ie7-icon:"\e01a"}.mce-i-backcolor{-ie7-icon:"\e01a"}.mce-i-table{-ie7-icon:"\e01b"}.mce-i-hr{-ie7-icon:"\e01c"}.mce-i-removeformat{-ie7-icon:"\e01d"}.mce-i-subscript{-ie7-icon:"\e01e"}.mce-i-superscript{-ie7-icon:"\e01f"}.mce-i-charmap{-ie7-icon:"\e020"}.mce-i-emoticons{-ie7-icon:"\e021"}.mce-i-print{-ie7-icon:"\e022"}.mce-i-fullscreen{-ie7-icon:"\e023"}.mce-i-spellchecker{-ie7-icon:"\e024"}.mce-i-nonbreaking{-ie7-icon:"\e025"}.mce-i-template{-ie7-icon:"\e026"}.mce-i-pagebreak{-ie7-icon:"\e027"}.mce-i-restoredraft{-ie7-icon:"\e028"}.mce-i-untitled{-ie7-icon:"\e029"}.mce-i-bold{-ie7-icon:"\e02a"}.mce-i-italic{-ie7-icon:"\e02b"}.mce-i-underline{-ie7-icon:"\e02c"}.mce-i-strikethrough{-ie7-icon:"\e02d"}.mce-i-visualchars{-ie7-icon:"\e02e"}.mce-i-ltr{-ie7-icon:"\e02f"}.mce-i-rtl{-ie7-icon:"\e030"}.mce-i-copy{-ie7-icon:"\e031"}.mce-i-resize{-ie7-icon:"\e032"}.mce-i-browse{-ie7-icon:"\e034"}.mce-i-pastetext{-ie7-icon:"\e035"}.mce-i-rotateleft{-ie7-icon:"\eaa8"}.mce-i-rotateright{-ie7-icon:"\eaa9"}.mce-i-crop{-ie7-icon:"\ee78"}.mce-i-editimage{-ie7-icon:"\e914"}.mce-i-options{-ie7-icon:"\ec6a"}.mce-i-flipv{-ie7-icon:"\eaaa"}.mce-i-fliph{-ie7-icon:"\eaac"}.mce-i-zoomin{-ie7-icon:"\eb35"}.mce-i-zoomout{-ie7-icon:"\eb36"}.mce-i-sun{-ie7-icon:"\eccc"}.mce-i-moon{-ie7-icon:"\eccd"}.mce-i-arrowleft{-ie7-icon:"\edc0"}.mce-i-arrowright{-ie7-icon:"\edb8"}.mce-i-drop{-ie7-icon:"\e934"}.mce-i-contrast{-ie7-icon:"\ecd4"}.mce-i-sharpen{-ie7-icon:"\eba7"}.mce-i-palette{-ie7-icon:"\e92a"}.mce-i-resize2{-ie7-icon:"\edf9"}.mce-i-orientation{-ie7-icon:"\e601"}.mce-i-invert{-ie7-icon:"\e602"}.mce-i-gamma{-ie7-icon:"\e600"}.mce-i-remove{-ie7-icon:"\ed6a"}.mce-i-codesample{-ie7-icon:"\e603"}.mce-i-checkbox,.mce-i-selected{-ie7-icon:"\e033"}.mce-i-selected{visibility:hidden}.mce-i-backcolor{background:#bbb} \ No newline at end of file diff --git a/www/js/tinymce/skins/lightgray/skin.min.css b/www/js/tinymce/skins/lightgray/skin.min.css new file mode 100644 index 0000000..4359855 --- /dev/null +++ b/www/js/tinymce/skins/lightgray/skin.min.css @@ -0,0 +1 @@ +.mce-container,.mce-container *,.mce-widget,.mce-widget *,.mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-widget button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:inherit !important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}div.mce-edit-area{background:#fff;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid rgba(0,0,0,0.2);width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#d9d9d9}.mce-grid td.mce-grid-cell div{border:1px solid #d6d6d6;width:15px;height:15px;margin:0;cursor:pointer}.mce-grid td.mce-grid-cell div:focus{border-color:#3498db}.mce-grid td.mce-grid-cell div[disabled]{cursor:not-allowed}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover,.mce-grid a:focus{border-color:#3498db}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#d6d6d6;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#3498db;background:#3498db}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%}.mce-colorbtn-trans div{text-align:center;vertical-align:middle;font-weight:bold;font-size:20px;line-height:16px;color:#707070}.mce-monospace{font-family:"Courier New",Courier,monospace}.mce-toolbar-grp{padding:2px 0}.mce-toolbar-grp .mce-flow-layout-item{margin-bottom:0}.mce-rtl .mce-wordcount{left:0;right:auto}.mce-croprect-container{position:absolute;top:0;left:0}.mce-croprect-handle{position:absolute;top:0;left:0;width:20px;height:20px;border:2px solid white}.mce-croprect-handle-nw{border-width:2px 0 0 2px;margin:-2px 0 0 -2px;cursor:nw-resize;top:100px;left:100px}.mce-croprect-handle-ne{border-width:2px 2px 0 0;margin:-2px 0 0 -20px;cursor:ne-resize;top:100px;left:200px}.mce-croprect-handle-sw{border-width:0 0 2px 2px;margin:-20px 2px 0 -2px;cursor:sw-resize;top:200px;left:100px}.mce-croprect-handle-se{border-width:0 2px 2px 0;margin:-20px 0 0 -20px;cursor:se-resize;top:200px;left:200px}.mce-croprect-handle-move{position:absolute;cursor:move;border:0}.mce-croprect-block{opacity:.3;filter:alpha(opacity=30);zoom:1;position:absolute;background:black}.mce-croprect-handle:focus{border-color:#3498db}.mce-croprect-handle-move:focus{outline:1px solid #3498db}.mce-imagepanel{overflow:auto;background:black}.mce-imagepanel img{position:absolute}.mce-imagetool.mce-btn .mce-ico{display:block;width:20px;height:20px;text-align:center;line-height:20px;font-size:20px;padding:5px}.mce-arrow-up{margin-top:12px}.mce-arrow-down{margin-top:-12px}.mce-arrow:before,.mce-arrow:after{position:absolute;left:50%;display:block;width:0;height:0;border-style:solid;border-color:transparent;content:""}.mce-arrow.mce-arrow-up:before{top:-9px;border-bottom-color:rgba(0,0,0,0.2);border-width:0 9px 9px;margin-left:-9px}.mce-arrow.mce-arrow-down:before{bottom:-9px;border-top-color:rgba(0,0,0,0.2);border-width:9px 9px 0;margin-left:-9px}.mce-arrow.mce-arrow-up:after{top:-8px;border-bottom-color:#f0f0f0;border-width:0 8px 8px;margin-left:-8px}.mce-arrow.mce-arrow-down:after{bottom:-8px;border-top-color:#f0f0f0;border-width:8px 8px 0;margin-left:-8px}.mce-arrow.mce-arrow-left:before,.mce-arrow.mce-arrow-left:after{margin:0}.mce-arrow.mce-arrow-left:before{left:8px}.mce-arrow.mce-arrow-left:after{left:9px}.mce-arrow.mce-arrow-right:before,.mce-arrow.mce-arrow-right:after{left:auto;margin:0}.mce-arrow.mce-arrow-right:before{right:8px}.mce-arrow.mce-arrow-right:after{right:9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left:before{left:-9px;top:50%;border-right-color:rgba(0,0,0,0.2);border-width:9px 9px 9px 0;margin-top:-9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left:after{left:-8px;top:50%;border-right-color:#f0f0f0;border-width:8px 8px 8px 0;margin-top:-8px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left{margin-left:12px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right:before{right:-9px;top:50%;border-left-color:rgba(0,0,0,0.2);border-width:9px 0 9px 9px;margin-top:-9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right:after{right:-8px;top:50%;border-left-color:#f0f0f0;border-width:8px 0 8px 8px;margin-top:-8px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right{margin-left:-14px}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#aaa;opacity:.6;filter:alpha(opacity=60);zoom:1}.mce-scroll{position:relative}.mce-panel{border:0 solid #cacaca;border:0 solid rgba(0,0,0,0.2);background-color:#f0f0f0}.mce-floatpanel{position:absolute}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;top:0;left:0;background:#fff;border:1px solid rgba(0,0,0,0.2);border:1px solid rgba(0,0,0,0.25)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px;*margin-top:0}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:rgba(0,0,0,0.2);border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#fff;position:fixed;top:0;left:0;opacity:0;transform:scale(.1);transition:transform 100ms ease-in,opacity 150ms ease-in}.mce-window.mce-in{transform:scale(1);opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #c5c5c5;position:relative}.mce-window-head .mce-close{position:absolute;right:0;top:0;height:38px;width:38px;text-align:center;cursor:pointer}.mce-window-head .mce-close i{color:#858585}.mce-close:hover i{color:#adadad}.mce-window-head .mce-title{line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:20px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:#fff;border-top:1px solid #c5c5c5}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window-body .mce-listbox{border-color:#ccc}.mce-rtl .mce-window-head .mce-close{position:absolute;right:auto;left:15px}.mce-rtl .mce-window-head .mce-dragh{left:auto;right:0}.mce-rtl .mce-window-head .mce-title{direction:rtl;text-align:right}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:white;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-n .mce-tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:none;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-progress{display:inline-block;position:relative;height:20px}.mce-progress .mce-bar-container{display:inline-block;width:100px;height:100%;margin-right:8px;border:1px solid #ccc;overflow:hidden}.mce-progress .mce-text{display:inline-block;margin-top:auto;margin-bottom:auto;font-size:14px;width:40px;color:#333}.mce-bar{display:block;width:0;height:100%;background-color:#d7d7d7;-webkit-transition:width .2s ease;transition:width .2s ease}.mce-notification{position:absolute;background-color:#f0f0f0;padding:5px;margin-top:5px;border-width:1px;border-style:solid;border-color:#ccc;transition:transform 100ms ease-in,opacity 150ms ease-in;opacity:0}.mce-notification.mce-in{opacity:1}.mce-notification-success{background-color:#dff0d8;border-color:#d6e9c6}.mce-notification-info{background-color:#d9edf7;border-color:#779ecb}.mce-notification-warning{background-color:#fcf8e3;border-color:#faebcc}.mce-notification-error{background-color:#f2dede;border-color:#ebccd1}.mce-notification.mce-has-close{padding-right:15px}.mce-notification .mce-ico{margin-top:5px}.mce-notification-inner{display:inline-block;font-size:14px;margin:5px 8px 4px 8px;text-align:center;white-space:normal;color:#31708f}.mce-notification-inner a{text-decoration:underline;cursor:pointer}.mce-notification .mce-progress{margin-right:8px}.mce-notification .mce-progress .mce-text{margin-top:5px}.mce-notification *,.mce-notification .mce-progress .mce-text{color:#333}.mce-notification .mce-progress .mce-bar-container{border-color:#ccc}.mce-notification .mce-progress .mce-bar-container .mce-bar{background-color:#333}.mce-notification-success *,.mce-notification-success .mce-progress .mce-text{color:#3c763d}.mce-notification-success .mce-progress .mce-bar-container{border-color:#d6e9c6}.mce-notification-success .mce-progress .mce-bar-container .mce-bar{background-color:#3c763d}.mce-notification-info *,.mce-notification-info .mce-progress .mce-text{color:#31708f}.mce-notification-info .mce-progress .mce-bar-container{border-color:#779ecb}.mce-notification-info .mce-progress .mce-bar-container .mce-bar{background-color:#31708f}.mce-notification-warning *,.mce-notification-warning .mce-progress .mce-text{color:#8a6d3b}.mce-notification-warning .mce-progress .mce-bar-container{border-color:#faebcc}.mce-notification-warning .mce-progress .mce-bar-container .mce-bar{background-color:#8a6d3b}.mce-notification-error *,.mce-notification-error .mce-progress .mce-text{color:#a94442}.mce-notification-error .mce-progress .mce-bar-container{border-color:#ebccd1}.mce-notification-error .mce-progress .mce-bar-container .mce-bar{background-color:#a94442}.mce-notification .mce-close{position:absolute;top:6px;right:8px;font-size:20px;font-weight:bold;line-height:20px;color:#858585;cursor:pointer;height:20px;overflow:hidden}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-btn{border:1px solid #b1b1b1;border-color:transparent transparent transparent transparent;position:relative;text-shadow:0 1px 1px rgba(255,255,255,0.75);display:inline-block;*display:inline;*zoom:1;background-color:#f0f0f0}.mce-btn:hover,.mce-btn:focus{color:#333;background-color:#e3e3e3;border-color:#ccc}.mce-btn.mce-disabled button,.mce-btn.mce-disabled:hover button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{background-color:#dbdbdb;border-color:#ccc}.mce-btn:active{background-color:#e0e0e0;border-color:#ccc}.mce-btn button{padding:4px 8px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#333;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px none}.mce-primary.mce-btn-has-text{min-width:50px}.mce-primary{color:#fff;border:1px solid transparent;border-color:transparent;background-color:#2d8ac7}.mce-primary:hover,.mce-primary:focus{background-color:#257cb6;border-color:transparent}.mce-primary.mce-disabled button,.mce-primary.mce-disabled:hover button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-primary.mce-active,.mce-primary.mce-active:hover,.mce-primary:not(.mce-disabled):active{background-color:#206ea1}.mce-primary button,.mce-primary button i{color:#fff;text-shadow:1px 1px none}.mce-btn .mce-txt{font-size:inherit;line-height:inherit;color:inherit}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:1px 5px;font-size:12px;*padding-bottom:2px}.mce-btn-small i{line-height:20px;vertical-align:top;*line-height:18px}.mce-btn .mce-caret{margin-top:8px;margin-left:0}.mce-btn-small .mce-caret{margin-top:8px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #333;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#aaa}.mce-caret.mce-up{border-bottom:4px solid #333;border-top:0}.mce-btn-flat{border:0;background:transparent;filter:none}.mce-btn-flat:hover,.mce-btn-flat.mce-active,.mce-btn-flat:focus,.mce-btn-flat:active{border:0;background:#e6e6e6;filter:none}.mce-btn-has-text .mce-ico{padding-right:5px}.mce-rtl .mce-btn button{direction:rtl}.mce-btn-group .mce-btn{border-width:1px;margin:0;margin-left:2px}.mce-btn-group:not(:first-child){border-left:1px solid #d9d9d9;padding-left:3px;margin-left:3px}.mce-btn-group .mce-first{margin-left:0}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-rtl .mce-btn-group .mce-btn{margin-left:0;margin-right:2px}.mce-rtl .mce-btn-group .mce-first{margin-right:0}.mce-rtl .mce-btn-group:not(:first-child){border-left:none;border-right:1px solid #d9d9d9;padding-right:4px;margin-right:4px}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;background-color:#f0f0f0;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0;overflow:hidden}.mce-checked i.mce-i-checkbox{color:#333;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox,.mce-checkbox.mce-focus i.mce-i-checkbox{border:1px solid rgba(82,168,236,0.8)}.mce-checkbox.mce-disabled .mce-label,.mce-checkbox.mce-disabled i.mce-i-checkbox{color:#acacac}.mce-checkbox .mce-label{vertical-align:middle}.mce-rtl .mce-checkbox{direction:rtl;text-align:right}.mce-rtl i.mce-i-checkbox{margin:0 0 0 3px}.mce-combobox{display:inline-block;*display:inline;*zoom:1;*height:32px}.mce-combobox input{border:1px solid #c5c5c5;border-right-color:#c5c5c5;height:28px}.mce-combobox.mce-disabled input{color:#adadad}.mce-combobox .mce-btn{border:1px solid #c5c5c5;border-left:0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox.mce-disabled .mce-btn button{cursor:default;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-colorbox i{border:1px solid #c5c5c5;width:14px;height:14px}.mce-colorbutton .mce-ico{position:relative}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:6px;padding-left:6px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-17px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-16px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;padding-right:4px;border-left:1px solid transparent}.mce-colorbutton:hover .mce-open{border-color:#ccc}.mce-colorbutton.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-colorbutton{direction:rtl}.mce-rtl .mce-colorbutton .mce-preview{margin-left:0;padding-right:0;padding-left:3px}.mce-rtl .mce-colorbutton.mce-btn-small .mce-preview{margin-left:0;padding-right:0;padding-left:2px}.mce-rtl .mce-colorbutton .mce-open{padding-left:4px;padding-right:4px;border-left:0}.mce-colorpicker{position:relative;width:250px;height:220px}.mce-colorpicker-sv{position:absolute;top:0;left:0;width:90%;height:100%;border:1px solid #c5c5c5;cursor:crosshair;overflow:hidden}.mce-colorpicker-h-chunk{width:100%}.mce-colorpicker-overlay1,.mce-colorpicker-overlay2{width:100%;height:100%;position:absolute;top:0;left:0}.mce-colorpicker-overlay1{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr='#ffffff', endColorstr='#00ffffff');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')";background:linear-gradient(to right, #fff, rgba(255,255,255,0))}.mce-colorpicker-overlay2{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#00000000', endColorstr='#000000');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')";background:linear-gradient(to bottom, rgba(0,0,0,0), #000)}.mce-colorpicker-selector1{background:none;position:absolute;width:12px;height:12px;margin:-8px 0 0 -8px;border:1px solid black;border-radius:50%}.mce-colorpicker-selector2{position:absolute;width:10px;height:10px;border:1px solid white;border-radius:50%}.mce-colorpicker-h{position:absolute;top:0;right:0;width:6.5%;height:100%;border:1px solid #c5c5c5;cursor:crosshair}.mce-colorpicker-h-marker{margin-top:-4px;position:absolute;top:0;left:-1px;width:100%;border:1px solid #333;background:#fff;height:4px;z-index:100}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#333}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:#666;color:#fff}.mce-path .mce-divider{display:inline}.mce-disabled .mce-path-item{color:#aaa}.mce-rtl .mce-path{direction:rtl}.mce-fieldset{border:0 solid #9e9e9e}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-rtl .mce-flow-layout{text-align:right;direction:rtl}.mce-rtl .mce-flow-layout-item{margin:2px 2px 2px 0}.mce-rtl .mce-flow-layout-item.mce-last{margin-left:2px}.mce-iframe{border:0 solid rgba(0,0,0,0.2);width:100%;height:100%}.mce-infobox{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);overflow:hidden;border:1px solid red}.mce-infobox div{display:block;margin:5px}.mce-infobox div button{position:absolute;top:50%;right:4px;cursor:pointer;margin-top:-8px;display:none}.mce-infobox div button:focus{outline:2px solid #ccc}.mce-infobox.mce-has-help div{margin-right:25px}.mce-infobox.mce-has-help button{display:block}.mce-infobox.mce-success{background:#dff0d8;border-color:#d6e9c6}.mce-infobox.mce-success div{color:#3c763d}.mce-infobox.mce-warning{background:#fcf8e3;border-color:#faebcc}.mce-infobox.mce-warning div{color:#8a6d3b}.mce-infobox.mce-error{background:#f2dede;border-color:#ebccd1}.mce-infobox.mce-error div{color:#a94442}.mce-rtl .mce-infobox div{text-align:right;direction:rtl}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label.mce-disabled{color:#aaa}.mce-label.mce-multiline{white-space:pre-wrap}.mce-label.mce-success{color:#468847}.mce-label.mce-warning{color:#c09853}.mce-label.mce-error{color:#b94a48}.mce-rtl .mce-label{text-align:right;direction:rtl}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;filter:none}.mce-menubar .mce-menubtn button{color:#333}.mce-menubar{border:1px solid rgba(217,217,217,0.52)}.mce-menubar .mce-menubtn button span{color:#333}.mce-menubar .mce-caret{border-top-color:#333}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:#ccc;background:#fff;filter:none}.mce-menubtn button{color:#333}.mce-menubtn.mce-btn-small span{font-size:12px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-menubtn .mce-caret{*margin-top:6px}.mce-rtl .mce-menubtn button{direction:rtl;text-align:right}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item .mce-ico,.mce-menu-item .mce-text{color:#333}.mce-menu-item.mce-disabled .mce-text,.mce-menu-item.mce-disabled .mce-ico{color:#adadad}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text,.mce-menu-item:focus .mce-text{color:white}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:white}.mce-menu-item.mce-disabled:hover{background:#ccc}.mce-menu-shortcut{display:inline-block;color:#adadad}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 15px 0 20px}.mce-menu-item:hover .mce-menu-shortcut,.mce-menu-item.mce-selected .mce-menu-shortcut,.mce-menu-item:focus .mce-menu-shortcut{color:white}.mce-menu-item .mce-caret{margin-top:4px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #333}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret,.mce-menu-item:hover .mce-caret{border-left-color:white}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item-normal.mce-active{background-color:#3498db}.mce-menu-item-preview.mce-active{border-left:5px solid #aaa}.mce-menu-item-normal.mce-active .mce-text{color:white}.mce-menu-item-normal.mce-active:hover .mce-text,.mce-menu-item-normal.mce-active:hover .mce-ico{color:white}.mce-menu-item-normal.mce-active:focus .mce-text,.mce-menu-item-normal.mce-active:focus .mce-ico{color:white}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:white;background-color:#2d8ac7}div.mce-menu .mce-menu-item-sep,.mce-menu-item-sep:hover{border:0;padding:0;height:1px;margin:9px 1px;overflow:hidden;background:transparent;border-bottom:1px solid rgba(0,0,0,0.1);cursor:default;filter:none}.mce-menu.mce-rtl{direction:rtl}.mce-rtl .mce-menu-item{text-align:right;direction:rtl;padding:6px 12px 6px 15px}.mce-menu-align.mce-rtl .mce-menu-shortcut,.mce-menu-align.mce-rtl .mce-caret{right:auto;left:0}.mce-rtl .mce-menu-item .mce-caret{margin-left:6px;margin-right:0;border-right:4px solid #333;border-left:0}.mce-rtl .mce-menu-item.mce-selected .mce-caret,.mce-rtl .mce-menu-item:focus .mce-caret,.mce-rtl .mce-menu-item:hover .mce-caret{border-left-color:transparent;border-right-color:white}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}.mce-throbber-inline{position:static;height:50px}.mce-menu .mce-throbber-inline{height:25px;background-size:contain}.mce-menu{position:absolute;left:0;top:0;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:-1px 0 0;min-width:160px;background:#fff;border:1px solid #989898;border:1px solid rgba(0,0,0,0.2);z-index:1002;max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-rtl .mce-listbox .mce-caret{right:auto;left:8px}.mce-rtl .mce-listbox button{padding-right:10px;padding-left:20px}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#333}.mce-selectbox{background:#fff;border:1px solid #c5c5c5}.mce-slider{border:1px solid #aaa;background:#eee;width:100px;height:10px;position:relative;display:block}.mce-slider.mce-vertical{width:10px;height:100px}.mce-slider-handle{border:1px solid #bbb;background:#ddd;display:block;width:13px;height:13px;position:absolute;top:0;left:0;margin-left:-1px;margin-top:-2px}.mce-slider-handle:focus{background:#bbb}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#ccc}.mce-splitbtn button{padding-right:6px;padding-left:6px}.mce-splitbtn .mce-open{padding-right:4px;padding-left:4px}.mce-splitbtn .mce-open.mce-active{background-color:#dbdbdb;outline:1px solid #ccc}.mce-splitbtn.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-splitbtn{direction:rtl;text-align:right}.mce-rtl .mce-splitbtn button{padding-right:4px;padding-left:4px}.mce-rtl .mce-splitbtn .mce-open{border-left:0}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #c5c5c5}.mce-tabs,.mce-tabs+.mce-container-body{background:#fff}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #c5c5c5;border-width:0 1px 0 0;background:#fff;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-rtl .mce-tabs{text-align:right;direction:rtl}.mce-rtl .mce-tab{border-width:0 0 0 1px}.mce-textbox{background:#fff;border:1px solid #c5c5c5;display:inline-block;-webkit-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#333}.mce-textbox:focus,.mce-textbox.mce-focus{border-color:#3498db}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px;height:auto}.mce-textbox.mce-disabled{color:#adadad}.mce-rtl .mce-textbox{text-align:right;direction:rtl}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce',Arial;font-style:normal;font-weight:normal;font-variant:normal;font-size:16px;line-height:16px;speak:none;vertical-align:text-top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;background:transparent center center;background-size:cover;width:16px;height:16px;color:#333}.mce-btn-small .mce-ico{font-family:'tinymce-small',Arial}.mce-i-save:before{content:"\e000"}.mce-i-newdocument:before{content:"\e001"}.mce-i-fullpage:before{content:"\e002"}.mce-i-alignleft:before{content:"\e003"}.mce-i-aligncenter:before{content:"\e004"}.mce-i-alignright:before{content:"\e005"}.mce-i-alignjustify:before{content:"\e006"}.mce-i-alignnone:before{content:"\e003"}.mce-i-cut:before{content:"\e007"}.mce-i-paste:before{content:"\e008"}.mce-i-searchreplace:before{content:"\e009"}.mce-i-bullist:before{content:"\e00a"}.mce-i-numlist:before{content:"\e00b"}.mce-i-indent:before{content:"\e00c"}.mce-i-outdent:before{content:"\e00d"}.mce-i-blockquote:before{content:"\e00e"}.mce-i-undo:before{content:"\e00f"}.mce-i-redo:before{content:"\e010"}.mce-i-link:before{content:"\e011"}.mce-i-unlink:before{content:"\e012"}.mce-i-anchor:before{content:"\e013"}.mce-i-image:before{content:"\e014"}.mce-i-media:before{content:"\e015"}.mce-i-help:before{content:"\e016"}.mce-i-code:before{content:"\e017"}.mce-i-insertdatetime:before{content:"\e018"}.mce-i-preview:before{content:"\e019"}.mce-i-forecolor:before{content:"\e01a"}.mce-i-backcolor:before{content:"\e01a"}.mce-i-table:before{content:"\e01b"}.mce-i-hr:before{content:"\e01c"}.mce-i-removeformat:before{content:"\e01d"}.mce-i-subscript:before{content:"\e01e"}.mce-i-superscript:before{content:"\e01f"}.mce-i-charmap:before{content:"\e020"}.mce-i-emoticons:before{content:"\e021"}.mce-i-print:before{content:"\e022"}.mce-i-fullscreen:before{content:"\e023"}.mce-i-spellchecker:before{content:"\e024"}.mce-i-nonbreaking:before{content:"\e025"}.mce-i-template:before{content:"\e026"}.mce-i-pagebreak:before{content:"\e027"}.mce-i-restoredraft:before{content:"\e028"}.mce-i-bold:before{content:"\e02a"}.mce-i-italic:before{content:"\e02b"}.mce-i-underline:before{content:"\e02c"}.mce-i-strikethrough:before{content:"\e02d"}.mce-i-visualchars:before{content:"\e02e"}.mce-i-visualblocks:before{content:"\e02e"}.mce-i-ltr:before{content:"\e02f"}.mce-i-rtl:before{content:"\e030"}.mce-i-copy:before{content:"\e031"}.mce-i-resize:before{content:"\e032"}.mce-i-browse:before{content:"\e034"}.mce-i-pastetext:before{content:"\e035"}.mce-i-rotateleft:before{content:"\eaa8"}.mce-i-rotateright:before{content:"\eaa9"}.mce-i-crop:before{content:"\ee78"}.mce-i-editimage:before{content:"\e915"}.mce-i-options:before{content:"\ec6a"}.mce-i-flipv:before{content:"\eaaa"}.mce-i-fliph:before{content:"\eaac"}.mce-i-zoomin:before{content:"\eb35"}.mce-i-zoomout:before{content:"\eb36"}.mce-i-sun:before{content:"\eccc"}.mce-i-moon:before{content:"\eccd"}.mce-i-arrowleft:before{content:"\edc0"}.mce-i-arrowright:before{content:"\e93c"}.mce-i-drop:before{content:"\e935"}.mce-i-contrast:before{content:"\ecd4"}.mce-i-sharpen:before{content:"\eba7"}.mce-i-resize2:before{content:"\edf9"}.mce-i-orientation:before{content:"\e601"}.mce-i-invert:before{content:"\e602"}.mce-i-gamma:before{content:"\e600"}.mce-i-remove:before{content:"\ed6a"}.mce-i-tablerowprops:before{content:"\e604"}.mce-i-tablecellprops:before{content:"\e605"}.mce-i-table2:before{content:"\e606"}.mce-i-tablemergecells:before{content:"\e607"}.mce-i-tableinsertcolbefore:before{content:"\e608"}.mce-i-tableinsertcolafter:before{content:"\e609"}.mce-i-tableinsertrowbefore:before{content:"\e60a"}.mce-i-tableinsertrowafter:before{content:"\e60b"}.mce-i-tablesplitcells:before{content:"\e60d"}.mce-i-tabledelete:before{content:"\e60e"}.mce-i-tableleftheader:before{content:"\e62a"}.mce-i-tabletopheader:before{content:"\e62b"}.mce-i-tabledeleterow:before{content:"\e800"}.mce-i-tabledeletecol:before{content:"\e801"}.mce-i-codesample:before{content:"\e603"}.mce-i-fill:before{content:"\e902"}.mce-i-borderwidth:before{content:"\e903"}.mce-i-line:before{content:"\e904"}.mce-i-count:before{content:"\e905"}.mce-i-translate:before{content:"\e907"}.mce-i-drag:before{content:"\e908"}.mce-i-home:before{content:"\e90b"}.mce-i-upload:before{content:"\e914"}.mce-i-bubble:before{content:"\e91c"}.mce-i-user:before{content:"\e91d"}.mce-i-lock:before{content:"\e926"}.mce-i-unlock:before{content:"\e927"}.mce-i-settings:before{content:"\e928"}.mce-i-remove2:before{content:"\e92a"}.mce-i-menu:before{content:"\e92d"}.mce-i-warning:before{content:"\e930"}.mce-i-question:before{content:"\e931"}.mce-i-pluscircle:before{content:"\e932"}.mce-i-info:before{content:"\e933"}.mce-i-notice:before{content:"\e934"}.mce-i-arrowup:before{content:"\e93b"}.mce-i-arrowdown:before{content:"\e93d"}.mce-i-arrowup2:before{content:"\e93f"}.mce-i-arrowdown2:before{content:"\e940"}.mce-i-menu2:before{content:"\e941"}.mce-i-newtab:before{content:"\e961"}.mce-i-a11y:before{content:"\e900"}.mce-i-plus:before{content:"\e93a"}.mce-i-minus:before{content:"\e939"}.mce-i-books:before{content:"\e911"}.mce-i-checkmark:before{content:"\e033"}.mce-i-checkbox:before,.mce-i-selected:before{content:"\e033"}.mce-i-selected{visibility:hidden}i.mce-i-backcolor{text-shadow:none;background:#bbb} \ No newline at end of file diff --git a/www/js/tinymce/themes/inlite/theme.min.js b/www/js/tinymce/themes/inlite/theme.min.js new file mode 100644 index 0000000..09f7100 --- /dev/null +++ b/www/js/tinymce/themes/inlite/theme.min.js @@ -0,0 +1 @@ +!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;g>i;++i)h[i]=d(e[i]);var j=f.apply(null,h);if(void 0===j)throw"module ["+b+"] returned undefined";c.instance=j},c=function(b,c,d){if("string"!=typeof b)throw"module id must be a string";if(void 0===c)throw"no dependencies for "+b;if(void 0===d)throw"no definition function for "+b;a[b]={deps:c,defn:d,instance:void 0}},d=function(c){var d=a[c];if(void 0===d)throw"module ["+c+"] was undefined";return void 0===d.instance&&b(c),d.instance},e=function(a,b){for(var c=a.length,e=new Array(c),f=0;c>f;++f)e.push(d(a[f]));b.apply(null,b)},f={};f.bolt={module:{api:{define:c,require:e,demand:d}}};var g=c,h=function(a,b){g(a,[],function(){return b})};h("1",tinymce.ThemeManager),h("2",tinymce.util.Delay),h("c",tinymce.util.Tools),h("d",tinymce.ui.Factory),h("e",tinymce.DOM),g("j",[],function(){var a=function(a){return function(b){return typeof b===a}},b=function(a){return Array.isArray(a)},c=function(a){return null===a},d=function(a){return function(d){return!c(d)&&!b(d)&&a(d)}};return{isString:a("string"),isNumber:a("number"),isBoolean:a("boolean"),isFunction:a("function"),isObject:d(a("object")),isNull:c,isArray:b}}),g("f",["c","d","j"],function(a,b,c){var d=function(a,b){return function(c,d){for(var e,f=d.parents.length;f--&&(e=d.parents[f].nodeName,"OL"!=e&&"UL"!=e););a.active(c&&e==b)}},e=function(a,b){var c=function(a,b){return{selector:a,handler:b}},e=function(a){b.active(a)},f=function(a){b.disabled(a)};return"bullist"==a?c("ul > li",d(b,"UL")):"numlist"==a?c("ol > li",d(b,"OL")):b.settings.stateSelector?c(b.settings.stateSelector,e):b.settings.disabledStateSelector?c(b.settings.disabledStateSelector,f):null},f=function(a,b,c){return function(){var d=e(b,c);null!==d&&a.selection.selectorChanged(d.selector,d.handler)}},g=function(a){return c.isArray(a)?a:c.isString(a)?a.split(/[ ,]/):[]},h=function(c,d,e){var h,i=[];if(e)return a.each(g(e),function(a){var d;"|"==a?h=null:b.has(a)?(a={type:a},i.push(a),h=null):c.buttons[a]&&(h||(h={type:"buttongroup",items:[]},i.push(h)),d=a,a=c.buttons[d],"function"==typeof a&&(a=a()),a.type=a.type||"button",a=b.create(a),a.on("postRender",f(c,d,a)),h.items.push(a))}),b.create({type:"toolbar",layout:"flow",name:d,items:i})};return{create:h}}),h("o",tinymce.util.Promise),g("p",[],function(){var a=0,b=function(){var a=function(){return Math.round(4294967295*Math.random()).toString(36)};return"s"+Date.now().toString(36)+a()+a()+a()},c=function(c){return c+a++ +b()};return{uuid:c}}),g("u",[],function(){var a=function(a,b){function c(c){var e,f,g;f=b[c?"startContainer":"endContainer"],g=b[c?"startOffset":"endOffset"],1==f.nodeType&&(e=a.create("span",{"data-mce-type":"bookmark"}),f.hasChildNodes()?(g=Math.min(g,f.childNodes.length-1),c?f.insertBefore(e,f.childNodes[g]):a.insertAfter(e,f.childNodes[g])):f.appendChild(e),f=e,g=0),d[c?"startContainer":"endContainer"]=f,d[c?"startOffset":"endOffset"]=g}var d={};return c(!0),b.collapsed||c(),d},b=function(a,b){function c(c){function d(a){for(var b=a.parentNode.firstChild,c=0;b;){if(b==a)return c;1==b.nodeType&&"bookmark"==b.getAttribute("data-mce-type")||c++,b=b.nextSibling}return-1}var e,f,g;e=g=b[c?"startContainer":"endContainer"],f=b[c?"startOffset":"endOffset"],e&&(1==e.nodeType&&(f=d(e),e=e.parentNode,a.remove(g)),b[c?"startContainer":"endContainer"]=e,b[c?"startOffset":"endOffset"]=f)}c(!0),c();var d=a.createRng();return d.setStart(b.startContainer,b.startOffset),b.endContainer&&d.setEnd(b.endContainer,b.endOffset),d};return{create:a,resolve:b}}),h("v",tinymce.dom.TreeWalker),h("w",tinymce.dom.RangeUtils),g("q",["u","c","v","w"],function(a,b,c,d){var e=function(a,b,d){var e,f,g=[];for(e=new c(b,a),f=b;f&&(1===f.nodeType&&g.push(f),f!==d);f=e.next());return g},f=function(c,d){var e,f,g;f=c.dom,g=c.selection,e=a.create(f,g.getRng()),b.each(d,function(a){c.dom.remove(a,!0)}),g.setRng(a.resolve(f,e))},g=function(a){return"A"===a.nodeName&&a.hasAttribute("href")},h=function(a,b){var c=a.getParent(b,g);return c?c:b},i=function(a){var c,f,i,j,k,l,m;return k=a.selection,l=a.dom,m=k.getRng(),c=h(l,d.getNode(m.startContainer,m.startOffset)),f=d.getNode(m.endContainer,m.endOffset),i=a.getBody(),j=b.grep(e(i,c,f),g)},j=function(a){f(a,i(a))};return{unlinkSelection:j}}),g("m",["p","q"],function(a,b){var c=function(a,b){var c,d,e;for(e='
    ',e+="",d=0;b>d;d++){for(e+="",c=0;a>c;c++)e+="";e+=""}return e+="",e+="

    "},d=function(a){var b=a.dom.select("*[data-mce-id]");return b[0]},e=function(a,b,e){a.undoManager.transact(function(){var f,g;a.insertContent(c(b,e)),f=d(a),f.removeAttribute("data-mce-id"),g=a.dom.select("td,th",f),a.selection.setCursorLocation(g[0],0)})},f=function(a,b){a.execCommand("FormatBlock",!1,b)},g=function(b,c,d){var e,f;e=b.editorUpload.blobCache,f=e.create(a.uuid("mceu"),d,c),e.add(f),b.insertContent(b.dom.createHTML("img",{src:f.blobUri()}))},h=function(a){a.selection.collapse(!1)},i=function(a){a.focus(),b.unlinkSelection(a),h(a)},j=function(a,b,c){a.focus(),a.dom.setAttrib(b,"href",c),h(a)},k=function(a,b){a.execCommand("mceInsertLink",!1,{href:b}),h(a)},l=function(a,b){var c=a.dom.getParent(a.selection.getStart(),"a[href]");c?j(a,c,b):k(a,b)},m=function(a,b){0===b.trim().length?i(a):l(a,b)};return{insertTable:e,formatBlock:f,insertBlob:g,createLink:m,unlink:i}}),g("r",[],function(){var a=function(a){return/^www\.|\.(com|org|edu|gov|uk|net|ca|de|jp|fr|au|us|ru|ch|it|nl|se|no|es|mil)$/i.test(a.trim())},b=function(a){return/^https?:\/\//.test(a.trim())};return{isDomainLike:a,isAbsolute:b}}),g("g",["c","d","o","m","r"],function(a,b,c,d,e){var f=function(a){a.find("textbox").eq(0).each(function(a){a.focus()})},g=function(c,d){var e=b.create(a.extend({type:"form",layout:"flex",direction:"row",padding:5,name:c,spacing:3},d));return e.on("show",function(){f(e)}),e},h=function(a,b){return b?a.show():a.hide()},i=function(a,b){return new c(function(c){a.windowManager.confirm("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",function(a){var d=a===!0?"http://"+b:b;c(d)})})},j=function(a,b){return!e.isAbsolute(b)&&e.isDomainLike(b)?i(a,b):c.resolve(b)},k=function(a,b){var c=function(){a.focus(),d.unlink(a),b()};return g("quicklink",{items:[{type:"button",name:"unlink",icon:"unlink",onclick:c,tooltip:"Remove link"},{type:"textbox",name:"linkurl",placeholder:"Paste or type a link"},{type:"button",icon:"checkmark",subtype:"primary",tooltip:"Ok",onclick:"submit"}],onshow:function(){var b,c="";b=a.dom.getParent(a.selection.getStart(),"a[href]"),b&&(c=a.dom.getAttrib(b,"href")),this.fromJSON({linkurl:c}),h(this.find("#unlink"),b)},onsubmit:function(c){j(a,c.data.linkurl).then(function(c){d.createLink(a,c),b()})}})};return{createQuickLinkForm:k}}),h("s",tinymce.geom.Rect),g("t",[],function(){var a=function(a){return{x:a.left,y:a.top,w:a.width,h:a.height}},b=function(a){return{left:a.x,top:a.y,width:a.w,height:a.h,right:a.x+a.w,bottom:a.y+a.h}};return{fromClientRect:a,toClientRect:b}}),g("h",["e","s","t"],function(a,b,c){var d=function(b){var c=a.getViewPort();return{x:b.x+c.x,y:b.y+c.y,w:b.w,h:b.h}},e=function(a){var b=a.getBoundingClientRect();return d({x:b.left,y:b.top,w:Math.max(a.clientWidth,a.offsetWidth),h:Math.max(a.clientHeight,a.offsetHeight)})},f=function(a,b){return e(b)},g=function(a){return e(a.getElement().ownerDocument.body)},h=function(a){return e(a.getContentAreaContainer()||a.getBody())},i=function(a){var b=a.selection.getBoundingClientRect();return b?d(c.fromClientRect(b)):null};return{getElementRect:f,getPageAreaRect:g,getContentAreaRect:h,getSelectionRect:i}}),g("i",["s","t"],function(a,b){var c=function(a,b){return{rect:a,position:b}},d=function(a,b){return{x:b.x,y:b.y,w:a.w,h:a.h}},e=function(b,e,f,g,h){var i,j,k;return i=a.findBestRelativePosition(h,f,g,b),f=a.clamp(f,g),i?(j=a.relativePosition(h,f,i),k=d(h,j),c(k,i)):(f=a.intersect(g,f),f?(i=a.findBestRelativePosition(h,f,g,e))?(j=a.relativePosition(h,f,i),k=d(h,j),c(k,i)):(k=d(h,f),c(k,i)):null)},f=function(a,b,c){return e(["cr-cl","cl-cr"],["bc-tc","bl-tl","br-tr"],a,b,c)},g=function(a,b,c){return e(["tc-bc","bc-tc","tl-bl","bl-tl","tr-br","br-tr"],["bc-tc","bl-tl","br-tr"],a,b,c)},h=function(a,c,d,e){var f;return"function"==typeof a?(f=a({elementRect:b.toClientRect(c),contentAreaRect:b.toClientRect(d),panelRect:b.toClientRect(e)}),b.fromClientRect(f)):e},i=function(a){return a.panelRect};return{calcInsert:f,calc:g,userConstrain:h,defaultHandler:i}}),g("a",["j"],function(a){var b=function(a,b){if(b(a))return!0;throw new Error("Default value doesn't match requested type.")},c=function(a){return function(c,d,e){var f=c.settings;return b(e,a),d in f&&a(f[d])?f[d]:e}},d=function(a,b){return a.split(b).filter(function(a){return a.length>0})},e=function(b,c){var e=function(a){return"string"==typeof a?d(a,/[ ,]/):a},f=function(a,b){return a===!1?[]:b};return a.isArray(b)?b:a.isString(b)?e(b):a.isBoolean(b)?f(b,c):c},f=function(a){return function(c,d,f){var g=d in c.settings?c.settings[d]:f;return b(f,a),e(g,f)}};return{getStringOr:c(a.isString),getBoolOr:c(a.isBoolean),getNumberOr:c(a.isNumber),getHandlerOr:c(a.isFunction),getToolbarItemsOr:f(a.isArray)}}),g("3",["c","d","e","f","g","h","i","a"],function(a,b,c,d,e,f,g,h){return function(){var i,j,k=["bold","italic","|","quicklink","h2","h3","blockquote"],l=["quickimage","quicktable"],m=function(b,c){return a.map(c,function(a){return d.create(b,a.id,a.items)})},n=function(a){return h.getToolbarItemsOr(a,"selection_toolbar",k)},o=function(a){return h.getToolbarItemsOr(a,"insert_toolbar",l)},p=function(a){return a.items().length>0},q=function(c,f){var g=m(c,f).concat([d.create(c,"text",n(c)),d.create(c,"insert",o(c)),e.createQuickLinkForm(c,z)]);return b.create({type:"floatpanel",role:"dialog",classes:"tinymce tinymce-inline arrow",ariaLabel:"Inline toolbar",layout:"flex",direction:"column",align:"stretch",autohide:!1,autofix:!0,fixed:!0,border:1,items:a.grep(g,p),oncancel:function(){c.focus()}})},r=function(a){a&&a.show()},s=function(a,b){a.moveTo(b.x,b.y)},t=function(b,c){c=c?c.substr(0,2):"",a.each({t:"down",b:"up",c:"center"},function(a,d){b.classes.toggle("arrow-"+a,d===c.substr(0,1))}),"cr"===c?(b.classes.toggle("arrow-left",!0),b.classes.toggle("arrow-right",!1)):"cl"===c?(b.classes.toggle("arrow-left",!0),b.classes.toggle("arrow-right",!0)):a.each({l:"left",r:"right"},function(a,d){b.classes.toggle("arrow-"+a,d===c.substr(1,1))})},u=function(a,b){var c=a.items().filter("#"+b);return c.length>0?(c[0].show(),a.reflow(),!0):!1},v=function(a,b,d,e){var i,k,l,m;return r(a),a.items().hide(),u(a,b)?(m=h.getHandlerOr(d,"inline_toolbar_position_handler",g.defaultHandler),i=f.getContentAreaRect(d),k=c.getRect(a.getEl()),l="insert"===b?g.calcInsert(e,i,k):g.calc(e,i,k),void(l?(k=l.rect,j=e,s(a,g.userConstrain(m,e,i,k)),t(a,l.position)):z(a))):void z(a)},w=function(){return i.items().filter("form:visible").length>0},x=function(a,b){if(i){if(i.items().hide(),!u(i,b))return void z(i);var d,e,k,l;r(i),i.items().hide(),u(i,b),l=h.getHandlerOr(a,"inline_toolbar_position_handler",g.defaultHandler),d=f.getContentAreaRect(a),e=c.getRect(i.getEl()),k=g.calc(j,d,e),k&&(e=k.rect,s(i,g.userConstrain(l,j,d,e)),t(i,k.position))}},y=function(a,b,c,d){i||(i=q(a,d),i.renderTo(document.body).reflow().moveTo(c.x,c.y),a.nodeChanged()),v(i,b,a,c)},z=function(){i&&i.hide()},A=function(){i&&i.find("toolbar:visible").eq(0).each(function(a){a.focus(!0)})},B=function(){i&&(i.remove(),i=null)},C=function(){return i&&i.visible()&&w()};return{show:y,showForm:x,inForm:C,hide:z,focus:A,remove:B}}}),g("k",["o"],function(a){var b=function(b){return new a(function(a){var c=new FileReader;c.onloadend=function(){a(c.result.split(",")[1])},c.readAsDataURL(b)})};return{blobToBase64:b}}),g("l",["o"],function(a){var b=function(){return new a(function(a){var b;b=document.createElement("input"),b.type="file",b.style.position="fixed",b.style.left=0,b.style.top=0,b.style.opacity=.001,document.body.appendChild(b),b.onchange=function(b){a(Array.prototype.slice.call(b.target.files))},b.click(),b.parentNode.removeChild(b)})};return{pickFile:b}}),g("4",["3","k","l","m"],function(a,b,c,d){var e=function(a){for(var b=function(b){return function(){d.formatBlock(a,b)}},c=1;6>c;c++){var e="h"+c;a.addButton(e,{text:e.toUpperCase(),tooltip:"Heading "+c,stateSelector:e,onclick:b(e),onPostRender:function(){var a=this.getEl().firstChild.firstChild;a.style.fontWeight="bold"}})}},f=function(a,f){a.addButton("quicklink",{icon:"link",tooltip:"Insert/Edit link",stateSelector:"a[href]",onclick:function(){f.showForm(a,"quicklink")}}),a.addButton("quickimage",{icon:"image",tooltip:"Insert image",onclick:function(){c.pickFile().then(function(c){var e=c[0];b.blobToBase64(e).then(function(b){d.insertBlob(a,b,e)})})}}),a.addButton("quicktable",{icon:"table",tooltip:"Insert table",onclick:function(){f.hide(),d.insertTable(a,2,2)}}),e(a)};return{addToEditor:f}}),h("n",tinymce.EditorManager),g("5",["n","e"],function(a,b){var c=function(a,b){var c=function(){a.fire("SkinLoaded"),b()};a.initialized?c():a.on("init",c)},d=function(b){var c=a.baseURL+"/skins/";return b?c+b:c+"lightgray"},e=function(a,b){return a.documentBaseURI.toAbsolute(b)},f=function(a,f){var g=a.settings,h=g.skin_url?e(a,g.skin_url):d(g.skin),i=function(){c(a,f)};b.styleSheetLoader.load(h+"/skin.min.css",i),a.contentCSS.push(h+"/content.inline.min.css")};return{load:f}}),g("8",[],function(){var a=function(a,b){return{id:a,rect:b}},b=function(a,b){for(var c=0;c li",c("UL")),"numlist"==g&&d.selectorChanged("ol > li",c("OL")),b.settings.stateSelector&&d.selectorChanged(b.settings.stateSelector,function(a){b.active(a)},!0),b.settings.disabledStateSelector&&d.selectorChanged(b.settings.disabledStateSelector,function(a){b.disabled(a)})}var g;"|"==b?d=null:n.has(b)?(b={type:b,size:c},e.push(b),d=null):(d||(d={type:"buttongroup",items:[]},e.push(d)),a.buttons[b]&&(g=b,b=a.buttons[g],"function"==typeof b&&(b=b()),b.type=b.type||"button",b.size=c,b=n.create(b),d.items.push(b),a.initialized?f():a.on("init",f)))}),{type:"toolbar",layout:"flow",items:e}}function c(a){function c(c){return c?(d.push(b(c,a)),!0):void 0}var d=[];if(tinymce.isArray(m.toolbar)){if(0===m.toolbar.length)return;tinymce.each(m.toolbar,function(a,b){m["toolbar"+(b+1)]=a}),delete m.toolbar}for(var e=1;10>e&&c(m["toolbar"+e]);e++);return d.length||m.toolbar===!1||c(m.toolbar||t),d.length?{type:"panel",layout:"stack",classes:"toolbar-grp",ariaRoot:!0,ariaRemember:!0,items:d}:void 0}function d(){function b(b){var c;return"|"==b?{text:"|"}:c=a.menuItems[b]}function c(c){var d,e,f,g,h;if(h=tinymce.makeMap((m.removed_menuitems||"").split(/[ ,]/)),m.menu?(e=m.menu[c],g=!0):e=s[c],e){d={text:e.title},f=[],o((e.items||"").split(/[ ,]/),function(a){var c=b(a);c&&!h[a]&&f.push(b(a))}),g||o(a.menuItems,function(a){a.context==c&&("before"==a.separator&&f.push({text:"|"}),a.prependToContext?f.unshift(a):f.push(a),"after"==a.separator&&f.push({text:"|"}))});for(var i=0;i=0;d--)for(e=g.length-1;e>=0;e--)if(g[e].predicate(f[d]))return{toolbar:g[e],element:f[d]};return null}var t;a.on("click keyup setContent",function(b){("setcontent"!=b.type||b.selection)&&tinymce.util.Delay.setEditorTimeout(a,function(){var b;b=s(a.selection.getNode()),b?(r(),p(b)):r()})}),a.on("blur hide",r),a.on("ObjectResizeStart",function(){var b=s(a.selection.getNode());b&&b.toolbar.panel&&b.toolbar.panel.hide()}),a.on("nodeChange ResizeEditor ResizeWindow",k),a.on("remove",function(){tinymce.each(c(),function(a){a.panel&&a.panel.remove()}),a.contextToolbars={}}),a.shortcuts.add("ctrl+shift+e > ctrl+shift+p","",function(){var b=s(a.selection.getNode());b&&b.toolbar.panel&&b.toolbar.panel.items()[0].focus()})}function i(a){return function(){a.initialized?a.fire("SkinLoaded"):a.on("init",function(){a.fire("SkinLoaded")})}}function j(b){function f(){if(o&&o.moveRel&&o.visible()&&!o._fixed){var b=a.selection.getScrollContainer(),c=a.getBody(),d=0,e=0;if(b){var f=p.getPos(c),g=p.getPos(b);d=Math.max(0,g.x-f.x),e=Math.max(0,g.y-f.y)}o.fixed(!1).moveRel(c,a.rtl?["tr-br","br-tr"]:["tl-bl","bl-tl","tr-br"]).moveBy(d,e)}}function g(){o&&(o.show(),f(),p.addClass(a.getBody(),"mce-edit-focus"))}function j(){o&&(o.hide(),r.hideAll(),p.removeClass(a.getBody(),"mce-edit-focus"))}function k(){return o?void(o.visible()||g()):(o=l.panel=n.create({type:q?"panel":"floatpanel",role:"application",classes:"tinymce tinymce-inline",layout:"flex",direction:"column",align:"stretch",autohide:!1,autofix:!0,fixed:!!q,border:1,items:[m.menubar===!1?null:{type:"menubar",border:"0 0 1 0",items:d()},c(m.toolbar_items_size)]}),a.fire("BeforeRenderUI"),o.renderTo(q||document.body).reflow(),e(o),g(),h(),a.on("nodeChange",f),a.on("activate",g),a.on("deactivate",j),void a.nodeChanged())}var o,q;return m.fixed_toolbar_container&&(q=p.select(m.fixed_toolbar_container)[0]),m.content_editable=!0,a.on("focus",function(){b.skinUiCss?tinymce.DOM.styleSheetLoader.load(b.skinUiCss,k,k):k()}),a.on("blur hide",j),a.on("remove",function(){o&&(o.remove(),o=null)}),b.skinUiCss&&tinymce.DOM.styleSheetLoader.load(b.skinUiCss,i(a)),{}}function k(b){function g(){return function(a){"readonly"==a.mode?j.find("*").disabled(!0):j.find("*").disabled(!1)}}var j,k,o;return b.skinUiCss&&tinymce.DOM.styleSheetLoader.load(b.skinUiCss,i(a)),j=l.panel=n.create({type:"panel",role:"application",classes:"tinymce",style:"visibility: hidden",layout:"stack",border:1,items:[m.menubar===!1?null:{type:"menubar",border:"0 0 1 0",items:d()},c(m.toolbar_items_size),{type:"panel",name:"iframe",layout:"stack",classes:"edit-area",html:"",border:"1 0 0 0"}]}),m.resize!==!1&&(k={type:"resizehandle",direction:m.resize,onResizeStart:function(){var b=a.getContentAreaContainer().firstChild;o={width:b.clientWidth,height:b.clientHeight}},onResize:function(a){"both"==m.resize?f(o.width+a.deltaX,o.height+a.deltaY):f(null,o.height+a.deltaY)}}),m.statusbar!==!1&&j.add({type:"panel",name:"statusbar",classes:"statusbar",layout:"flow",border:"1 0 0 0",ariaRoot:!0,items:[{type:"elementpath",editor:a},k]}),a.fire("BeforeRenderUI"),a.on("SwitchMode",g()),j.renderBefore(b.targetNode).reflow(),m.readonly&&a.setMode("readonly"),m.width&&tinymce.DOM.setStyle(j.getEl(),"width",m.width),a.on("remove",function(){j.remove(),j=null}),e(j),h(),{iframeContainer:j.find("#iframe")[0].getEl(),editorContainer:j.getEl()}}var l=this,m=a.settings,n=tinymce.ui.Factory,o=tinymce.each,p=tinymce.DOM,q=tinymce.geom.Rect,r=tinymce.ui.FloatPanel,s={file:{title:"File",items:"newdocument"},edit:{title:"Edit",items:"undo redo | cut copy paste pastetext | selectall"},insert:{title:"Insert",items:"|"},view:{title:"View",items:"visualaid |"},format:{title:"Format",items:"bold italic underline strikethrough superscript subscript | formats | removeformat"},table:{title:"Table"},tools:{title:"Tools"}},t="undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image";l.renderUI=function(b){var c=m.skin!==!1?m.skin||"lightgray":!1;if(c){var d=m.skin_url;d=d?a.documentBaseURI.toAbsolute(d):tinymce.baseURL+"/skins/"+c,tinymce.Env.documentMode<=7?b.skinUiCss=d+"/skin.ie7.min.css":b.skinUiCss=d+"/skin.min.css",a.contentCSS.push(d+"/content"+(a.inline?".inline":"")+".min.css")}return a.on("ProgressState",function(a){l.throbber=l.throbber||new tinymce.ui.Throbber(l.panel.getEl("body")),a.state?l.throbber.show(a.time):l.throbber.hide()}),m.inline?j(b):k(b)},l.resizeTo=f,l.resizeBy=g}); \ No newline at end of file diff --git a/www/js/tinymce/tinymce.min.js b/www/js/tinymce/tinymce.min.js new file mode 100644 index 0000000..6f5d573 --- /dev/null +++ b/www/js/tinymce/tinymce.min.js @@ -0,0 +1,58 @@ +// 4.4.3 (2016-09-01) +!function(e,t){"use strict";function n(e,t){for(var n,r=[],i=0;i=r.x&&o.x+o.w<=r.w+r.x&&o.y>=r.y&&o.y+o.h<=r.h+r.y)return i[a];return null}function n(e,t,n){return o(e.x-t,e.y-n,e.w+2*t,e.h+2*n)}function r(e,t){var n,r,i,a;return n=l(e.x,t.x),r=l(e.y,t.y),i=s(e.x+e.w,t.x+t.w),a=s(e.y+e.h,t.y+t.h),0>i-n||0>a-r?null:o(n,r,i-n,a-r)}function i(e,t,n){var r,i,a,s,c,u,d,f,h,p;return c=e.x,u=e.y,d=e.x+e.w,f=e.y+e.h,h=t.x+t.w,p=t.y+t.h,r=l(0,t.x-c),i=l(0,t.y-u),a=l(0,d-h),s=l(0,f-p),c+=r,u+=i,n&&(d+=r,f+=i,c-=a,u-=s),d-=a,f-=s,o(c,u,d-c,f-u)}function o(e,t,n,r){return{x:e,y:t,w:n,h:r}}function a(e){return o(e.left,e.top,e.width,e.height)}var s=Math.min,l=Math.max,c=Math.round;return{inflate:n,relativePosition:e,findBestRelativePosition:t,intersect:r,clamp:i,create:o,fromClientRect:a}}),r(c,[],function(){function e(e,t){return function(){e.apply(t,arguments)}}function t(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],s(t,e(r,this),e(i,this))}function n(e){var t=this;return null===this._state?void this._deferreds.push(e):void l(function(){var n=t._state?e.onFulfilled:e.onRejected;if(null===n)return void(t._state?e.resolve:e.reject)(t._value);var r;try{r=n(t._value)}catch(i){return void e.reject(i)}e.resolve(r)})}function r(t){try{if(t===this)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if("function"==typeof n)return void s(e(n,t),e(r,this),e(i,this))}this._state=!0,this._value=t,o.call(this)}catch(a){i.call(this,a)}}function i(e){this._state=!1,this._value=e,o.call(this)}function o(){for(var e=0,t=this._deferreds.length;t>e;e++)n.call(this,this._deferreds[e]);this._deferreds=null}function a(e,t,n,r){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.resolve=n,this.reject=r}function s(e,t,n){var r=!1;try{e(function(e){r||(r=!0,t(e))},function(e){r||(r=!0,n(e))})}catch(i){if(r)return;r=!0,n(i)}}if(window.Promise)return window.Promise;var l=t.immediateFn||"function"==typeof setImmediate&&setImmediate||function(e){setTimeout(e,1)},c=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};return t.prototype["catch"]=function(e){return this.then(null,e)},t.prototype.then=function(e,r){var i=this;return new t(function(t,o){n.call(i,new a(e,r,t,o))})},t.all=function(){var e=Array.prototype.slice.call(1===arguments.length&&c(arguments[0])?arguments[0]:arguments);return new t(function(t,n){function r(o,a){try{if(a&&("object"==typeof a||"function"==typeof a)){var s=a.then;if("function"==typeof s)return void s.call(a,function(e){r(o,e)},n)}e[o]=a,0===--i&&t(e)}catch(l){n(l)}}if(0===e.length)return t([]);for(var i=e.length,o=0;or;r++)e[r].then(t,n)})},t}),r(u,[c],function(e){function t(e,t){function n(e){window.setTimeout(e,0)}var r,i=window.requestAnimationFrame,o=["ms","moz","webkit"];for(r=0;r=534;return{opera:r,webkit:i,ie:o,gecko:l,mac:c,iOS:u,android:d,contentEditable:g,transparentSrc:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",caretAfter:8!=o,range:window.getSelection&&"Range"in window,documentMode:o&&!s?document.documentMode||7:10,fileApi:f,ceFalse:o===!1||o>8,canHaveCSP:o===!1||o>11,desktop:!h&&!p,windowsPhone:m}}),r(f,[u,d],function(e,t){function n(e,t,n,r){e.addEventListener?e.addEventListener(t,n,r||!1):e.attachEvent&&e.attachEvent("on"+t,n)}function r(e,t,n,r){e.removeEventListener?e.removeEventListener(t,n,r||!1):e.detachEvent&&e.detachEvent("on"+t,n)}function i(e,t){var n,r=t;return n=e.path,n&&n.length>0&&(r=n[0]),e.deepPath&&(n=e.deepPath(),n&&n.length>0&&(r=n[0])),r}function o(e,n){function r(){return!1}function o(){return!0}var a,s=n||{},l;for(a in e)u[a]||(s[a]=e[a]);if(s.target||(s.target=s.srcElement||document),t.experimentalShadowDom&&(s.target=i(e,s.target)),e&&c.test(e.type)&&e.pageX===l&&e.clientX!==l){var d=s.target.ownerDocument||document,f=d.documentElement,h=d.body;s.pageX=e.clientX+(f&&f.scrollLeft||h&&h.scrollLeft||0)-(f&&f.clientLeft||h&&h.clientLeft||0),s.pageY=e.clientY+(f&&f.scrollTop||h&&h.scrollTop||0)-(f&&f.clientTop||h&&h.clientTop||0)}return s.preventDefault=function(){s.isDefaultPrevented=o,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},s.stopPropagation=function(){s.isPropagationStopped=o,e&&(e.stopPropagation?e.stopPropagation():e.cancelBubble=!0)},s.stopImmediatePropagation=function(){s.isImmediatePropagationStopped=o,s.stopPropagation()},s.isDefaultPrevented||(s.isDefaultPrevented=r,s.isPropagationStopped=r,s.isImmediatePropagationStopped=r),"undefined"==typeof s.metaKey&&(s.metaKey=!1),s}function a(t,i,o){function a(){o.domLoaded||(o.domLoaded=!0,i(u))}function s(){("complete"===c.readyState||"interactive"===c.readyState&&c.body)&&(r(c,"readystatechange",s),a())}function l(){try{c.documentElement.doScroll("left")}catch(t){return void e.setTimeout(l)}a()}var c=t.document,u={type:"ready"};return o.domLoaded?void i(u):(c.addEventListener?"complete"===c.readyState?a():n(t,"DOMContentLoaded",a):(n(c,"readystatechange",s),c.documentElement.doScroll&&t.self===t.top&&l()),void n(t,"load",a))}function s(){function e(e,t){var n,r,o,a,s=i[t];if(n=s&&s[e.type])for(r=0,o=n.length;o>r;r++)if(a=n[r],a&&a.func.call(a.scope,e)===!1&&e.preventDefault(),e.isImmediatePropagationStopped())return}var t=this,i={},s,c,u,d,f;c=l+(+new Date).toString(32),d="onmouseenter"in document.documentElement,u="onfocusin"in document.documentElement,f={mouseenter:"mouseover",mouseleave:"mouseout"},s=1,t.domLoaded=!1,t.events=i,t.bind=function(r,l,h,p){function m(t){e(o(t||N.event),g)}var g,v,y,b,C,x,w,N=window;if(r&&3!==r.nodeType&&8!==r.nodeType){for(r[c]?g=r[c]:(g=s++,r[c]=g,i[g]={}),p=p||r,l=l.split(" "),y=l.length;y--;)b=l[y],x=m,C=w=!1,"DOMContentLoaded"===b&&(b="ready"),t.domLoaded&&"ready"===b&&"complete"==r.readyState?h.call(p,o({type:b})):(d||(C=f[b],C&&(x=function(t){var n,r;if(n=t.currentTarget,r=t.relatedTarget,r&&n.contains)r=n.contains(r);else for(;r&&r!==n;)r=r.parentNode;r||(t=o(t||N.event),t.type="mouseout"===t.type?"mouseleave":"mouseenter",t.target=n,e(t,g))})),u||"focusin"!==b&&"focusout"!==b||(w=!0,C="focusin"===b?"focus":"blur",x=function(t){t=o(t||N.event),t.type="focus"===t.type?"focusin":"focusout",e(t,g)}),v=i[g][b],v?"ready"===b&&t.domLoaded?h({type:b}):v.push({func:h,scope:p}):(i[g][b]=v=[{func:h,scope:p}],v.fakeName=C,v.capture=w,v.nativeHandler=x,"ready"===b?a(r,x,t):n(r,C||b,x,w)));return r=v=0,h}},t.unbind=function(e,n,o){var a,s,l,u,d,f;if(!e||3===e.nodeType||8===e.nodeType)return t;if(a=e[c]){if(f=i[a],n){for(n=n.split(" "),l=n.length;l--;)if(d=n[l],s=f[d]){if(o)for(u=s.length;u--;)if(s[u].func===o){var h=s.nativeHandler,p=s.fakeName,m=s.capture;s=s.slice(0,u).concat(s.slice(u+1)),s.nativeHandler=h,s.fakeName=p,s.capture=m,f[d]=s}o&&0!==s.length||(delete f[d],r(e,s.fakeName||d,s.nativeHandler,s.capture))}}else{for(d in f)s=f[d],r(e,s.fakeName||d,s.nativeHandler,s.capture);f={}}for(d in f)return t;delete i[a];try{delete e[c]}catch(g){e[c]=null}}return t},t.fire=function(n,r,i){var a;if(!n||3===n.nodeType||8===n.nodeType)return t;i=o(null,i),i.type=r,i.target=n;do a=n[c],a&&e(i,a),n=n.parentNode||n.ownerDocument||n.defaultView||n.parentWindow;while(n&&!i.isPropagationStopped());return t},t.clean=function(e){var n,r,i=t.unbind;if(!e||3===e.nodeType||8===e.nodeType)return t;if(e[c]&&i(e),e.getElementsByTagName||(e=e.document),e&&e.getElementsByTagName)for(i(e),r=e.getElementsByTagName("*"),n=r.length;n--;)e=r[n],e[c]&&i(e);return t},t.destroy=function(){i={}},t.cancel=function(e){return e&&(e.preventDefault(),e.stopImmediatePropagation()),!1}}var l="mce-data-",c=/^(?:mouse|contextmenu)|click/,u={keyLocation:1,layerX:1,layerY:1,returnValue:1,webkitMovementX:1,webkitMovementY:1,keyIdentifier:1};return s.Event=new s,s.Event.bind(window,"ready",function(){}),s}),r(h,[],function(){function e(e,t,n,r){var i,o,a,s,l,c,d,h,p,m;if((t?t.ownerDocument||t:z)!==D&&B(t),t=t||D,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(M&&!r){if(i=ve.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&I(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return Z.apply(n,t.getElementsByTagName(e)),n;if((a=i[3])&&x.getElementsByClassName)return Z.apply(n,t.getElementsByClassName(a)),n}if(x.qsa&&(!P||!P.test(e))){if(h=d=F,p=t,m=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(c=_(e),(d=t.getAttribute("id"))?h=d.replace(be,"\\$&"):t.setAttribute("id",h),h="[id='"+h+"'] ",l=c.length;l--;)c[l]=h+f(c[l]);p=ye.test(e)&&u(t.parentNode)||t,m=c.join(",")}if(m)try{return Z.apply(n,p.querySelectorAll(m)),n}catch(g){}finally{d||t.removeAttribute("id")}}}return k(e.replace(se,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>w.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[F]=!0,e}function i(e){var t=D.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=e.length;r--;)w.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||X)-(~e.sourceIndex||X);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function c(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function u(e){return e&&typeof e.getElementsByTagName!==Y&&e}function d(){}function f(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function h(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=W++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,l,c=[U,o];if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i){if(l=t[F]||(t[F]={}),(s=l[r])&&s[0]===U&&s[1]===o)return c[2]=s[2];if(l[r]=c,c[2]=e(t,n,a))return!0}}}function p(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function m(t,n,r){for(var i=0,o=n.length;o>i;i++)e(t,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,c=null!=t;l>s;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),c&&t.push(s)));return a}function v(e,t,n,i,o,a){return i&&!i[F]&&(i=v(i)),o&&!o[F]&&(o=v(o,a)),r(function(r,a,s,l){var c,u,d,f=[],h=[],p=a.length,v=r||m(t||"*",s.nodeType?[s]:s,[]),y=!e||!r&&t?v:g(v,f,e,s,l),b=n?o||(r?e:p||i)?[]:a:y;if(n&&n(y,b,s,l),i)for(c=g(b,h),i(c,[],s,l),u=c.length;u--;)(d=c[u])&&(b[h[u]]=!(y[h[u]]=d));if(r){if(o||e){if(o){for(c=[],u=b.length;u--;)(d=b[u])&&c.push(y[u]=d);o(null,b=[],c,l)}for(u=b.length;u--;)(d=b[u])&&(c=o?te.call(r,d):f[u])>-1&&(r[c]=!(a[c]=d))}}else b=g(b===a?b.splice(p,b.length):b),o?o(null,a,b,l):Z.apply(a,b)})}function y(e){for(var t,n,r,i=e.length,o=w.relative[e[0].type],a=o||w.relative[" "],s=o?1:0,l=h(function(e){return e===t},a,!0),c=h(function(e){return te.call(t,e)>-1},a,!0),u=[function(e,n,r){return!o&&(r||n!==T)||((t=n).nodeType?l(e,n,r):c(e,n,r))}];i>s;s++)if(n=w.relative[e[s].type])u=[h(p(u),n)];else{if(n=w.filter[e[s].type].apply(null,e[s].matches),n[F]){for(r=++s;i>r&&!w.relative[e[r].type];r++);return v(s>1&&p(u),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,r>s&&y(e.slice(s,r)),i>r&&y(e=e.slice(r)),i>r&&f(e))}u.push(n)}return p(u)}function b(t,n){var i=n.length>0,o=t.length>0,a=function(r,a,s,l,c){var u,d,f,h=0,p="0",m=r&&[],v=[],y=T,b=r||o&&w.find.TAG("*",c),C=U+=null==y?1:Math.random()||.1,x=b.length;for(c&&(T=a!==D&&a);p!==x&&null!=(u=b[p]);p++){if(o&&u){for(d=0;f=t[d++];)if(f(u,a,s)){l.push(u);break}c&&(U=C)}i&&((u=!f&&u)&&h--,r&&m.push(u))}if(h+=p,i&&p!==h){for(d=0;f=n[d++];)f(m,v,a,s);if(r){if(h>0)for(;p--;)m[p]||v[p]||(v[p]=J.call(l));v=g(v)}Z.apply(l,v),c&&!r&&v.length>0&&h+n.length>1&&e.uniqueSort(l)}return c&&(U=C,T=y),m};return i?r(a):a}var C,x,w,N,E,_,S,k,T,R,A,B,D,L,M,P,O,H,I,F="sizzle"+-new Date,z=window.document,U=0,W=0,V=n(),$=n(),q=n(),j=function(e,t){return e===t&&(A=!0),0},Y=typeof t,X=1<<31,K={}.hasOwnProperty,G=[],J=G.pop,Q=G.push,Z=G.push,ee=G.slice,te=G.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},ne="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",re="[\\x20\\t\\r\\n\\f]",ie="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",oe="\\["+re+"*("+ie+")(?:"+re+"*([*^$|!~]?=)"+re+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+ie+"))|)"+re+"*\\]",ae=":("+ie+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+oe+")*)|.*)\\)|)",se=new RegExp("^"+re+"+|((?:^|[^\\\\])(?:\\\\.)*)"+re+"+$","g"),le=new RegExp("^"+re+"*,"+re+"*"),ce=new RegExp("^"+re+"*([>+~]|"+re+")"+re+"*"),ue=new RegExp("="+re+"*([^\\]'\"]*?)"+re+"*\\]","g"),de=new RegExp(ae),fe=new RegExp("^"+ie+"$"),he={ID:new RegExp("^#("+ie+")"),CLASS:new RegExp("^\\.("+ie+")"),TAG:new RegExp("^("+ie+"|[*])"),ATTR:new RegExp("^"+oe),PSEUDO:new RegExp("^"+ae),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+re+"*(even|odd|(([+-]|)(\\d*)n|)"+re+"*(?:([+-]|)"+re+"*(\\d+)|))"+re+"*\\)|)","i"),bool:new RegExp("^(?:"+ne+")$","i"),needsContext:new RegExp("^"+re+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+re+"*((?:-\\d)?\\d*)"+re+"*\\)|)(?=[^-]|$)","i")},pe=/^(?:input|select|textarea|button)$/i,me=/^h\d$/i,ge=/^[^{]+\{\s*\[native \w/,ve=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ye=/[+~]/,be=/'|\\/g,Ce=new RegExp("\\\\([\\da-f]{1,6}"+re+"?|("+re+")|.)","ig"),xe=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)};try{Z.apply(G=ee.call(z.childNodes),z.childNodes),G[z.childNodes.length].nodeType}catch(we){Z={apply:G.length?function(e,t){Q.apply(e,ee.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}x=e.support={},E=e.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},B=e.setDocument=function(e){function t(e){try{return e.top}catch(t){}return null}var n,r=e?e.ownerDocument||e:z,o=r.defaultView;return r!==D&&9===r.nodeType&&r.documentElement?(D=r,L=r.documentElement,M=!E(r),o&&o!==t(o)&&(o.addEventListener?o.addEventListener("unload",function(){B()},!1):o.attachEvent&&o.attachEvent("onunload",function(){B()})),x.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),x.getElementsByTagName=i(function(e){return e.appendChild(r.createComment("")),!e.getElementsByTagName("*").length}),x.getElementsByClassName=ge.test(r.getElementsByClassName),x.getById=i(function(e){return L.appendChild(e).id=F,!r.getElementsByName||!r.getElementsByName(F).length}),x.getById?(w.find.ID=function(e,t){if(typeof t.getElementById!==Y&&M){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},w.filter.ID=function(e){var t=e.replace(Ce,xe);return function(e){return e.getAttribute("id")===t}}):(delete w.find.ID,w.filter.ID=function(e){var t=e.replace(Ce,xe);return function(e){var n=typeof e.getAttributeNode!==Y&&e.getAttributeNode("id");return n&&n.value===t}}),w.find.TAG=x.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==Y?t.getElementsByTagName(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},w.find.CLASS=x.getElementsByClassName&&function(e,t){return M?t.getElementsByClassName(e):void 0},O=[],P=[],(x.qsa=ge.test(r.querySelectorAll))&&(i(function(e){e.innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&P.push("[*^$]="+re+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||P.push("\\["+re+"*(?:value|"+ne+")"),e.querySelectorAll(":checked").length||P.push(":checked")}),i(function(e){var t=r.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&P.push("name"+re+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||P.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),P.push(",.*:")})),(x.matchesSelector=ge.test(H=L.matches||L.webkitMatchesSelector||L.mozMatchesSelector||L.oMatchesSelector||L.msMatchesSelector))&&i(function(e){x.disconnectedMatch=H.call(e,"div"),H.call(e,"[s!='']:x"),O.push("!=",ae)}),P=P.length&&new RegExp(P.join("|")),O=O.length&&new RegExp(O.join("|")),n=ge.test(L.compareDocumentPosition),I=n||ge.test(L.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},j=n?function(e,t){if(e===t)return A=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!x.sortDetached&&t.compareDocumentPosition(e)===n?e===r||e.ownerDocument===z&&I(z,e)?-1:t===r||t.ownerDocument===z&&I(z,t)?1:R?te.call(R,e)-te.call(R,t):0:4&n?-1:1)}:function(e,t){if(e===t)return A=!0,0;var n,i=0,o=e.parentNode,s=t.parentNode,l=[e],c=[t];if(!o||!s)return e===r?-1:t===r?1:o?-1:s?1:R?te.call(R,e)-te.call(R,t):0;if(o===s)return a(e,t);for(n=e;n=n.parentNode;)l.unshift(n);for(n=t;n=n.parentNode;)c.unshift(n);for(;l[i]===c[i];)i++;return i?a(l[i],c[i]):l[i]===z?-1:c[i]===z?1:0},r):D},e.matches=function(t,n){return e(t,null,null,n)},e.matchesSelector=function(t,n){if((t.ownerDocument||t)!==D&&B(t),n=n.replace(ue,"='$1']"),x.matchesSelector&&M&&(!O||!O.test(n))&&(!P||!P.test(n)))try{var r=H.call(t,n);if(r||x.disconnectedMatch||t.document&&11!==t.document.nodeType)return r}catch(i){}return e(n,D,null,[t]).length>0},e.contains=function(e,t){return(e.ownerDocument||e)!==D&&B(e),I(e,t)},e.attr=function(e,n){(e.ownerDocument||e)!==D&&B(e);var r=w.attrHandle[n.toLowerCase()],i=r&&K.call(w.attrHandle,n.toLowerCase())?r(e,n,!M):t;return i!==t?i:x.attributes||!M?e.getAttribute(n):(i=e.getAttributeNode(n))&&i.specified?i.value:null},e.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},e.uniqueSort=function(e){var t,n=[],r=0,i=0;if(A=!x.detectDuplicates,R=!x.sortStable&&e.slice(0),e.sort(j),A){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return R=null,e},N=e.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=N(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=N(t);return n},w=e.selectors={cacheLength:50,createPseudo:r,match:he,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Ce,xe),e[3]=(e[3]||e[4]||e[5]||"").replace(Ce,xe),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(t){return t[1]=t[1].toLowerCase(),"nth"===t[1].slice(0,3)?(t[3]||e.error(t[0]),t[4]=+(t[4]?t[5]+(t[6]||1):2*("even"===t[3]||"odd"===t[3])),t[5]=+(t[7]+t[8]||"odd"===t[3])):t[3]&&e.error(t[0]),t},PSEUDO:function(e){var t,n=!e[6]&&e[2];return he.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&de.test(n)&&(t=_(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Ce,xe).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=V[e+" "];return t||(t=new RegExp("(^|"+re+")"+e+"("+re+"|$)"))&&V(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==Y&&e.getAttribute("class")||"")})},ATTR:function(t,n,r){return function(i){var o=e.attr(i,t);return null==o?"!="===n:n?(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o+" ").indexOf(r)>-1:"|="===n?o===r||o.slice(0,r.length+1)===r+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var c,u,d,f,h,p,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s;if(g){if(o){for(;m;){for(d=t;d=d[m];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;p=m="only"===e&&!p&&"nextSibling"}return!0}if(p=[a?g.firstChild:g.lastChild],a&&y){for(u=g[F]||(g[F]={}),c=u[e]||[],h=c[0]===U&&c[1],f=c[0]===U&&c[2],d=h&&g.childNodes[h];d=++h&&d&&d[m]||(f=h=0)||p.pop();)if(1===d.nodeType&&++f&&d===t){u[e]=[U,h,f];break}}else if(y&&(c=(t[F]||(t[F]={}))[e])&&c[0]===U)f=c[1];else for(;(d=++h&&d&&d[m]||(f=h=0)||p.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++f||(y&&((d[F]||(d[F]={}))[e]=[U,f]),d!==t)););return f-=i,f===r||f%r===0&&f/r>=0}}},PSEUDO:function(t,n){var i,o=w.pseudos[t]||w.setFilters[t.toLowerCase()]||e.error("unsupported pseudo: "+t);return o[F]?o(n):o.length>1?(i=[t,t,"",n],w.setFilters.hasOwnProperty(t.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=te.call(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=S(e.replace(se,"$1"));return i[F]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),!n.pop()}}),has:r(function(t){return function(n){return e(t,n).length>0}}),contains:r(function(e){return e=e.replace(Ce,xe),function(t){return(t.textContent||t.innerText||N(t)).indexOf(e)>-1}}),lang:r(function(t){return fe.test(t||"")||e.error("unsupported lang: "+t),t=t.replace(Ce,xe).toLowerCase(),function(e){var n;do if(n=M?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return n=n.toLowerCase(),n===t||0===n.indexOf(t+"-");while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=window.location&&window.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===L},focus:function(e){return e===D.activeElement&&(!D.hasFocus||D.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!w.pseudos.empty(e)},header:function(e){return me.test(e.nodeName)},input:function(e){return pe.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:c(function(){return[0]}),last:c(function(e,t){return[t-1]}),eq:c(function(e,t,n){return[0>n?n+t:n]}),even:c(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:c(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:c(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:c(function(e,t,n){for(var r=0>n?n+t:n;++r2&&"ID"===(a=o[0]).type&&x.getById&&9===t.nodeType&&M&&w.relative[o[1].type]){if(t=(w.find.ID(a.matches[0].replace(Ce,xe),t)||[])[0], +!t)return n;c&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=he.needsContext.test(e)?0:o.length;i--&&(a=o[i],!w.relative[s=a.type]);)if((l=w.find[s])&&(r=l(a.matches[0].replace(Ce,xe),ye.test(o[0].type)&&u(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&f(o),!e)return Z.apply(n,r),n;break}}return(c||S(e,d))(r,t,!M,n,ye.test(e)&&u(t.parentNode)||t),n},x.sortStable=F.split("").sort(j).join("")===F,x.detectDuplicates=!!A,B(),x.sortDetached=i(function(e){return 1&e.compareDocumentPosition(D.createElement("div"))}),i(function(e){return e.innerHTML="
    ","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){return n?void 0:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),x.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?void 0:e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(ne,function(e,t,n){var r;return n?void 0:e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),e}),r(p,[],function(){function e(e){var t=e,n,r;if(!u(e))for(t=[],n=0,r=e.length;r>n;n++)t[n]=e[n];return t}function n(e,n,r){var i,o;if(!e)return 0;if(r=r||e,e.length!==t){for(i=0,o=e.length;o>i;i++)if(n.call(r,e[i],i,e)===!1)return 0}else for(i in e)if(e.hasOwnProperty(i)&&n.call(r,e[i],i,e)===!1)return 0;return 1}function r(e,t){var r=[];return n(e,function(n,i){r.push(t(n,i,e))}),r}function i(e,t){var r=[];return n(e,function(n,i){t&&!t(n,i,e)||r.push(n)}),r}function o(e,t){var n,r;if(e)for(n=0,r=e.length;r>n;n++)if(e[n]===t)return n;return-1}function a(e,t,n,r){var i=0;for(arguments.length<3&&(n=e[0]);ir;r++)if(t.call(n,e[r],r,e))return r;return-1}function l(e,n,r){var i=s(e,n,r);return-1!==i?e[i]:t}function c(e){return e[e.length-1]}var u=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};return{isArray:u,toArray:e,each:n,map:r,filter:i,indexOf:o,reduce:a,findIndex:s,find:l,last:c}}),r(m,[d,p],function(e,n){function r(e){return null===e||e===t?"":(""+e).replace(h,"")}function i(e,r){return r?"array"==r&&n.isArray(e)?!0:typeof e==r:e!==t}function o(e,t,n){var r;for(e=e||[],t=t||",","string"==typeof e&&(e=e.split(t)),n=n||{},r=e.length;r--;)n[e[r]]={};return n}function a(e,t,n){var r=this,i,o,a,s,l,c=0;if(e=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(e),a=e[3].match(/(^|\.)(\w+)$/i)[2],o=r.createNS(e[3].replace(/\.\w+$/,""),n),!o[a]){if("static"==e[2])return o[a]=t,void(this.onCreate&&this.onCreate(e[2],e[3],o[a]));t[a]||(t[a]=function(){},c=1),o[a]=t[a],r.extend(o[a].prototype,t),e[5]&&(i=r.resolve(e[5]).prototype,s=e[5].match(/\.(\w+)$/i)[1],l=o[a],c?o[a]=function(){return i[s].apply(this,arguments)}:o[a]=function(){return this.parent=i[s],l.apply(this,arguments)},o[a].prototype[a]=o[a],r.each(i,function(e,t){o[a].prototype[t]=i[t]}),r.each(t,function(e,t){i[t]?o[a].prototype[t]=function(){return this.parent=i[t],e.apply(this,arguments)}:t!=a&&(o[a].prototype[t]=e)})),r.each(t["static"],function(e,t){o[a][t]=e})}}function s(e,n){var r,i,o,a=arguments,s;for(r=1,i=a.length;i>r;r++){n=a[r];for(o in n)n.hasOwnProperty(o)&&(s=n[o],s!==t&&(e[o]=s))}return e}function l(e,t,r,i){i=i||this,e&&(r&&(e=e[r]),n.each(e,function(e,n){return t.call(i,e,n,r)===!1?!1:void l(e,t,r,i)}))}function c(e,t){var n,r;for(t=t||window,e=e.split("."),n=0;nn&&(t=t[e[n]],t);n++);return t}function d(e,t){return!e||i(e,"array")?e:n.map(e.split(t||","),r)}function f(t){var n=e.cacheSuffix;return n&&(t+=(-1===t.indexOf("?")?"?":"&")+n),t}var h=/^\s*|\s*$/g;return{trim:r,isArray:n.isArray,is:i,toArray:n.toArray,makeMap:o,each:n.each,map:n.map,grep:n.filter,inArray:n.indexOf,extend:s,create:a,walk:l,createNS:c,resolve:u,explode:d,_addCacheSuffix:f}}),r(g,[f,h,m,d],function(e,n,r,i){function o(e){return"undefined"!=typeof e}function a(e){return"string"==typeof e}function s(e){return e&&e==e.window}function l(e,t){var n,r,i;for(t=t||w,i=t.createElement("div"),n=t.createDocumentFragment(),i.innerHTML=e;r=i.firstChild;)n.appendChild(r);return n}function c(e,t,n,r){var i;if(a(t))t=l(t,v(e[0]));else if(t.length&&!t.nodeType){if(t=f.makeArray(t),r)for(i=t.length-1;i>=0;i--)c(e,t[i],n,r);else for(i=0;ii&&(a=e[i],t.call(a,i,a)!==!1);i++);return e}function g(e,t){var n=[];return m(e,function(e,r){t(r,e)&&n.push(r)}),n}function v(e){return e?9==e.nodeType?e:e.ownerDocument:w}function y(e,n,r){var i=[],o=e[n];for("string"!=typeof r&&r instanceof f&&(r=r[0]);o&&9!==o.nodeType;){if(r!==t){if(o===r)break;if("string"==typeof r&&f(o).is(r))break}1===o.nodeType&&i.push(o),o=o[n]}return i}function b(e,n,r,i){var o=[];for(i instanceof f&&(i=i[0]);e;e=e[n])if(!r||e.nodeType===r){if(i!==t){if(e===i)break;if("string"==typeof i&&f(e).is(i))break}o.push(e)}return o}function C(e,t,n){for(e=e[t];e;e=e[t])if(e.nodeType==n)return e;return null}function x(e,t,n){m(n,function(n,r){e[n]=e[n]||{},e[n][t]=r})}var w=document,N=Array.prototype.push,E=Array.prototype.slice,_=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,S=e.Event,k,T=r.makeMap("children,contents,next,prev"),R=r.makeMap("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"," "),A=r.makeMap("checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected"," "),B={"for":"htmlFor","class":"className",readonly:"readOnly"},D={"float":"cssFloat"},L={},M={},P=/^\s*|\s*$/g;return f.fn=f.prototype={constructor:f,selector:"",context:null,length:0,init:function(e,t){var n=this,r,i;if(!e)return n;if(e.nodeType)return n.context=n[0]=e,n.length=1,n;if(t&&t.nodeType)n.context=t;else{if(t)return f(e).attr(t);n.context=t=document}if(a(e)){if(n.selector=e,r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:_.exec(e),!r)return f(t).find(e);if(r[1])for(i=l(e,v(t)).firstChild;i;)N.call(n,i),i=i.nextSibling;else{if(i=v(t).getElementById(r[2]),!i)return n;if(i.id!==r[2])return n.find(e);n.length=1,n[0]=i}}else this.add(e,!1);return n},toArray:function(){return r.toArray(this)},add:function(e,t){var n=this,r,i;if(a(e))return n.add(f(e));if(t!==!1)for(r=f.unique(n.toArray().concat(f.makeArray(e))),n.length=r.length,i=0;it;t++)f.find(e,this[t],r);return f(r)},filter:function(e){return f("function"==typeof e?g(this.toArray(),function(t,n){return e(n,t)}):f.filter(e,this.toArray()))},closest:function(e){var t=[];return e instanceof f&&(e=e[0]),this.each(function(n,r){for(;r;){if("string"==typeof e&&f(r).is(e)){t.push(r);break}if(r==e){t.push(r);break}r=r.parentNode}}),f(t)},offset:function(e){var t,n,r,i=0,o=0,a;return e?this.css(e):(t=this[0],t&&(n=t.ownerDocument,r=n.documentElement,t.getBoundingClientRect&&(a=t.getBoundingClientRect(),i=a.left+(r.scrollLeft||n.body.scrollLeft)-r.clientLeft,o=a.top+(r.scrollTop||n.body.scrollTop)-r.clientTop)),{left:i,top:o})},push:N,sort:[].sort,splice:[].splice},r.extend(f,{extend:r.extend,makeArray:function(e){return s(e)||e.nodeType?[e]:r.toArray(e)},inArray:h,isArray:r.isArray,each:m,trim:p,grep:g,find:n,expr:n.selectors,unique:n.uniqueSort,text:n.getText,contains:n.contains,filter:function(e,t,n){var r=t.length;for(n&&(e=":not("+e+")");r--;)1!=t[r].nodeType&&t.splice(r,1);return t=1===t.length?f.find.matchesSelector(t[0],e)?[t[0]]:[]:f.find.matches(e,t)}}),m({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return y(e,"parentNode")},next:function(e){return C(e,"nextSibling",1)},prev:function(e){return C(e,"previousSibling",1)},children:function(e){return b(e.firstChild,"nextSibling",1)},contents:function(e){return r.toArray(("iframe"===e.nodeName?e.contentDocument||e.contentWindow.document:e).childNodes)}},function(e,t){f.fn[e]=function(n){var r=this,i=[];return r.each(function(){var e=t.call(i,this,n,i);e&&(f.isArray(e)?i.push.apply(i,e):i.push(e))}),this.length>1&&(T[e]||(i=f.unique(i)),0===e.indexOf("parents")&&(i=i.reverse())),i=f(i),n?i.filter(n):i}}),m({parentsUntil:function(e,t){return y(e,"parentNode",t)},nextUntil:function(e,t){return b(e,"nextSibling",1,t).slice(1)},prevUntil:function(e,t){return b(e,"previousSibling",1,t).slice(1)}},function(e,t){f.fn[e]=function(n,r){var i=this,o=[];return i.each(function(){var e=t.call(o,this,n,o);e&&(f.isArray(e)?o.push.apply(o,e):o.push(e))}),this.length>1&&(o=f.unique(o),0!==e.indexOf("parents")&&"prevUntil"!==e||(o=o.reverse())),o=f(o),r?o.filter(r):o}}),f.fn.is=function(e){return!!e&&this.filter(e).length>0},f.fn.init.prototype=f.fn,f.overrideDefaults=function(e){function t(r,i){return n=n||e(),0===arguments.length&&(r=n.element),i||(i=n.context),new t.fn.init(r,i)}var n;return f.extend(t,this),t},i.ie&&i.ie<8&&(x(L,"get",{maxlength:function(e){var t=e.maxLength;return 2147483647===t?k:t},size:function(e){var t=e.size;return 20===t?k:t},"class":function(e){return e.className},style:function(e){var t=e.style.cssText;return 0===t.length?k:t}}),x(L,"set",{"class":function(e,t){e.className=t},style:function(e,t){e.style.cssText=t}})),i.ie&&i.ie<9&&(D["float"]="styleFloat",x(M,"set",{opacity:function(e,t){var n=e.style;null===t||""===t?n.removeAttribute("filter"):(n.zoom=1,n.filter="alpha(opacity="+100*t+")")}})),f.attrHooks=L,f.cssHooks=M,f}),r(v,[],function(){return function(e,t){function n(e,t,n,r){function i(e){return e=parseInt(e,10).toString(16),e.length>1?e:"0"+e}return"#"+i(t)+i(n)+i(r)}var r=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,i=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,o=/\s*([^:]+):\s*([^;]+);?/g,a=/\s+$/,s,l,c={},u,d,f,h="\ufeff";for(e=e||{},t&&(d=t.getValidStyles(),f=t.getInvalidStyles()),u=("\\\" \\' \\; \\: ; : "+h).split(" "),l=0;l-1&&n||(m[e+t]=-1==l?s[0]:s.join(" "),delete m[e+"-top"+t],delete m[e+"-right"+t],delete m[e+"-bottom"+t],delete m[e+"-left"+t])}}function u(e){var t=m[e],n;if(t){for(t=t.split(" "),n=t.length;n--;)if(t[n]!==t[0])return!1;return m[e]=t[0],!0}}function d(e,t,n,r){u(t)&&u(n)&&u(r)&&(m[e]=m[t]+" "+m[n]+" "+m[r],delete m[t],delete m[n],delete m[r])}function f(e){return b=!0,c[e]}function h(e,t){return b&&(e=e.replace(/\uFEFF[0-9]/g,function(e){return c[e]})),t||(e=e.replace(/\\([\'\";:])/g,"$1")),e}function p(t,n,r,i,o,a){if(o=o||a)return o=h(o),"'"+o.replace(/\'/g,"\\'")+"'";if(n=h(n||r||i),!e.allow_script_urls){var s=n.replace(/[\s\r\n]+/,"");if(/(java|vb)script:/i.test(s))return"";if(!e.allow_svg_data_urls&&/^data:image\/svg/i.test(s))return""}return C&&(n=C.call(x,n,"style")),"url('"+n.replace(/\'/g,"\\'")+"')"}var m={},g,v,y,b,C=e.url_converter,x=e.url_converter_scope||this;if(t){for(t=t.replace(/[\u0000-\u001F]/g,""),t=t.replace(/\\[\"\';:\uFEFF]/g,f).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(e){return e.replace(/[;:]/g,f)});g=o.exec(t);){if(v=g[1].replace(a,"").toLowerCase(),y=g[2].replace(a,""),y=y.replace(/\\[0-9a-f]+/g,function(e){return String.fromCharCode(parseInt(e.substr(1),16))}),v&&y.length>0){if(!e.allow_script_urls&&("behavior"==v||/expression\s*\(|\/\*|\*\//.test(y)))continue;"font-weight"===v&&"700"===y?y="bold":"color"!==v&&"background-color"!==v||(y=y.toLowerCase()),y=y.replace(r,n),y=y.replace(i,p),m[v]=b?h(y,!0):y}o.lastIndex=g.index+g[0].length}s("border","",!0),s("border","-width"),s("border","-color"),s("border","-style"),s("padding",""),s("margin",""),d("border","border-width","border-style","border-color"),"medium none"===m.border&&delete m.border,"none"===m["border-image"]&&delete m["border-image"]}return m},serialize:function(e,t){function n(t){var n,r,o,a;if(n=d[t])for(r=0,o=n.length;o>r;r++)t=n[r],a=e[t],a!==s&&a.length>0&&(i+=(i.length>0?" ":"")+t+": "+a+";")}function r(e,t){var n;return n=f["*"],n&&n[e]?!1:(n=f[t],!n||!n[e])}var i="",o,a;if(t&&d)n("*"),n(t);else for(o in e)a=e[o],a!==s&&a.length>0&&(f&&!r(o,t)||(i+=(i.length>0?" ":"")+o+": "+a+";"));return i}}}}),r(y,[],function(){return function(e,t){function n(e,n,r,i){var o,a;if(e){if(!i&&e[n])return e[n];if(e!=t){if(o=e[r])return o;for(a=e.parentNode;a&&a!=t;a=a.parentNode)if(o=a[r])return o}}}function r(e,n,r,i){var o,a,s;if(e){if(o=e[r],t&&o===t)return;if(o){if(!i)for(s=o[n];s;s=s[n])if(!s[n])return s;return o}if(a=e.parentNode,a&&a!==t)return a}}var i=e;this.current=function(){return i},this.next=function(e){return i=n(i,"firstChild","nextSibling",e)},this.prev=function(e){return i=n(i,"lastChild","previousSibling",e)},this.prev2=function(e){return i=r(i,"lastChild","previousSibling",e)}}}),r(b,[m],function(e){function t(n){function r(){return P.createDocumentFragment()}function i(e,t){N(F,e,t)}function o(e,t){N(z,e,t)}function a(e){i(e.parentNode,j(e))}function s(e){i(e.parentNode,j(e)+1)}function l(e){o(e.parentNode,j(e))}function c(e){o(e.parentNode,j(e)+1)}function u(e){e?(M[V]=M[W],M[$]=M[U]):(M[W]=M[V],M[U]=M[$]),M.collapsed=F}function d(e){a(e),c(e)}function f(e){i(e,0),o(e,1===e.nodeType?e.childNodes.length:e.nodeValue.length)}function h(e,t){var n=M[W],r=M[U],i=M[V],o=M[$],a=t.startContainer,s=t.startOffset,l=t.endContainer,c=t.endOffset;return 0===e?w(n,r,a,s):1===e?w(i,o,a,s):2===e?w(i,o,l,c):3===e?w(n,r,l,c):void 0}function p(){E(I)}function m(){return E(O)}function g(){return E(H)}function v(e){var t=this[W],r=this[U],i,o;3!==t.nodeType&&4!==t.nodeType||!t.nodeValue?(t.childNodes.length>0&&(o=t.childNodes[r]),o?t.insertBefore(e,o):3==t.nodeType?n.insertAfter(e,t):t.appendChild(e)):r?r>=t.nodeValue.length?n.insertAfter(e,t):(i=t.splitText(r),t.parentNode.insertBefore(e,i)):t.parentNode.insertBefore(e,t)}function y(e){var t=M.extractContents();M.insertNode(e),e.appendChild(t),M.selectNode(e)}function b(){return q(new t(n),{startContainer:M[W],startOffset:M[U],endContainer:M[V],endOffset:M[$],collapsed:M.collapsed,commonAncestorContainer:M.commonAncestorContainer})}function C(e,t){var n;if(3==e.nodeType)return e;if(0>t)return e;for(n=e.firstChild;n&&t>0;)--t,n=n.nextSibling;return n?n:e}function x(){return M[W]==M[V]&&M[U]==M[$]}function w(e,t,r,i){var o,a,s,l,c,u;if(e==r)return t==i?0:i>t?-1:1;for(o=r;o&&o.parentNode!=e;)o=o.parentNode;if(o){for(a=0,s=e.firstChild;s!=o&&t>a;)a++,s=s.nextSibling;return a>=t?-1:1}for(o=e;o&&o.parentNode!=r;)o=o.parentNode;if(o){for(a=0,s=r.firstChild;s!=o&&i>a;)a++,s=s.nextSibling;return i>a?-1:1}for(l=n.findCommonAncestor(e,r),c=e;c&&c.parentNode!=l;)c=c.parentNode;for(c||(c=l),u=r;u&&u.parentNode!=l;)u=u.parentNode;if(u||(u=l),c==u)return 0;for(s=l.firstChild;s;){if(s==c)return-1;if(s==u)return 1;s=s.nextSibling}}function N(e,t,r){var i,o;for(e?(M[W]=t,M[U]=r):(M[V]=t,M[$]=r),i=M[V];i.parentNode;)i=i.parentNode;for(o=M[W];o.parentNode;)o=o.parentNode;o==i?w(M[W],M[U],M[V],M[$])>0&&M.collapse(e):M.collapse(e),M.collapsed=x(),M.commonAncestorContainer=n.findCommonAncestor(M[W],M[V])}function E(e){var t,n=0,r=0,i,o,a,s,l,c;if(M[W]==M[V])return _(e);for(t=M[V],i=t.parentNode;i;t=i,i=i.parentNode){if(i==M[W])return S(t,e);++n}for(t=M[W],i=t.parentNode;i;t=i,i=i.parentNode){if(i==M[V])return k(t,e);++r}for(o=r-n,a=M[W];o>0;)a=a.parentNode,o--;for(s=M[V];0>o;)s=s.parentNode,o++;for(l=a.parentNode,c=s.parentNode;l!=c;l=l.parentNode,c=c.parentNode)a=l,s=c;return T(a,s,e)}function _(e){var t,n,i,o,a,s,l,c,u;if(e!=I&&(t=r()),M[U]==M[$])return t;if(3==M[W].nodeType){if(n=M[W].nodeValue,i=n.substring(M[U],M[$]),e!=H&&(o=M[W],c=M[U],u=M[$]-M[U],0===c&&u>=o.nodeValue.length-1?o.parentNode.removeChild(o):o.deleteData(c,u),M.collapse(F)),e==I)return;return i.length>0&&t.appendChild(P.createTextNode(i)),t}for(o=C(M[W],M[U]),a=M[$]-M[U];o&&a>0;)s=o.nextSibling,l=D(o,e),t&&t.appendChild(l),--a,o=s;return e!=H&&M.collapse(F),t}function S(e,t){var n,i,o,a,s,l;if(t!=I&&(n=r()),i=R(e,t),n&&n.appendChild(i),o=j(e),a=o-M[U],0>=a)return t!=H&&(M.setEndBefore(e),M.collapse(z)),n;for(i=e.previousSibling;a>0;)s=i.previousSibling,l=D(i,t),n&&n.insertBefore(l,n.firstChild),--a,i=s;return t!=H&&(M.setEndBefore(e),M.collapse(z)),n}function k(e,t){var n,i,o,a,s,l;for(t!=I&&(n=r()),o=A(e,t),n&&n.appendChild(o),i=j(e),++i,a=M[$]-i,o=e.nextSibling;o&&a>0;)s=o.nextSibling,l=D(o,t),n&&n.appendChild(l),--a,o=s;return t!=H&&(M.setStartAfter(e),M.collapse(F)),n}function T(e,t,n){var i,o,a,s,l,c,u;for(n!=I&&(o=r()),i=A(e,n),o&&o.appendChild(i),a=j(e),s=j(t),++a,l=s-a,c=e.nextSibling;l>0;)u=c.nextSibling,i=D(c,n),o&&o.appendChild(i),c=u,--l;return i=R(t,n),o&&o.appendChild(i),n!=H&&(M.setStartAfter(e),M.collapse(F)),o}function R(e,t){var n=C(M[V],M[$]-1),r,i,o,a,s,l=n!=M[V];if(n==e)return B(n,l,z,t);for(r=n.parentNode,i=B(r,z,z,t);r;){for(;n;)o=n.previousSibling,a=B(n,l,z,t),t!=I&&i.insertBefore(a,i.firstChild),l=F,n=o;if(r==e)return i;n=r.previousSibling,r=r.parentNode,s=B(r,z,z,t),t!=I&&s.appendChild(i),i=s}}function A(e,t){var n=C(M[W],M[U]),r=n!=M[W],i,o,a,s,l;if(n==e)return B(n,r,F,t);for(i=n.parentNode,o=B(i,z,F,t);i;){for(;n;)a=n.nextSibling,s=B(n,r,F,t),t!=I&&o.appendChild(s),r=F,n=a;if(i==e)return o;n=i.nextSibling,i=i.parentNode,l=B(i,z,F,t),t!=I&&l.appendChild(o),o=l}}function B(e,t,r,i){var o,a,s,l,c;if(t)return D(e,i);if(3==e.nodeType){if(o=e.nodeValue,r?(l=M[U],a=o.substring(l),s=o.substring(0,l)):(l=M[$],a=o.substring(0,l),s=o.substring(l)),i!=H&&(e.nodeValue=s),i==I)return;return c=n.clone(e,z),c.nodeValue=a,c}if(i!=I)return n.clone(e,z)}function D(e,t){return t!=I?t==H?n.clone(e,F):e:void e.parentNode.removeChild(e)}function L(){return n.create("body",null,g()).outerText}var M=this,P=n.doc,O=0,H=1,I=2,F=!0,z=!1,U="startOffset",W="startContainer",V="endContainer",$="endOffset",q=e.extend,j=n.nodeIndex;return q(M,{startContainer:P,startOffset:0,endContainer:P,endOffset:0,collapsed:F,commonAncestorContainer:P,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:i,setEnd:o,setStartBefore:a,setStartAfter:s,setEndBefore:l,setEndAfter:c,collapse:u,selectNode:d,selectNodeContents:f,compareBoundaryPoints:h,deleteContents:p,extractContents:m,cloneContents:g,insertNode:v,surroundContents:y,cloneRange:b,toStringIE:L}),M}return t.prototype.toString=function(){return this.toStringIE()},t}),r(C,[m],function(e){function t(e){var t;return t=document.createElement("div"),t.innerHTML=e,t.textContent||t.innerText||e}function n(e,t){var n,r,i,a={};if(e){for(e=e.split(","),t=t||10,n=0;n\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,l=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,c=/[<>&\"\']/g,u=/&#([a-z0-9]+);?|&([a-z0-9]+);/gi,d={128:"\u20ac",130:"\u201a",131:"\u0192",132:"\u201e",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02c6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017d",145:"\u2018",146:"\u2019",147:"\u201c",148:"\u201d",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02dc",153:"\u2122",154:"\u0161",155:"\u203a",156:"\u0153",158:"\u017e",159:"\u0178"};o={'"':""","'":"'","<":"<",">":">","&":"&","`":"`"},a={"<":"<",">":">","&":"&",""":'"',"'":"'"},i=n("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);var f={encodeRaw:function(e,t){return e.replace(t?s:l,function(e){return o[e]||e})},encodeAllRaw:function(e){return(""+e).replace(c,function(e){return o[e]||e})},encodeNumeric:function(e,t){return e.replace(t?s:l,function(e){return e.length>1?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":o[e]||"&#"+e.charCodeAt(0)+";"})},encodeNamed:function(e,t,n){return n=n||i,e.replace(t?s:l,function(e){return o[e]||n[e]||e})},getEncodeFunc:function(e,t){function a(e,n){return e.replace(n?s:l,function(e){return o[e]||t[e]||"&#"+e.charCodeAt(0)+";"||e})}function c(e,n){return f.encodeNamed(e,n,t)}return t=n(t)||i,e=r(e.replace(/\+/g,",")),e.named&&e.numeric?a:e.named?t?c:f.encodeNamed:e.numeric?f.encodeNumeric:f.encodeRaw},decode:function(e){return e.replace(u,function(e,n){return n?(n="x"===n.charAt(0).toLowerCase()?parseInt(n.substr(1),16):parseInt(n,10),n>65535?(n-=65536,String.fromCharCode(55296+(n>>10),56320+(1023&n))):d[n]||String.fromCharCode(n)):a[e]||i[e]||t(e)})}};return f}),r(x,[m,u],function(e,t){return function(n,r){function i(e){n.getElementsByTagName("head")[0].appendChild(e)}function o(r,o,c){function u(){for(var e=b.passed,t=e.length;t--;)e[t]();b.status=2,b.passed=[],b.failed=[]}function d(){for(var e=b.failed,t=e.length;t--;)e[t]();b.status=3,b.passed=[],b.failed=[]}function f(){var e=navigator.userAgent.match(/WebKit\/(\d*)/);return!!(e&&e[1]<536)}function h(e,n){e()||((new Date).getTime()-y0)return v=n.createElement("style"),v.textContent='@import "'+r+'"',m(),void i(v);p()}i(g),g.href=r}}var a=0,s={},l;r=r||{},l=r.maxLoadTime||5e3,this.load=o}}),r(w,[h,g,v,f,y,b,C,d,m,x],function(e,n,r,i,o,a,s,l,c,u){function d(e,t){var n={},r=t.keep_values,i;return i={set:function(n,r,i){t.url_converter&&(r=t.url_converter.call(t.url_converter_scope||e,r,i,n[0])),n.attr("data-mce-"+i,r).attr(i,r)},get:function(e,t){return e.attr("data-mce-"+t)||e.attr(t)}},n={style:{set:function(e,t){return null!==t&&"object"==typeof t?void e.css(t):(r&&e.attr("data-mce-style",t),void e.attr("style",t))},get:function(t){var n=t.attr("data-mce-style")||t.attr("style");return n=e.serializeStyle(e.parseStyle(n),t[0].nodeName)}}},r&&(n.href=n.src=i),n}function f(e,t){var n=t.attr("style");n=e.serializeStyle(e.parseStyle(n),t[0].nodeName),n||(n=null),t.attr("data-mce-style",n)}function h(e,t){var n=0,r,i;if(e)for(r=e.nodeType,e=e.previousSibling;e;e=e.previousSibling)i=e.nodeType,(!t||3!=i||i!=r&&e.nodeValue.length)&&(n++,r=i);return n}function p(e,t){var o=this,a;o.doc=e,o.win=window,o.files={},o.counter=0,o.stdMode=!b||e.documentMode>=8,o.boxModel=!b||"CSS1Compat"==e.compatMode||o.stdMode,o.styleSheetLoader=new u(e),o.boundEvents=[],o.settings=t=t||{},o.schema=t.schema,o.styles=new r({url_converter:t.url_converter,url_converter_scope:t.url_converter_scope},t.schema),o.fixDoc(e),o.events=t.ownEvents?new i(t.proxy):i.Event,o.attrHooks=d(o,t),a=t.schema?t.schema.getBlockElements():{},o.$=n.overrideDefaults(function(){return{context:e,element:o.getRoot()}}),o.isBlock=function(e){if(!e)return!1;var t=e.nodeType;return t?!(1!==t||!a[e.nodeName]):!!a[e]}}var m=c.each,g=c.is,v=c.grep,y=c.trim,b=l.ie,C=/^([a-z0-9],?)+$/i,x=/^[ \t\r\n]*$/;return p.prototype={$$:function(e){return"string"==typeof e&&(e=this.get(e)),this.$(e)},root:null,fixDoc:function(e){var t=this.settings,n;if(b&&t.schema){"abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video".replace(/\w+/g,function(t){e.createElement(t)});for(n in t.schema.getCustomElements())e.createElement(n)}},clone:function(e,t){var n=this,r,i;return!b||1!==e.nodeType||t?e.cloneNode(t):(i=n.doc,t?r.firstChild:(r=i.createElement(e.nodeName),m(n.getAttribs(e),function(t){n.setAttrib(r,t.nodeName,n.getAttrib(e,t.nodeName))}),r))},getRoot:function(){var e=this;return e.settings.root_element||e.doc.body},getViewPort:function(e){var t,n;return e=e?e:this.win,t=e.document,n=this.boxModel?t.documentElement:t.body,{x:e.pageXOffset||n.scrollLeft,y:e.pageYOffset||n.scrollTop,w:e.innerWidth||n.clientWidth,h:e.innerHeight||n.clientHeight}},getRect:function(e){var t=this,n,r;return e=t.get(e),n=t.getPos(e),r=t.getSize(e),{x:n.x,y:n.y,w:r.w,h:r.h}},getSize:function(e){var t=this,n,r;return e=t.get(e),n=t.getStyle(e,"width"),r=t.getStyle(e,"height"),-1===n.indexOf("px")&&(n=0),-1===r.indexOf("px")&&(r=0),{w:parseInt(n,10)||e.offsetWidth||e.clientWidth,h:parseInt(r,10)||e.offsetHeight||e.clientHeight}},getParent:function(e,t,n){return this.getParents(e,t,n,!1)},getParents:function(e,n,r,i){var o=this,a,s=[];for(e=o.get(e),i=i===t,r=r||("BODY"!=o.getRoot().nodeName?o.getRoot().parentNode:null),g(n,"string")&&(a=n, +n="*"===n?function(e){return 1==e.nodeType}:function(e){return o.is(e,a)});e&&e!=r&&e.nodeType&&9!==e.nodeType;){if(!n||n(e)){if(!i)return e;s.push(e)}e=e.parentNode}return i?s:null},get:function(e){var t;return e&&this.doc&&"string"==typeof e&&(t=e,e=this.doc.getElementById(e),e&&e.id!==t)?this.doc.getElementsByName(t)[1]:e},getNext:function(e,t){return this._findSib(e,t,"nextSibling")},getPrev:function(e,t){return this._findSib(e,t,"previousSibling")},select:function(t,n){var r=this;return e(t,r.get(n)||r.settings.root_element||r.doc,[])},is:function(n,r){var i;if(n.length===t){if("*"===r)return 1==n.nodeType;if(C.test(r)){for(r=r.toLowerCase().split(/,/),n=n.nodeName.toLowerCase(),i=r.length-1;i>=0;i--)if(r[i]==n)return!0;return!1}}if(n.nodeType&&1!=n.nodeType)return!1;var o=n.nodeType?[n]:n;return e(r,o[0].ownerDocument||o[0],null,o).length>0},add:function(e,t,n,r,i){var o=this;return this.run(e,function(e){var a;return a=g(t,"string")?o.doc.createElement(t):t,o.setAttribs(a,n),r&&(r.nodeType?a.appendChild(r):o.setHTML(a,r)),i?a:e.appendChild(a)})},create:function(e,t,n){return this.add(this.doc.createElement(e),e,t,n,1)},createHTML:function(e,t,n){var r="",i;r+="<"+e;for(i in t)t.hasOwnProperty(i)&&null!==t[i]&&"undefined"!=typeof t[i]&&(r+=" "+i+'="'+this.encode(t[i])+'"');return"undefined"!=typeof n?r+">"+n+"":r+" />"},createFragment:function(e){var t,n,r=this.doc,i;for(i=r.createElement("div"),t=r.createDocumentFragment(),e&&(i.innerHTML=e);n=i.firstChild;)t.appendChild(n);return t},remove:function(e,t){return e=this.$$(e),t?e.each(function(){for(var e;e=this.firstChild;)3==e.nodeType&&0===e.data.length?this.removeChild(e):this.parentNode.insertBefore(e,this)}).remove():e.remove(),e.length>1?e.toArray():e[0]},setStyle:function(e,t,n){e=this.$$(e).css(t,n),this.settings.update_styles&&f(this,e)},getStyle:function(e,n,r){return e=this.$$(e),r?e.css(n):(n=n.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}),"float"==n&&(n=l.ie&&l.ie<12?"styleFloat":"cssFloat"),e[0]&&e[0].style?e[0].style[n]:t)},setStyles:function(e,t){e=this.$$(e).css(t),this.settings.update_styles&&f(this,e)},removeAllAttribs:function(e){return this.run(e,function(e){var t,n=e.attributes;for(t=n.length-1;t>=0;t--)e.removeAttributeNode(n.item(t))})},setAttrib:function(e,t,n){var r=this,i,o,a=r.settings;""===n&&(n=null),e=r.$$(e),i=e.attr(t),e.length&&(o=r.attrHooks[t],o&&o.set?o.set(e,n,t):e.attr(t,n),i!=n&&a.onSetAttrib&&a.onSetAttrib({attrElm:e,attrName:t,attrValue:n}))},setAttribs:function(e,t){var n=this;n.$$(e).each(function(e,r){m(t,function(e,t){n.setAttrib(r,t,e)})})},getAttrib:function(e,t,n){var r=this,i,o;return e=r.$$(e),e.length&&(i=r.attrHooks[t],o=i&&i.get?i.get(e,t):e.attr(t)),"undefined"==typeof o&&(o=n||""),o},getPos:function(e,t){var r=this,i=0,o=0,a,s=r.doc,l=s.body,c;if(e=r.get(e),t=t||l,e){if(t===l&&e.getBoundingClientRect&&"static"===n(l).css("position"))return c=e.getBoundingClientRect(),t=r.boxModel?s.documentElement:l,i=c.left+(s.documentElement.scrollLeft||l.scrollLeft)-t.clientLeft,o=c.top+(s.documentElement.scrollTop||l.scrollTop)-t.clientTop,{x:i,y:o};for(a=e;a&&a!=t&&a.nodeType;)i+=a.offsetLeft||0,o+=a.offsetTop||0,a=a.offsetParent;for(a=e.parentNode;a&&a!=t&&a.nodeType;)i-=a.scrollLeft||0,o-=a.scrollTop||0,a=a.parentNode}return{x:i,y:o}},parseStyle:function(e){return this.styles.parse(e)},serializeStyle:function(e,t){return this.styles.serialize(e,t)},addStyle:function(e){var t=this,n=t.doc,r,i;if(t!==p.DOM&&n===document){var o=p.DOM.addedStyles;if(o=o||[],o[e])return;o[e]=!0,p.DOM.addedStyles=o}i=n.getElementById("mceDefaultStyles"),i||(i=n.createElement("style"),i.id="mceDefaultStyles",i.type="text/css",r=n.getElementsByTagName("head")[0],r.firstChild?r.insertBefore(i,r.firstChild):r.appendChild(i)),i.styleSheet?i.styleSheet.cssText+=e:i.appendChild(n.createTextNode(e))},loadCSS:function(e){var t=this,n=t.doc,r;return t!==p.DOM&&n===document?void p.DOM.loadCSS(e):(e||(e=""),r=n.getElementsByTagName("head")[0],void m(e.split(","),function(e){var i;e=c._addCacheSuffix(e),t.files[e]||(t.files[e]=!0,i=t.create("link",{rel:"stylesheet",href:e}),b&&n.documentMode&&n.recalc&&(i.onload=function(){n.recalc&&n.recalc(),i.onload=null}),r.appendChild(i))}))},addClass:function(e,t){this.$$(e).addClass(t)},removeClass:function(e,t){this.toggleClass(e,t,!1)},hasClass:function(e,t){return this.$$(e).hasClass(t)},toggleClass:function(e,t,r){this.$$(e).toggleClass(t,r).each(function(){""===this.className&&n(this).attr("class",null)})},show:function(e){this.$$(e).show()},hide:function(e){this.$$(e).hide()},isHidden:function(e){return"none"==this.$$(e).css("display")},uniqueId:function(e){return(e?e:"mce_")+this.counter++},setHTML:function(e,t){e=this.$$(e),b?e.each(function(e,r){if(r.canHaveHTML!==!1){for(;r.firstChild;)r.removeChild(r.firstChild);try{r.innerHTML="
    "+t,r.removeChild(r.firstChild)}catch(i){n("
    ").html("
    "+t).contents().slice(1).appendTo(r)}return t}}):e.html(t)},getOuterHTML:function(e){return e=this.get(e),1==e.nodeType&&"outerHTML"in e?e.outerHTML:n("
    ").append(n(e).clone()).html()},setOuterHTML:function(e,t){var r=this;r.$$(e).each(function(){try{if("outerHTML"in this)return void(this.outerHTML=t)}catch(e){}r.remove(n(this).html(t),!0)})},decode:s.decode,encode:s.encodeAllRaw,insertAfter:function(e,t){return t=this.get(t),this.run(e,function(e){var n,r;return n=t.parentNode,r=t.nextSibling,r?n.insertBefore(e,r):n.appendChild(e),e})},replace:function(e,t,n){var r=this;return r.run(t,function(t){return g(t,"array")&&(e=e.cloneNode(!0)),n&&m(v(t.childNodes),function(t){e.appendChild(t)}),t.parentNode.replaceChild(e,t)})},rename:function(e,t){var n=this,r;return e.nodeName!=t.toUpperCase()&&(r=n.create(t),m(n.getAttribs(e),function(t){n.setAttrib(r,t.nodeName,n.getAttrib(e,t.nodeName))}),n.replace(r,e,1)),r||e},findCommonAncestor:function(e,t){for(var n=e,r;n;){for(r=t;r&&n!=r;)r=r.parentNode;if(n==r)break;n=n.parentNode}return!n&&e.ownerDocument?e.ownerDocument.documentElement:n},toHex:function(e){return this.styles.toHex(c.trim(e))},run:function(e,t,n){var r=this,i;return"string"==typeof e&&(e=r.get(e)),e?(n=n||this,e.nodeType||!e.length&&0!==e.length?t.call(n,e):(i=[],m(e,function(e,o){e&&("string"==typeof e&&(e=r.get(e)),i.push(t.call(n,e,o)))}),i)):!1},getAttribs:function(e){var t;if(e=this.get(e),!e)return[];if(b){if(t=[],"OBJECT"==e.nodeName)return e.attributes;"OPTION"===e.nodeName&&this.getAttrib(e,"selected")&&t.push({specified:1,nodeName:"selected"});var n=/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;return e.cloneNode(!1).outerHTML.replace(n,"").replace(/[\w:\-]+/gi,function(e){t.push({specified:1,nodeName:e})}),t}return e.attributes},isEmpty:function(e,t){var n=this,r,i,a,s,l,c=0;if(e=e.firstChild){s=new o(e,e.parentNode),t=t||(n.schema?n.schema.getNonEmptyElements():null);do{if(a=e.nodeType,1===a){if(e.getAttribute("data-mce-bogus"))continue;if(l=e.nodeName.toLowerCase(),t&&t[l]){if("br"===l){c++;continue}return!1}for(i=n.getAttribs(e),r=i.length;r--;)if(l=i[r].nodeName,"name"===l||"data-mce-bookmark"===l)return!1}if(8==a)return!1;if(3===a&&!x.test(e.nodeValue))return!1}while(e=s.next())}return 1>=c},createRng:function(){var e=this.doc;return e.createRange?e.createRange():new a(this)},nodeIndex:h,split:function(e,t,n){function r(e){function t(e){var t=e.previousSibling&&"SPAN"==e.previousSibling.nodeName,n=e.nextSibling&&"SPAN"==e.nextSibling.nodeName;return t&&n}var n,o=e.childNodes,a=e.nodeType;if(1!=a||"bookmark"!=e.getAttribute("data-mce-type")){for(n=o.length-1;n>=0;n--)r(o[n]);if(9!=a){if(3==a&&e.nodeValue.length>0){var s=y(e.nodeValue).length;if(!i.isBlock(e.parentNode)||s>0||0===s&&t(e))return}else if(1==a&&(o=e.childNodes,1==o.length&&o[0]&&1==o[0].nodeType&&"bookmark"==o[0].getAttribute("data-mce-type")&&e.parentNode.insertBefore(o[0],e),o.length||/^(br|hr|input|img)$/i.test(e.nodeName)))return;i.remove(e)}return e}}var i=this,o=i.createRng(),a,s,l;return e&&t?(o.setStart(e.parentNode,i.nodeIndex(e)),o.setEnd(t.parentNode,i.nodeIndex(t)),a=o.extractContents(),o=i.createRng(),o.setStart(t.parentNode,i.nodeIndex(t)+1),o.setEnd(e.parentNode,i.nodeIndex(e)+1),s=o.extractContents(),l=e.parentNode,l.insertBefore(r(a),e),n?l.insertBefore(n,e):l.insertBefore(t,e),l.insertBefore(r(s),e),i.remove(e),n||t):void 0},bind:function(e,t,n,r){var i=this;if(c.isArray(e)){for(var o=e.length;o--;)e[o]=i.bind(e[o],t,n,r);return e}return!i.settings.collect||e!==i.doc&&e!==i.win||i.boundEvents.push([e,t,n,r]),i.events.bind(e,t,n,r||i)},unbind:function(e,t,n){var r=this,i;if(c.isArray(e)){for(i=e.length;i--;)e[i]=r.unbind(e[i],t,n);return e}if(r.boundEvents&&(e===r.doc||e===r.win))for(i=r.boundEvents.length;i--;){var o=r.boundEvents[i];e!=o[0]||t&&t!=o[1]||n&&n!=o[2]||this.events.unbind(o[0],o[1],o[2])}return this.events.unbind(e,t,n)},fire:function(e,t,n){return this.events.fire(e,t,n)},getContentEditable:function(e){var t;return e&&1==e.nodeType?(t=e.getAttribute("data-mce-contenteditable"),t&&"inherit"!==t?t:"inherit"!==e.contentEditable?e.contentEditable:null):null},getContentEditableParent:function(e){for(var t=this.getRoot(),n=null;e&&e!==t&&(n=this.getContentEditable(e),null===n);e=e.parentNode);return n},destroy:function(){var t=this;if(t.boundEvents){for(var n=t.boundEvents.length;n--;){var r=t.boundEvents[n];this.events.unbind(r[0],r[1],r[2])}t.boundEvents=null}e.setDocument&&e.setDocument(),t.win=t.doc=t.root=t.events=t.frag=null},isChildOf:function(e,t){for(;e;){if(t===e)return!0;e=e.parentNode}return!1},dumpRng:function(e){return"startContainer: "+e.startContainer.nodeName+", startOffset: "+e.startOffset+", endContainer: "+e.endContainer.nodeName+", endOffset: "+e.endOffset},_findSib:function(e,t,n){var r=this,i=t;if(e)for("string"==typeof i&&(i=function(e){return r.is(e,t)}),e=e[n];e;e=e[n])if(i(e))return e;return null}},p.DOM=new p(document),p.nodeIndex=h,p}),r(N,[w,m],function(e,t){function n(){function e(e,n){function i(){a.remove(l),s&&(s.onreadystatechange=s.onload=s=null),n()}function o(){"undefined"!=typeof console&&console.log&&console.log("Failed to load: "+e)}var a=r,s,l;l=a.uniqueId(),s=document.createElement("script"),s.id=l,s.type="text/javascript",s.src=t._addCacheSuffix(e),"onreadystatechange"in s?s.onreadystatechange=function(){/loaded|complete/.test(s.readyState)&&i()}:s.onload=i,s.onerror=o,(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}var n=0,a=1,s=2,l={},c=[],u={},d=[],f=0,h;this.isDone=function(e){return l[e]==s},this.markDone=function(e){l[e]=s},this.add=this.load=function(e,t,r){var i=l[e];i==h&&(c.push(e),l[e]=n),t&&(u[e]||(u[e]=[]),u[e].push({func:t,scope:r||this}))},this.remove=function(e){delete l[e],delete u[e]},this.loadQueue=function(e,t){this.loadScripts(c,e,t)},this.loadScripts=function(t,n,r){function c(e){i(u[e],function(e){e.func.call(e.scope)}),u[e]=h}var p;d.push({func:n,scope:r||this}),(p=function(){var n=o(t);t.length=0,i(n,function(t){return l[t]==s?void c(t):void(l[t]!=a&&(l[t]=a,f++,e(t,function(){l[t]=s,f--,c(t),p()})))}),f||(i(d,function(e){e.func.call(e.scope)}),d.length=0)})()}}var r=e.DOM,i=t.each,o=t.grep;return n.ScriptLoader=new n,n}),r(E,[N,m],function(e,n){function r(){var e=this;e.items=[],e.urls={},e.lookup={}}var i=n.each;return r.prototype={get:function(e){return this.lookup[e]?this.lookup[e].instance:t},dependencies:function(e){var t;return this.lookup[e]&&(t=this.lookup[e].dependencies),t||[]},requireLangPack:function(t,n){var i=r.language;if(i&&r.languageLoad!==!1){if(n)if(n=","+n+",",-1!=n.indexOf(","+i.substr(0,2)+","))i=i.substr(0,2);else if(-1==n.indexOf(","+i+","))return;e.ScriptLoader.add(this.urls[t]+"/langs/"+i+".js")}},add:function(e,t,n){return this.items.push(t),this.lookup[e]={instance:t,dependencies:n},t},remove:function(e){delete this.urls[e],delete this.lookup[e]},createUrl:function(e,t){return"object"==typeof t?t:{prefix:e.prefix,resource:t,suffix:e.suffix}},addComponents:function(t,n){var r=this.urls[t];i(n,function(t){e.ScriptLoader.add(r+"/"+t)})},load:function(n,o,a,s){function l(){var r=c.dependencies(n);i(r,function(e){var n=c.createUrl(o,e);c.load(n.resource,n,t,t)}),a&&(s?a.call(s):a.call(e))}var c=this,u=o;c.urls[n]||("object"==typeof o&&(u=o.prefix+o.resource+o.suffix),0!==u.indexOf("/")&&-1==u.indexOf("://")&&(u=r.baseURL+"/"+u),c.urls[n]=u.substring(0,u.lastIndexOf("/")),c.lookup[n]?l():e.ScriptLoader.add(u,l,s))}},r.PluginManager=new r,r.ThemeManager=new r,r}),r(_,[],function(){function e(e){return function(t){return!!t&&t.nodeType==e}}function t(e){return e=e.toLowerCase().split(" "),function(t){var n,r;if(t&&t.nodeType)for(r=t.nodeName.toLowerCase(),n=0;nn.length-1?t=n.length-1:0>t&&(t=0),n[t]||e}function a(e){this.walk=function(t,n){function r(e){var t;return t=e[0],3===t.nodeType&&t===c&&u>=t.nodeValue.length&&e.splice(0,1),t=e[e.length-1],0===f&&e.length>0&&t===d&&3===t.nodeType&&e.splice(e.length-1,1),e}function i(e,t,n){for(var r=[];e&&e!=n;e=e[t])r.push(e);return r}function a(e,t){do{if(e.parentNode==t)return e;e=e.parentNode}while(e)}function s(e,t,o){var a=o?"nextSibling":"previousSibling";for(g=e,v=g.parentNode;g&&g!=t;g=v)v=g.parentNode,y=i(g==e?g:g[a],a),y.length&&(o||y.reverse(),n(r(y)))}var c=t.startContainer,u=t.startOffset,d=t.endContainer,f=t.endOffset,h,p,m,g,v,y,b;if(b=e.select("td[data-mce-selected],th[data-mce-selected]"),b.length>0)return void l(b,function(e){n([e])});if(1==c.nodeType&&c.hasChildNodes()&&(c=c.childNodes[u]),1==d.nodeType&&d.hasChildNodes()&&(d=o(d,f)),c==d)return n(r([c]));for(h=e.findCommonAncestor(c,d),g=c;g;g=g.parentNode){if(g===d)return s(c,h,!0);if(g===h)break}for(g=d;g;g=g.parentNode){if(g===c)return s(d,h);if(g===h)break}p=a(c,h)||c,m=a(d,h)||d,s(c,p,!0),y=i(p==c?p:p.nextSibling,"nextSibling",m==d?m.nextSibling:m),y.length&&n(r(y)),s(d,m)},this.split=function(e){function t(e,t){return e.splitText(t)}var n=e.startContainer,r=e.startOffset,i=e.endContainer,o=e.endOffset;return n==i&&3==n.nodeType?r>0&&rr?(o-=r,n=i=t(i,o).previousSibling,o=i.nodeValue.length,r=0):o=0):(3==n.nodeType&&r>0&&r0&&o0)return h=v,p=n?v.nodeValue.length:0,void(i=!0);if(e.isBlock(v)||y[v.nodeName.toLowerCase()])return;s=v}o&&s&&(h=s,i=!0,p=0)}var h,p,m,g=e.getRoot(),v,y,b,C;if(h=n[(r?"start":"end")+"Container"],p=n[(r?"start":"end")+"Offset"],C=1==h.nodeType&&p===h.childNodes.length,y=e.schema.getNonEmptyElements(),b=r,!u(h)){if(1==h.nodeType&&p>h.childNodes.length-1&&(b=!1),9===h.nodeType&&(h=e.getRoot(),p=0),h===g){if(b&&(v=h.childNodes[p>0?p-1:0])){if(u(v))return;if(y[v.nodeName]||"TABLE"==v.nodeName)return}if(h.hasChildNodes()){if(p=Math.min(!b&&p>0?p-1:p,h.childNodes.length-1),h=h.childNodes[p],p=0,!o&&h===g.lastChild&&"TABLE"===h.nodeName)return;if(l(h)||u(h))return;if(h.hasChildNodes()&&!/TABLE/.test(h.nodeName)){v=h,m=new t(h,g);do{if(c(v)||u(v)){i=!1;break}if(3===v.nodeType&&v.nodeValue.length>0){p=b?0:v.nodeValue.length,h=v,i=!0;break}if(y[v.nodeName.toLowerCase()]&&!a(v)){p=e.nodeIndex(v),h=v.parentNode,"IMG"!=v.nodeName||b||p++,i=!0;break}}while(v=b?m.next():m.prev())}}}o&&(3===h.nodeType&&0===p&&f(!0),1===h.nodeType&&(v=h.childNodes[p],v||(v=h.childNodes[p-1]),!v||"BR"!==v.nodeName||d(v,"A")||s(v)||s(v,!0)||f(!0,v))),b&&!o&&3===h.nodeType&&p===h.nodeValue.length&&f(!1),i&&n["set"+(r?"Start":"End")](h,p)}}var i,o;return o=n.collapsed,r(!0),o||r(),i&&o&&n.collapse(!0),i}}function s(t,n,r){var i,o,a;if(i=r.elementFromPoint(t,n),o=r.body.createTextRange(),i&&"HTML"!=i.tagName||(i=r.body),o.moveToElementText(i),a=e.toArray(o.getClientRects()),a=a.sort(function(e,t){return e=Math.abs(Math.max(e.top-n,e.bottom-n)),t=Math.abs(Math.max(t.top-n,t.bottom-n)),e-t}),a.length>0){n=(a[0].bottom+a[0].top)/2;try{return o.moveToPoint(t,n),o.collapse(!0),o}catch(s){}}return null}var l=e.each,c=n.isContentEditableFalse,u=i.isCaretContainer;return a.compareRanges=function(e,t){if(e&&t){if(!e.item&&!e.duplicate)return e.startContainer==t.startContainer&&e.startOffset==t.startOffset;if(e.item&&t.item&&e.item(0)===t.item(0))return!0;if(e.isEqual&&t.isEqual&&t.isEqual(e))return!0}return!1},a.getCaretRangeFromPoint=function(e,t,n){var r,i;if(n.caretPositionFromPoint)i=n.caretPositionFromPoint(e,t),r=n.createRange(),r.setStart(i.offsetNode,i.offset),r.collapse(!0);else if(n.caretRangeFromPoint)r=n.caretRangeFromPoint(e,t);else if(n.body.createTextRange){r=n.body.createTextRange();try{r.moveToPoint(e,t),r.collapse(!0)}catch(o){r=s(e,t,n)}}return r},a.getSelectedNode=function(e){var t=e.startContainer,n=e.startOffset;return t.hasChildNodes()&&e.endOffset==n+1?t.childNodes[n]:null},a.getNode=function(e,t){return 1==e.nodeType&&e.hasChildNodes()&&(t>=e.childNodes.length&&(t=e.childNodes.length-1),e=e.childNodes[t]),e},a}),r(R,[T,d,u],function(e,t,n){return function(r){function i(e){var t,n;if(n=r.$(e).parentsUntil(r.getBody()).add(e),n.length===a.length){for(t=n.length;t>=0&&n[t]===a[t];t--);if(-1===t)return a=n,!0}return a=n,!1}var o,a=[];"onselectionchange"in r.getDoc()||r.on("NodeChange Click MouseUp KeyUp Focus",function(t){var n,i;n=r.selection.getRng(),i={startContainer:n.startContainer,startOffset:n.startOffset,endContainer:n.endContainer,endOffset:n.endOffset},"nodechange"!=t.type&&e.compareRanges(i,o)||r.fire("SelectionChange"),o=i}),r.on("contextmenu",function(){r.fire("SelectionChange")}),r.on("SelectionChange",function(){var e=r.selection.getStart(!0);!t.range&&r.selection.isCollapsed()||!i(e)&&r.dom.isChildOf(e,r.getBody())&&r.nodeChanged({selectionChange:!0})}),r.on("MouseUp",function(e){e.isDefaultPrevented()||("IMG"==r.selection.getNode().nodeName?n.setEditorTimeout(r,function(){r.nodeChanged()}):r.nodeChanged())}),this.nodeChanged=function(e){var t=r.selection,n,i,o;r.initialized&&t&&!r.settings.disable_nodechange&&!r.readonly&&(o=r.getBody(),n=t.getStart()||o,n.ownerDocument==r.getDoc()&&r.dom.isChildOf(n,o)||(n=o),"IMG"==n.nodeName&&t.isCollapsed()&&(n=n.parentNode),i=[],r.dom.getParent(n,function(e){return e===o?!0:void i.push(e)}),e=e||{},e.element=n,e.parents=i,r.fire("NodeChange",e))}}}),r(A,[],function(){function e(e,t,n){var r,i,o=n?"lastChild":"firstChild",a=n?"prev":"next";if(e[o])return e[o];if(e!==t){if(r=e[a])return r;for(i=e.parent;i&&i!==t;i=i.parent)if(r=i[a])return r}}function t(e,t){this.name=e,this.type=t,1===t&&(this.attributes=[],this.attributes.map={})}var n=/^[ \t\r\n]*$/,r={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};return t.prototype={replace:function(e){var t=this;return e.parent&&e.remove(),t.insert(e,t),t.remove(),t},attr:function(e,t){var n=this,r,i,o;if("string"!=typeof e){for(i in e)n.attr(i,e[i]);return n}if(r=n.attributes){if(t!==o){if(null===t){if(e in r.map)for(delete r.map[e],i=r.length;i--;)if(r[i].name===e)return r=r.splice(i,1),n;return n}if(e in r.map){for(i=r.length;i--;)if(r[i].name===e){r[i].value=t;break}}else r.push({name:e,value:t});return r.map[e]=t,n}return r.map[e]}},clone:function(){var e=this,n=new t(e.name,e.type),r,i,o,a,s;if(o=e.attributes){for(s=[],s.map={},r=0,i=o.length;i>r;r++)a=o[r],"id"!==a.name&&(s[s.length]={name:a.name,value:a.value},s.map[a.name]=a.value);n.attributes=s}return n.value=e.value,n.shortEnded=e.shortEnded,n},wrap:function(e){var t=this;return t.parent.insert(e,t),e.append(t),t},unwrap:function(){var e=this,t,n;for(t=e.firstChild;t;)n=t.next,e.insert(t,e,!0),t=n;e.remove()},remove:function(){var e=this,t=e.parent,n=e.next,r=e.prev;return t&&(t.firstChild===e?(t.firstChild=n,n&&(n.prev=null)):r.next=n,t.lastChild===e?(t.lastChild=r,r&&(r.next=null)):n.prev=r,e.parent=e.next=e.prev=null),e},append:function(e){var t=this,n;return e.parent&&e.remove(),n=t.lastChild,n?(n.next=e,e.prev=n,t.lastChild=e):t.lastChild=t.firstChild=e,e.parent=t,e},insert:function(e,t,n){var r;return e.parent&&e.remove(),r=t.parent||this,n?(t===r.firstChild?r.firstChild=e:t.prev.next=e,e.prev=t.prev,e.next=t,t.prev=e):(t===r.lastChild?r.lastChild=e:t.next.prev=e,e.next=t.next,e.prev=t,t.next=e),e.parent=r,e},getAll:function(t){var n=this,r,i=[];for(r=n.firstChild;r;r=e(r,n))r.name===t&&i.push(r);return i},empty:function(){var t=this,n,r,i;if(t.firstChild){for(n=[],i=t.firstChild;i;i=e(i,t))n.push(i);for(r=n.length;r--;)i=n[r],i.parent=i.firstChild=i.lastChild=i.next=i.prev=null}return t.firstChild=t.lastChild=null,t},isEmpty:function(t){var r=this,i=r.firstChild,o,a;if(i)do{if(1===i.type){if(i.attributes.map["data-mce-bogus"])continue;if(t[i.name])return!1;for(o=i.attributes.length;o--;)if(a=i.attributes[o].name,"name"===a||0===a.indexOf("data-mce-bookmark"))return!1}if(8===i.type)return!1;if(3===i.type&&!n.test(i.value))return!1}while(i=e(i,r));return!0},walk:function(t){return e(this,null,t)}},t.create=function(e,n){var i,o;if(i=new t(e,r[e]||1),n)for(o in n)i.attr(o,n[o]);return i},t}),r(B,[m],function(e){function t(e,t){return e?e.split(t||" "):[]}function n(e){function n(e,n,r){function i(e,t){var n={},r,i;for(r=0,i=e.length;i>r;r++)n[e[r]]=t||{};return n}var s,c,u,d=arguments;for(r=r||[],n=n||"","string"==typeof r&&(r=t(r)),c=3;co;o++)i.attributes[n[o]]={},i.attributesOrder.push(n[o])}var a={},l,c,u,d,f,h;return i[e]?i[e]:(l=t("id accesskey class dir lang style tabindex title"),c=t("address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"),u=t("a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd label map noscript object q s samp script select small span strong sub sup textarea u var #text #comment"),"html4"!=e&&(l.push.apply(l,t("contenteditable contextmenu draggable dropzone hidden spellcheck translate")),c.push.apply(c,t("article aside details dialog figure header footer hgroup section nav")),u.push.apply(u,t("audio canvas command datalist mark meter output picture progress time wbr video ruby bdi keygen"))),"html5-strict"!=e&&(l.push("xml:lang"),h=t("acronym applet basefont big font strike tt"),u.push.apply(u,h),s(h,function(e){n(e,"",u)}),f=t("center dir isindex noframes"),c.push.apply(c,f),d=[].concat(c,u),s(f,function(e){n(e,"",d)})),d=d||[].concat(c,u),n("html","manifest","head body"),n("head","","base command link meta noscript script style title"),n("title hr noscript br"),n("base","href target"),n("link","href rel media hreflang type sizes hreflang"),n("meta","name http-equiv content charset"),n("style","media type scoped"),n("script","src async defer type charset"),n("body","onafterprint onbeforeprint onbeforeunload onblur onerror onfocus onhashchange onload onmessage onoffline ononline onpagehide onpageshow onpopstate onresize onscroll onstorage onunload",d),n("address dt dd div caption","",d),n("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn","",u),n("blockquote","cite",d),n("ol","reversed start type","li"),n("ul","","li"),n("li","value",d),n("dl","","dt dd"),n("a","href target rel media hreflang type",u),n("q","cite",u),n("ins del","cite datetime",d),n("img","src sizes srcset alt usemap ismap width height"),n("iframe","src name width height",d),n("embed","src type width height"),n("object","data type typemustmatch name usemap form width height",d,"param"),n("param","name value"),n("map","name",d,"area"),n("area","alt coords shape href target rel media hreflang type"),n("table","border","caption colgroup thead tfoot tbody tr"+("html4"==e?" col":"")),n("colgroup","span","col"),n("col","span"),n("tbody thead tfoot","","tr"),n("tr","","td th"),n("td","colspan rowspan headers",d),n("th","colspan rowspan headers scope abbr",d),n("form","accept-charset action autocomplete enctype method name novalidate target",d),n("fieldset","disabled form name",d,"legend"),n("label","form for",u),n("input","accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"),n("button","disabled form formaction formenctype formmethod formnovalidate formtarget name type value","html4"==e?d:u),n("select","disabled form multiple name required size","option optgroup"),n("optgroup","disabled label","option"),n("option","disabled label selected value"),n("textarea","cols dirname disabled form maxlength name readonly required rows wrap"),n("menu","type label",d,"li"),n("noscript","",d),"html4"!=e&&(n("wbr"),n("ruby","",u,"rt rp"),n("figcaption","",d),n("mark rt rp summary bdi","",u),n("canvas","width height",d),n("video","src crossorigin poster preload autoplay mediagroup loop muted controls width height buffered",d,"track source"),n("audio","src crossorigin preload autoplay mediagroup loop muted controls buffered volume",d,"track source"),n("picture","","img source"),n("source","src srcset type media sizes"),n("track","kind src srclang label default"),n("datalist","",u,"option"),n("article section nav aside header footer","",d),n("hgroup","","h1 h2 h3 h4 h5 h6"),n("figure","",d,"figcaption"),n("time","datetime",u),n("dialog","open",d),n("command","type label icon disabled checked radiogroup command"),n("output","for form name",u),n("progress","value max",u),n("meter","value min max low high optimum",u),n("details","open",d,"summary"),n("keygen","autofocus challenge disabled form keytype name")),"html5-strict"!=e&&(r("script","language xml:space"),r("style","xml:space"),r("object","declare classid code codebase codetype archive standby align border hspace vspace"),r("embed","align name hspace vspace"),r("param","valuetype type"),r("a","charset name rev shape coords"),r("br","clear"),r("applet","codebase archive code object alt name width height align hspace vspace"),r("img","name longdesc align border hspace vspace"),r("iframe","longdesc frameborder marginwidth marginheight scrolling align"),r("font basefont","size color face"),r("input","usemap align"),r("select","onchange"),r("textarea"),r("h1 h2 h3 h4 h5 h6 div p legend caption","align"),r("ul","type compact"),r("li","type"),r("ol dl menu dir","compact"),r("pre","width xml:space"),r("hr","align noshade size width"),r("isindex","prompt"),r("table","summary width frame rules cellspacing cellpadding align bgcolor"),r("col","width align char charoff valign"),r("colgroup","width align char charoff valign"),r("thead","align char charoff valign"),r("tr","align char charoff valign bgcolor"),r("th","axis align char charoff valign nowrap bgcolor width height"),r("form","accept"),r("td","abbr axis scope align char charoff valign nowrap bgcolor width height"),r("tfoot","align char charoff valign"),r("tbody","align char charoff valign"),r("area","nohref"),r("body","background bgcolor text link vlink alink")),"html4"!=e&&(r("input button select textarea","autofocus"),r("input textarea","placeholder"),r("a","download"),r("link script img","crossorigin"),r("iframe","sandbox seamless allowfullscreen")),s(t("a form meter progress dfn"),function(e){a[e]&&delete a[e].children[e]}),delete a.caption.children.table,delete a.script,i[e]=a,a)}function r(e,t){var n;return e&&(n={},"string"==typeof e&&(e={"*":e}),s(e,function(e,r){n[r]=n[r.toUpperCase()]="map"==t?a(e,/[, ]/):c(e,/[, ]/)})),n}var i={},o={},a=e.makeMap,s=e.each,l=e.extend,c=e.explode,u=e.inArray;return function(e){function o(t,n,r){var o=e[t];return o?o=a(o,/[, ]/,a(o.toUpperCase(),/[, ]/)):(o=i[t],o||(o=a(n," ",a(n.toUpperCase()," ")),o=l(o,r),i[t]=o)),o}function d(e){return new RegExp("^"+e.replace(/([?+*])/g,".$1")+"$")}function f(e){var n,r,i,o,s,l,c,f,h,p,m,g,v,b,x,w,N,E,_,S=/^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,k=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,T=/[*?+]/;if(e)for(e=t(e,","),y["@"]&&(w=y["@"].attributes,N=y["@"].attributesOrder),n=0,r=e.length;r>n;n++)if(s=S.exec(e[n])){if(b=s[1],h=s[2],x=s[3],f=s[5],g={},v=[],l={attributes:g,attributesOrder:v},"#"===b&&(l.paddEmpty=!0),"-"===b&&(l.removeEmpty=!0),"!"===s[4]&&(l.removeEmptyAttrs=!0),w){for(E in w)g[E]=w[E];v.push.apply(v,N)}if(f)for(f=t(f,"|"),i=0,o=f.length;o>i;i++)if(s=k.exec(f[i])){if(c={},m=s[1],p=s[2].replace(/::/g,":"),b=s[3],_=s[4],"!"===m&&(l.attributesRequired=l.attributesRequired||[],l.attributesRequired.push(p),c.required=!0),"-"===m){delete g[p],v.splice(u(v,p),1);continue}b&&("="===b&&(l.attributesDefault=l.attributesDefault||[],l.attributesDefault.push({name:p,value:_}),c.defaultValue=_),":"===b&&(l.attributesForced=l.attributesForced||[],l.attributesForced.push({name:p,value:_}),c.forcedValue=_),"<"===b&&(c.validValues=a(_,"?"))), +T.test(p)?(l.attributePatterns=l.attributePatterns||[],c.pattern=d(p),l.attributePatterns.push(c)):(g[p]||v.push(p),g[p]=c)}w||"@"!=h||(w=g,N=v),x&&(l.outputName=h,y[x]=l),T.test(h)?(l.pattern=d(h),C.push(l)):y[h]=l}}function h(e){y={},C=[],f(e),s(N,function(e,t){b[t]=e.children})}function p(e){var n=/^(~)?(.+)$/;e&&(i.text_block_elements=i.block_elements=null,s(t(e,","),function(e){var t=n.exec(e),r="~"===t[1],i=r?"span":"div",o=t[2];if(b[o]=b[i],M[o]=i,r||(R[o.toUpperCase()]={},R[o]={}),!y[o]){var a=y[i];a=l({},a),delete a.removeEmptyAttrs,delete a.removeEmpty,y[o]=a}s(b,function(e,t){e[i]&&(b[t]=e=l({},b[t]),e[o]=e[i])})}))}function m(n){var r=/^([+\-]?)(\w+)\[([^\]]+)\]$/;i[e.schema]=null,n&&s(t(n,","),function(e){var n=r.exec(e),i,o;n&&(o=n[1],i=o?b[n[2]]:b[n[2]]={"#comment":{}},i=b[n[2]],s(t(n[3],"|"),function(e){"-"===o?delete i[e]:i[e]={}}))})}function g(e){var t=y[e],n;if(t)return t;for(n=C.length;n--;)if(t=C[n],t.pattern.test(e))return t}var v=this,y={},b={},C=[],x,w,N,E,_,S,k,T,R,A,B,D,L,M={},P={};e=e||{},N=n(e.schema),e.verify_html===!1&&(e.valid_elements="*[*]"),x=r(e.valid_styles),w=r(e.invalid_styles,"map"),T=r(e.valid_classes,"map"),E=o("whitespace_elements","pre script noscript style textarea video audio iframe object"),_=o("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr"),S=o("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr track"),k=o("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls"),A=o("non_empty_elements","td th iframe video audio object script",S),B=o("move_caret_before_on_enter_elements","table",A),D=o("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside nav figure"),R=o("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex option datalist select optgroup figcaption",D),L=o("text_inline_elements","span strong b em i font strike u var cite dfn code mark q sup sub samp"),s((e.special||"script noscript style textarea").split(" "),function(e){P[e]=new RegExp("]*>","gi")}),e.valid_elements?h(e.valid_elements):(s(N,function(e,t){y[t]={attributes:e.attributes,attributesOrder:e.attributesOrder},b[t]=e.children}),"html5"!=e.schema&&s(t("strong/b em/i"),function(e){e=t(e,"/"),y[e[1]].outputName=e[0]}),s(t("ol ul sub sup blockquote span font a table tbody tr strong em b i"),function(e){y[e]&&(y[e].removeEmpty=!0)}),s(t("p h1 h2 h3 h4 h5 h6 th td pre div address caption"),function(e){y[e].paddEmpty=!0}),s(t("span"),function(e){y[e].removeEmptyAttrs=!0})),p(e.custom_elements),m(e.valid_children),f(e.extended_valid_elements),m("+ol[ul|ol],+ul[ul|ol]"),e.invalid_elements&&s(c(e.invalid_elements),function(e){y[e]&&delete y[e]}),g("span")||f("span[!data-mce-type|*]"),v.children=b,v.getValidStyles=function(){return x},v.getInvalidStyles=function(){return w},v.getValidClasses=function(){return T},v.getBoolAttrs=function(){return k},v.getBlockElements=function(){return R},v.getTextBlockElements=function(){return D},v.getTextInlineElements=function(){return L},v.getShortEndedElements=function(){return S},v.getSelfClosingElements=function(){return _},v.getNonEmptyElements=function(){return A},v.getMoveCaretBeforeOnEnterElements=function(){return B},v.getWhiteSpaceElements=function(){return E},v.getSpecialElements=function(){return P},v.isValidChild=function(e,t){var n=b[e];return!(!n||!n[t])},v.isValid=function(e,t){var n,r,i=g(e);if(i){if(!t)return!0;if(i.attributes[t])return!0;if(n=i.attributePatterns)for(r=n.length;r--;)if(n[r].pattern.test(e))return!0}return!1},v.getElementRule=g,v.getCustomElements=function(){return M},v.addValidElements=f,v.setValidElements=h,v.addCustomElements=p,v.addValidChildren=m,v.elements=y}}),r(D,[B,C,m],function(e,t,n){function r(e,t,n){var r=1,i,o,a,s;for(s=e.getShortEndedElements(),a=/<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g,a.lastIndex=i=n;o=a.exec(t);){if(i=a.lastIndex,"/"===o[1])r--;else if(!o[1]){if(o[2]in s)continue;r++}if(0===r)break}return i}function i(i,a){function s(){}var l=this;i=i||{},l.schema=a=a||new e,i.fix_self_closing!==!1&&(i.fix_self_closing=!0),o("comment cdata text start end pi doctype".split(" "),function(e){e&&(l[e]=i[e]||s)}),l.parse=function(e){function o(e){var t,n;for(t=h.length;t--&&h[t].name!==e;);if(t>=0){for(n=h.length-1;n>=t;n--)e=h[n],e.valid&&l.end(e.name);h.length=t}}function s(e,t,n,r,o){var a,s,l=/[\s\u0000-\u001F]+/g;if(t=t.toLowerCase(),n=t in x?t:z(n||r||o||""),N&&!y&&0!==t.indexOf("data-")){if(a=T[t],!a&&R){for(s=R.length;s--&&(a=R[s],!a.pattern.test(t)););-1===s&&(a=null)}if(!a)return;if(a.validValues&&!(n in a.validValues))return}if(W[t]&&!i.allow_script_urls){var c=n.replace(l,"");try{c=decodeURIComponent(c)}catch(u){c=unescape(c)}if(V.test(c))return;if(!i.allow_html_data_urls&&$.test(c)&&!/^data:image\//i.test(c))return}p.map[t]=n,p.push({name:t,value:n})}var l=this,c,u=0,d,f,h=[],p,m,g,v,y,b,C,x,w,N,E,_,S,k,T,R,A,B,D,L,M,P,O,H,I,F=0,z=t.decode,U,W=n.makeMap("src,href,data,background,formaction,poster"),V=/((java|vb)script|mhtml):/i,$=/^data:/i;for(P=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([A-Za-z0-9\\-_\\:\\.]+)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g"),O=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g,C=a.getShortEndedElements(),M=i.self_closing_elements||a.getSelfClosingElements(),x=a.getBoolAttrs(),N=i.validate,b=i.remove_internals,U=i.fix_self_closing,H=a.getSpecialElements();c=P.exec(e);){if(u0&&h[h.length-1].name===d&&o(d),!N||(E=a.getElementRule(d))){if(_=!0,N&&(T=E.attributes,R=E.attributePatterns),(k=c[8])?(y=-1!==k.indexOf("data-mce-type"),y&&b&&(_=!1),p=[],p.map={},k.replace(O,s)):(p=[],p.map={}),N&&!y){if(A=E.attributesRequired,B=E.attributesDefault,D=E.attributesForced,L=E.removeEmptyAttrs,L&&!p.length&&(_=!1),D)for(m=D.length;m--;)S=D[m],v=S.name,I=S.value,"{$uid}"===I&&(I="mce_"+F++),p.map[v]=I,p.push({name:v,value:I});if(B)for(m=B.length;m--;)S=B[m],v=S.name,v in p.map||(I=S.value,"{$uid}"===I&&(I="mce_"+F++),p.map[v]=I,p.push({name:v,value:I}));if(A){for(m=A.length;m--&&!(A[m]in p.map););-1===m&&(_=!1)}if(S=p.map["data-mce-bogus"]){if("all"===S){u=r(a,e,P.lastIndex),P.lastIndex=u;continue}_=!1}}_&&l.start(d,p,w)}else _=!1;if(f=H[d]){f.lastIndex=u=c.index+c[0].length,(c=f.exec(e))?(_&&(g=e.substr(u,c.index-u)),u=c.index+c[0].length):(g=e.substr(u),u=e.length),_&&(g.length>0&&l.text(g,!0),l.end(d)),P.lastIndex=u;continue}w||(k&&k.indexOf("/")==k.length-1?_&&l.end(d):h.push({name:d,valid:_}))}else(d=c[1])?(">"===d.charAt(0)&&(d=" "+d),i.allow_conditional_comments||"[if"!==d.substr(0,3)||(d=" "+d),l.comment(d)):(d=c[2])?l.cdata(d):(d=c[3])?l.doctype(d):(d=c[4])&&l.pi(d,c[5]);u=c.index+c[0].length}for(u=0;m--)d=h[m],d.valid&&l.end(d.name)}}var o=n.each;return i.findEndTag=r,i}),r(L,[A,B,D,m],function(e,t,n,r){var i=r.makeMap,o=r.each,a=r.explode,s=r.extend;return function(r,l){function c(t){var n,r,o,a,s,c,d,f,h,p,m,g,v,y,b;for(m=i("tr,td,th,tbody,thead,tfoot,table"),p=l.getNonEmptyElements(),g=l.getTextBlockElements(),v=l.getSpecialElements(),n=0;n1){for(a.reverse(),s=c=u.filterNode(a[0].clone()),h=0;h0)return void(t.value=r);if(n=t.next){if(3==n.type&&n.value.length){t=t.prev;continue}if(!o[n.name]&&"script"!=n.name&&"style"!=n.name){t=t.prev;continue}}i=t.prev,t.remove(),t=i}}function g(e){var t,n={};for(t in e)"li"!==t&&"p"!=t&&(n[t]=e[t]);return n}var v,y,b,C,x,w,N,E,_,S,k,T,R,A=[],B,D,L,M,P,O,H,I;if(o=o||{},h={},p={},T=s(i("script,style,head,html,body,title,meta,param"),l.getBlockElements()),H=l.getNonEmptyElements(),O=l.children,k=r.validate,I="forced_root_block"in o?o.forced_root_block:r.forced_root_block,P=l.getWhiteSpaceElements(),R=/^[ \t\r\n]+/,D=/[ \t\r\n]+$/,L=/[ \t\r\n]+/g,M=/^[ \t\r\n]+$/,v=new n({validate:k,allow_script_urls:r.allow_script_urls,allow_conditional_comments:r.allow_conditional_comments,self_closing_elements:g(l.getSelfClosingElements()),cdata:function(e){b.append(u("#cdata",4)).value=e},text:function(e,t){var n;B||(e=e.replace(L," "),b.lastChild&&T[b.lastChild.name]&&(e=e.replace(R,""))),0!==e.length&&(n=u("#text",3),n.raw=!!t,b.append(n).value=e)},comment:function(e){b.append(u("#comment",8)).value=e},pi:function(e,t){b.append(u(e,7)).value=t,m(b)},doctype:function(e){var t;t=b.append(u("#doctype",10)),t.value=e,m(b)},start:function(e,t,n){var r,i,o,a,s;if(o=k?l.getElementRule(e):{}){for(r=u(o.outputName||e,1),r.attributes=t,r.shortEnded=n,b.append(r),s=O[b.name],s&&O[r.name]&&!s[r.name]&&A.push(r),i=f.length;i--;)a=f[i].name,a in t.map&&(_=p[a],_?_.push(r):p[a]=[r]);T[e]&&m(r),n||(b=r),!B&&P[e]&&(B=!0)}},end:function(t){var n,r,i,o,a;if(r=k?l.getElementRule(t):{}){if(T[t]&&!B){if(n=b.firstChild,n&&3===n.type)if(i=n.value.replace(R,""),i.length>0)n.value=i,n=n.next;else for(o=n.next,n.remove(),n=o;n&&3===n.type;)i=n.value,o=n.next,(0===i.length||M.test(i))&&(n.remove(),n=o),n=o;if(n=b.lastChild,n&&3===n.type)if(i=n.value.replace(D,""),i.length>0)n.value=i,n=n.prev;else for(o=n.prev,n.remove(),n=o;n&&3===n.type;)i=n.value,o=n.prev,(0===i.length||M.test(i))&&(n.remove(),n=o),n=o}if(B&&P[t]&&(B=!1),(r.removeEmpty||r.paddEmpty)&&b.isEmpty(H))if(r.paddEmpty)b.empty().append(new e("#text","3")).value="\xa0";else if(!b.attributes.map.name&&!b.attributes.map.id)return a=b.parent,T[b.name]?b.empty().remove():b.unwrap(),void(b=a);b=b.parent}}},l),y=b=new e(o.context||r.root_name,11),v.parse(t),k&&A.length&&(o.context?o.invalid=!0:c(A)),I&&("body"==y.name||o.isRootContent)&&a(),!o.invalid){for(S in h){for(_=d[S],C=h[S],N=C.length;N--;)C[N].parent||C.splice(N,1);for(x=0,w=_.length;w>x;x++)_[x](C,S,o)}for(x=0,w=f.length;w>x;x++)if(_=f[x],_.name in p){for(C=p[_.name],N=C.length;N--;)C[N].parent||C.splice(N,1);for(N=0,E=_.callbacks.length;E>N;N++)_.callbacks[N](C,_.name,o)}}return y},r.remove_trailing_brs&&u.addNodeFilter("br",function(t){var n,r=t.length,i,o=s({},l.getBlockElements()),a=l.getNonEmptyElements(),c,u,d,f,h,p;for(o.body=1,n=0;r>n;n++)if(i=t[n],c=i.parent,o[i.parent.name]&&i===c.lastChild){for(d=i.prev;d;){if(f=d.name,"span"!==f||"bookmark"!==d.attr("data-mce-type")){if("br"!==f)break;if("br"===f){i=null;break}}d=d.prev}i&&(i.remove(),c.isEmpty(a)&&(h=l.getElementRule(c.name),h&&(h.removeEmpty?c.remove():h.paddEmpty&&(c.empty().append(new e("#text",3)).value="\xa0"))))}else{for(u=i;c&&c.firstChild===u&&c.lastChild===u&&(u=c,!o[c.name]);)c=c.parent;u===c&&(p=new e("#text",3),p.value="\xa0",i.replace(p))}}),r.allow_html_in_named_anchor||u.addAttributeFilter("id,name",function(e){for(var t=e.length,n,r,i,o;t--;)if(o=e[t],"a"===o.name&&o.firstChild&&!o.attr("href")){i=o.parent,n=o.lastChild;do r=n.prev,i.insert(n,o),n=r;while(n)}}),r.validate&&l.getValidClasses()&&u.addAttributeFilter("class",function(e){for(var t=e.length,n,r,i,o,a,s=l.getValidClasses(),c,u;t--;){for(n=e[t],r=n.attr("class").split(" "),a="",i=0;i0&&(f=r[r.length-1],f.length>0&&"\n"!==f&&r.push("\n")),r.push("<",e),t)for(c=0,u=t.length;u>c;c++)d=t[c],r.push(" ",d.name,'="',s(d.value,!0),'"');!n||l?r[r.length]=">":r[r.length]=" />",n&&i&&a[e]&&r.length>0&&(f=r[r.length-1],f.length>0&&"\n"!==f&&r.push("\n"))},end:function(e){var t;r.push(""),i&&a[e]&&r.length>0&&(t=r[r.length-1],t.length>0&&"\n"!==t&&r.push("\n"))},text:function(e,t){e.length>0&&(r[r.length]=t?e:s(e))},cdata:function(e){r.push("")},comment:function(e){r.push("")},pi:function(e,t){t?r.push(""):r.push(""),i&&r.push("\n")},doctype:function(e){r.push("",i?"\n":"")},reset:function(){r.length=0},getContent:function(){return r.join("").replace(/\n$/,"")}}}}),r(P,[M,B],function(e,t){return function(n,r){var i=this,o=new e(n);n=n||{},n.validate="validate"in n?n.validate:!0,i.schema=r=r||new t,i.writer=o,i.serialize=function(e){function t(e){var n=i[e.type],s,l,c,u,d,f,h,p,m;if(n)n(e);else{if(s=e.name,l=e.shortEnded,c=e.attributes,a&&c&&c.length>1&&(f=[],f.map={},m=r.getElementRule(e.name))){for(h=0,p=m.attributesOrder.length;p>h;h++)u=m.attributesOrder[h],u in c.map&&(d=c.map[u],f.map[u]=d,f.push({name:u,value:d}));for(h=0,p=c.length;p>h;h++)u=c[h].name,u in f.map||(d=c.map[u],f.map[u]=d,f.push({name:u,value:d}));c=f}if(o.start(e.name,c,l),!l){if(e=e.firstChild)do t(e);while(e=e.next);o.end(s)}}}var i,a;return a=n.validate,i={3:function(e){o.text(e.value,e.raw)},8:function(e){o.comment(e.value)},7:function(e){o.pi(e.name,e.value)},10:function(e){o.doctype(e.value)},4:function(e){o.cdata(e.value)},11:function(e){if(e=e.firstChild)do t(e);while(e=e.next)}},o.reset(),1!=e.type||n.inner?i[11](e):t(e),o.getContent()}}}),r(O,[w,L,D,C,P,A,B,d,m,S],function(e,t,n,r,i,o,a,s,l,c){function u(e){function t(e){return e&&"br"===e.name}var n,r;n=e.lastChild,t(n)&&(r=n.prev,t(r)&&(n.remove(),r.remove()))}var d=l.each,f=l.trim,h=e.DOM,p=["data-mce-selected"];return function(e,o){function m(e){var t=new RegExp(["]+data-mce-bogus[^>]+>[\u200b\ufeff]+<\\/span>","\\s?("+p.join("|")+')="[^"]+"'].join("|"),"gi");return e=c.trim(e.replace(t,""))}function g(){var e=o.getBody().innerHTML,t=/<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g,r,i,a,s,l,c=o.schema;for(e=m(e),l=c.getShortEndedElements();s=t.exec(e);)i=t.lastIndex,a=s[0].length,r=l[s[1]]?i:n.findEndTag(c,e,i),e=e.substring(0,i-a)+e.substring(r),t.lastIndex=i-a;return f(e)}function v(e){-1===l.inArray(p,e)&&(C.addAttributeFilter(e,function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)}),p.push(e))}var y,b,C;return o&&(y=o.dom,b=o.schema),y=y||h,b=b||new a(e),e.entity_encoding=e.entity_encoding||"named",e.remove_trailing_brs="remove_trailing_brs"in e?e.remove_trailing_brs:!0,C=new t(e,b),C.addAttributeFilter("data-mce-tabindex",function(e,t){for(var n=e.length,r;n--;)r=e[n],r.attr("tabindex",r.attributes.map["data-mce-tabindex"]),r.attr(t,null)}),C.addAttributeFilter("src,href,style",function(t,n){for(var r=t.length,i,o,a="data-mce-"+n,s=e.url_converter,l=e.url_converter_scope,c;r--;)i=t[r],o=i.attributes.map[a],o!==c?(i.attr(n,o.length>0?o:null),i.attr(a,null)):(o=i.attributes.map[n],"style"===n?o=y.serializeStyle(y.parseStyle(o),i.name):s&&(o=s.call(l,o,n,i.name)),i.attr(n,o.length>0?o:null))}),C.addAttributeFilter("class",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.attr("class"),r&&(r=n.attr("class").replace(/(?:^|\s)mce-item-\w+(?!\S)/g,""),n.attr("class",r.length>0?r:null))}),C.addAttributeFilter("data-mce-type",function(e,t,n){for(var r=e.length,i;r--;)i=e[r],"bookmark"!==i.attributes.map["data-mce-type"]||n.cleanup||i.remove()}),C.addNodeFilter("noscript",function(e){for(var t=e.length,n;t--;)n=e[t].firstChild,n&&(n.value=r.decode(n.value))}),C.addNodeFilter("script,style",function(e,t){function n(e){return e.replace(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(()?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")}for(var r=e.length,i,o,a;r--;)i=e[r],o=i.firstChild?i.firstChild.value:"","script"===t?(a=i.attr("type"),a&&i.attr("type","mce-no/type"==a?null:a.replace(/^mce\-/,"")),o.length>0&&(i.firstChild.value="// ")):o.length>0&&(i.firstChild.value="")}),C.addNodeFilter("#comment",function(e){for(var t=e.length,n;t--;)n=e[t],0===n.value.indexOf("[CDATA[")?(n.name="#cdata",n.type=4,n.value=n.value.replace(/^\[CDATA\[|\]\]$/g,"")):0===n.value.indexOf("mce:protected ")&&(n.name="#text",n.type=3,n.raw=!0,n.value=unescape(n.value).substr(14))}),C.addNodeFilter("xml:namespace,input",function(e,t){for(var n=e.length,r;n--;)r=e[n],7===r.type?r.remove():1===r.type&&("input"!==t||"type"in r.attributes.map||r.attr("type","text"))}),e.fix_list_elements&&C.addNodeFilter("ul,ol",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.parent,"ul"!==r.name&&"ol"!==r.name||n.prev&&"li"===n.prev.name&&n.prev.append(n)}),C.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style,data-mce-selected,data-mce-expando,data-mce-type,data-mce-resize",function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)}),{schema:b,addNodeFilter:C.addNodeFilter,addAttributeFilter:C.addAttributeFilter,serialize:function(t,n){var r=this,o,a,l,h,p,m;return s.ie&&y.select("script,style,select,map").length>0?(p=t.innerHTML,t=t.cloneNode(!1),y.setHTML(t,p)):t=t.cloneNode(!0),o=document.implementation,o.createHTMLDocument&&(a=o.createHTMLDocument(""),d("BODY"==t.nodeName?t.childNodes:[t],function(e){a.body.appendChild(a.importNode(e,!0))}),t="BODY"!=t.nodeName?a.body.firstChild:a.body,l=y.doc,y.doc=a),n=n||{},n.format=n.format||"html",n.selection&&(n.forced_root_block=""),n.no_events||(n.node=t,r.onPreProcess(n)),m=C.parse(f(n.getInner?t.innerHTML:y.getOuterHTML(t)),n),u(m),h=new i(e,b),n.content=h.serialize(m),n.cleanup||(n.content=c.trim(n.content),n.content=n.content.replace(/\uFEFF/g,"")),n.no_events||r.onPostProcess(n),l&&(y.doc=l),n.node=null,n.content},addRules:function(e){b.addValidElements(e)},setRules:function(e){b.setValidElements(e)},onPreProcess:function(e){o&&o.fire("PreProcess",e)},onPostProcess:function(e){o&&o.fire("PostProcess",e)},addTempAttr:v,trimHtml:m,getTrimmedContent:g}}}),r(H,[],function(){function e(e){function t(t,n){var r,i=0,o,a,s,l,c,u,d=-1,f;if(r=t.duplicate(),r.collapse(n),f=r.parentElement(),f.ownerDocument===e.dom.doc){for(;"false"===f.contentEditable;)f=f.parentNode;if(!f.hasChildNodes())return{node:f,inside:1};for(s=f.children,o=s.length-1;o>=i;)if(u=Math.floor((i+o)/2),l=s[u],r.moveToElementText(l),d=r.compareEndPoints(n?"StartToStart":"EndToEnd",t),d>0)o=u-1;else{if(!(0>d))return{node:l};i=u+1}if(0>d)for(l?r.collapse(!1):(r.moveToElementText(f),r.collapse(!0),l=f,a=!0),c=0;0!==r.compareEndPoints(n?"StartToStart":"StartToEnd",t)&&0!==r.move("character",1)&&f==r.parentElement();)c++;else for(r.collapse(!0),c=0;0!==r.compareEndPoints(n?"StartToStart":"StartToEnd",t)&&0!==r.move("character",-1)&&f==r.parentElement();)c++;return{node:l,position:d,offset:c,inside:a}}}function n(){function n(e){var n=t(o,e),r,i,s=0,l,c,u;if(r=n.node,i=n.offset,n.inside&&!r.hasChildNodes())return void a[e?"setStart":"setEnd"](r,0);if(i===c)return void a[e?"setStartBefore":"setEndAfter"](r);if(n.position<0){if(l=n.inside?r.firstChild:r.nextSibling,!l)return void a[e?"setStartAfter":"setEndAfter"](r);if(!i)return void(3==l.nodeType?a[e?"setStart":"setEnd"](l,0):a[e?"setStartBefore":"setEndBefore"](l));for(;l;){if(3==l.nodeType&&(u=l.nodeValue,s+=u.length,s>=i)){r=l,s-=i,s=u.length-s;break}l=l.nextSibling}}else{if(l=r.previousSibling,!l)return a[e?"setStartBefore":"setEndBefore"](r);if(!i)return void(3==r.nodeType?a[e?"setStart":"setEnd"](l,r.nodeValue.length):a[e?"setStartAfter":"setEndAfter"](l));for(;l;){if(3==l.nodeType&&(s+=l.nodeValue.length,s>=i)){r=l,s-=i;break}l=l.previousSibling}}a[e?"setStart":"setEnd"](r,s)}var o=e.getRng(),a=i.createRng(),s,l,c,u,d;if(s=o.item?o.item(0):o.parentElement(),s.ownerDocument!=i.doc)return a;if(l=e.isCollapsed(),o.item)return a.setStart(s.parentNode,i.nodeIndex(s)),a.setEnd(a.startContainer,a.startOffset+1),a;try{n(!0),l||n()}catch(f){if(-2147024809!=f.number)throw f;d=r.getBookmark(2),c=o.duplicate(),c.collapse(!0),s=c.parentElement(),l||(c=o.duplicate(),c.collapse(!1),u=c.parentElement(),u.innerHTML=u.innerHTML),s.innerHTML=s.innerHTML,r.moveToBookmark(d),o=e.getRng(),n(!0),l||n()}return a}var r=this,i=e.dom,o=!1;this.getBookmark=function(n){function r(e){var t,n,r,o,a=[];for(t=e.parentNode,n=i.getRoot().parentNode;t!=n&&9!==t.nodeType;){for(r=t.children,o=r.length;o--;)if(e===r[o]){a.push(o);break}e=t,t=t.parentNode}return a}function o(e){var n;return n=t(a,e),n?{position:n.position,offset:n.offset,indexes:r(n.node),inside:n.inside}:void 0}var a=e.getRng(),s={};return 2===n&&(a.item?s.start={ctrl:!0,indexes:r(a.item(0))}:(s.start=o(!0),e.isCollapsed()||(s.end=o()))),s},this.moveToBookmark=function(e){function t(e){var t,n,r,o;for(t=i.getRoot(),n=e.length-1;n>=0;n--)o=t.children,r=e[n],r<=o.length-1&&(t=o[r]);return t}function n(n){var i=e[n?"start":"end"],a,s,l,c;i&&(a=i.position>0,s=o.createTextRange(),s.moveToElementText(t(i.indexes)),c=i.offset,c!==l?(s.collapse(i.inside||a),s.moveStart("character",a?-c:c)):s.collapse(n),r.setEndPoint(n?"StartToStart":"EndToStart",s),n&&r.collapse(!0))}var r,o=i.doc.body;e.start&&(e.start.ctrl?(r=o.createControlRange(),r.addElement(t(e.start.indexes)),r.select()):(r=o.createTextRange(),n(!0),n(),r.select()))},this.addRange=function(t){function n(e){var t,n,a,d,p;a=i.create("a"),t=e?s:c,n=e?l:u,d=r.duplicate(),t!=f&&t!=f.documentElement||(t=h,n=0),3==t.nodeType?(t.parentNode.insertBefore(a,t),d.moveToElementText(a),d.moveStart("character",n),i.remove(a),r.setEndPoint(e?"StartToStart":"EndToEnd",d)):(p=t.childNodes,p.length?(n>=p.length?i.insertAfter(a,p[p.length-1]):t.insertBefore(a,p[n]),d.moveToElementText(a)):t.canHaveHTML&&(t.innerHTML="",a=t.firstChild,d.moveToElementText(a),d.collapse(o)),r.setEndPoint(e?"StartToStart":"EndToEnd",d),i.remove(a))}var r,a,s,l,c,u,d,f=e.dom.doc,h=f.body,p,m;if(s=t.startContainer,l=t.startOffset,c=t.endContainer,u=t.endOffset,r=h.createTextRange(),s==c&&1==s.nodeType){if(l==u&&!s.hasChildNodes()){if(s.canHaveHTML)return d=s.previousSibling,d&&!d.hasChildNodes()&&i.isBlock(d)?d.innerHTML="":d=null,s.innerHTML="",r.moveToElementText(s.lastChild),r.select(),i.doc.selection.clear(),s.innerHTML="",void(d&&(d.innerHTML=""));l=i.nodeIndex(s),s=s.parentNode}if(l==u-1)try{if(m=s.childNodes[l],a=h.createControlRange(),a.addElement(m),a.select(),p=e.getRng(),p.item&&m===p.item(0))return}catch(g){}}n(!0),n(),r.select()},this.getRangeAt=n}return e}),r(I,[d],function(e){return{BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,modifierPressed:function(e){return e.shiftKey||e.ctrlKey||e.altKey||this.metaKeyPressed(e)},metaKeyPressed:function(t){return e.mac?t.metaKey:t.ctrlKey&&!t.altKey}}}),r(F,[I,m,u,d,_],function(e,t,n,r,i){function o(e,t){for(;t&&t!=e;){if(s(t)||a(t))return t;t=t.parentNode}return null}var a=i.isContentEditableFalse,s=i.isContentEditableTrue;return function(i,s){function l(e){var t=s.settings.object_resizing;return t===!1||r.iOS?!1:("string"!=typeof t&&(t="table,img,div"),"false"===e.getAttribute("data-mce-resize")?!1:e==s.getBody()?!1:s.dom.is(e,t))}function c(t){var n,r,i,o,a;n=t.screenX-L,r=t.screenY-M,U=n*B[2]+H,W=r*B[3]+I,U=5>U?5:U,W=5>W?5:W,i="IMG"==k.nodeName&&s.settings.resize_img_proportional!==!1?!e.modifierPressed(t):e.modifierPressed(t)||"IMG"==k.nodeName&&B[2]*B[3]!==0,i&&(j(n)>j(r)?(W=Y(U*F),U=Y(W/F)):(U=Y(W/F),W=Y(U*F))),_.setStyles(T,{width:U,height:W}),o=B.startPos.x+n,a=B.startPos.y+r,o=o>0?o:0,a=a>0?a:0,_.setStyles(R,{left:o,top:a,display:"block"}),R.innerHTML=U+" × "+W,B[2]<0&&T.clientWidth<=U&&_.setStyle(T,"left",P+(H-U)),B[3]<0&&T.clientHeight<=W&&_.setStyle(T,"top",O+(I-W)),n=X.scrollWidth-K,r=X.scrollHeight-G,n+r!==0&&_.setStyles(R,{left:o-n,top:a-r}),z||(s.fire("ObjectResizeStart",{target:k,width:H,height:I}),z=!0)}function u(){function e(e,t){t&&(k.style[e]||!s.schema.isValid(k.nodeName.toLowerCase(),e)?_.setStyle(k,e,t):_.setAttrib(k,e,t))}z=!1,e("width",U),e("height",W),_.unbind(V,"mousemove",c),_.unbind(V,"mouseup",u),$!=V&&(_.unbind($,"mousemove",c),_.unbind($,"mouseup",u)),_.remove(T),_.remove(R),q&&"TABLE"!=k.nodeName||d(k),s.fire("ObjectResized",{target:k,width:U,height:W}),_.setAttrib(k,"style",_.getAttrib(k,"style")),s.nodeChanged()}function d(e,t,n){var i,o,a,d,h;f(),x(),i=_.getPos(e,X),P=i.x,O=i.y,h=e.getBoundingClientRect(),o=h.width||h.right-h.left,a=h.height||h.bottom-h.top,k!=e&&(C(),k=e,U=W=0),d=s.fire("ObjectSelected",{target:e}),l(e)&&!d.isDefaultPrevented()?S(A,function(e,i){function s(t){L=t.screenX,M=t.screenY,H=k.clientWidth,I=k.clientHeight,F=I/H,B=e,e.startPos={x:o*e[0]+P,y:a*e[1]+O},K=X.scrollWidth,G=X.scrollHeight,T=k.cloneNode(!0),_.addClass(T,"mce-clonedresizable"),_.setAttrib(T,"data-mce-bogus","all"),T.contentEditable=!1,T.unSelectabe=!0,_.setStyles(T,{left:P,top:O,margin:0}),T.removeAttribute("data-mce-selected"),X.appendChild(T),_.bind(V,"mousemove",c),_.bind(V,"mouseup",u),$!=V&&(_.bind($,"mousemove",c),_.bind($,"mouseup",u)),R=_.add(X,"div",{"class":"mce-resize-helper","data-mce-bogus":"all"},H+" × "+I)}var l;return t?void(i==t&&s(n)):(l=_.get("mceResizeHandle"+i),l&&_.remove(l),l=_.add(X,"div",{id:"mceResizeHandle"+i,"data-mce-bogus":"all","class":"mce-resizehandle",unselectable:!0,style:"cursor:"+i+"-resize; margin:0; padding:0"}),r.ie&&(l.contentEditable=!1),_.bind(l,"mousedown",function(e){e.stopImmediatePropagation(),e.preventDefault(),s(e)}),e.elm=l,void _.setStyles(l,{left:o*e[0]+P-l.offsetWidth/2,top:a*e[1]+O-l.offsetHeight/2}))}):f(),k.setAttribute("data-mce-selected","1")}function f(){var e,t;x(),k&&k.removeAttribute("data-mce-selected");for(e in A)t=_.get("mceResizeHandle"+e),t&&(_.unbind(t),_.remove(t))}function h(e){function t(e,t){if(e)do if(e===t)return!0;while(e=e.parentNode)}var n,r;if(!z&&!s.removed)return S(_.select("img[data-mce-selected],hr[data-mce-selected]"),function(e){e.removeAttribute("data-mce-selected")}),r="mousedown"==e.type?e.target:i.getNode(),r=_.$(r).closest(q?"table":"table,img,hr")[0],t(r,X)&&(w(),n=i.getStart(!0),t(n,r)&&t(i.getEnd(!0),r)&&(!q||r!=n&&"IMG"!==n.nodeName))?void d(r):void f()}function p(e,t,n){e&&e.attachEvent&&e.attachEvent("on"+t,n)}function m(e,t,n){e&&e.detachEvent&&e.detachEvent("on"+t,n)}function g(e){var t=e.srcElement,n,r,i,o,a,l,c;n=t.getBoundingClientRect(),l=D.clientX-n.left,c=D.clientY-n.top;for(r in A)if(i=A[r],o=t.offsetWidth*i[0],a=t.offsetHeight*i[1],j(o-l)<8&&j(a-c)<8){B=i;break}z=!0,s.fire("ObjectResizeStart",{target:k,width:k.clientWidth,height:k.clientHeight}),s.getDoc().selection.empty(),d(t,r,D)}function v(e){e.preventDefault?e.preventDefault():e.returnValue=!1}function y(e){return a(o(s.getBody(),e))}function b(e){var t=e.srcElement;if(y(t))return void v(e);if(t!=k){if(s.fire("ObjectSelected",{target:t}),C(),0===t.id.indexOf("mceResizeHandle"))return void(e.returnValue=!1);"IMG"!=t.nodeName&&"TABLE"!=t.nodeName||(f(),k=t,p(t,"resizestart",g))}}function C(){m(k,"resizestart",g)}function x(){for(var e in A){var t=A[e];t.elm&&(_.unbind(t.elm),delete t.elm)}}function w(){try{s.getDoc().execCommand("enableObjectResizing",!1,!1)}catch(e){}}function N(e){var t;if(q){t=V.body.createControlRange();try{return t.addElement(e),t.select(),!0}catch(n){}}}function E(){k=T=null,q&&(C(),m(X,"controlselect",b))}var _=s.dom,S=t.each,k,T,R,A,B,D,L,M,P,O,H,I,F,z,U,W,V=s.getDoc(),$=document,q=r.ie&&r.ie<11,j=Math.abs,Y=Math.round,X=s.getBody(),K,G;A={nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};var J=".mce-content-body";return s.contentStyles.push(J+" div.mce-resizehandle {position: absolute;border: 1px solid black;box-sizing: box-sizing;background: #FFF;width: 7px;height: 7px;z-index: 10000}"+J+" .mce-resizehandle:hover {background: #000}"+J+" img[data-mce-selected],"+J+" hr[data-mce-selected] {outline: 1px solid black;resize: none}"+J+" .mce-clonedresizable {position: absolute;"+(r.gecko?"":"outline: 1px dashed black;")+"opacity: .5;filter: alpha(opacity=50);z-index: 10000}"+J+" .mce-resize-helper {background: #555;background: rgba(0,0,0,0.75);border-radius: 3px;border: 1px;color: white;display: none;font-family: sans-serif;font-size: 12px;white-space: nowrap;line-height: 14px;margin: 5px 10px;padding: 5px;position: absolute;z-index: 10001}"),s.on("init",function(){q?(s.on("ObjectResized",function(e){"TABLE"!=e.target.nodeName&&(f(),N(e.target))}),p(X,"controlselect",b),s.on("mousedown",function(e){D=e})):(w(),r.ie>=11&&(s.on("mousedown click",function(e){var t=e.target,n=t.nodeName;z||!/^(TABLE|IMG|HR)$/.test(n)||y(t)||(s.selection.select(t,"TABLE"==n),"mousedown"==e.type&&s.nodeChanged())}),s.dom.bind(X,"mscontrolselect",function(e){function t(e){n.setEditorTimeout(s,function(){s.selection.select(e)})}return y(e.target)?(e.preventDefault(),void t(e.target)):void(/^(TABLE|IMG|HR)$/.test(e.target.nodeName)&&(e.preventDefault(),"IMG"==e.target.tagName&&t(e.target)))})));var e=n.throttle(function(e){s.composing||h(e)});s.on("nodechange ResizeEditor ResizeWindow drop",e),s.on("keyup compositionend",function(t){k&&"TABLE"==k.nodeName&&e(t)}),s.on("hide blur",f)}),s.on("remove",x),{isResizable:l,showResizeRect:d,hideResizeRect:f,updateResizeRect:h,controlSelect:N,destroy:E}}}),r(z,[],function(){function e(e){return function(){return e}}function t(e){return function(t){return!e(t)}}function n(e,t){return function(n){return e(t(n))}}function r(){var e=a.call(arguments);return function(t){for(var n=0;n=e.length?e.apply(this,t.slice(1)):function(){var e=t.concat([].slice.call(arguments));return o.apply(this,e)}}var a=[].slice;return{constant:e,negate:t,and:i,or:r,curry:o,compose:n}}),r(U,[_,p,k],function(e,t,n){function r(e){return m(e)?!1:d(e)?!f(e.parentNode):h(e)||u(e)||p(e)||c(e)}function i(e,t){for(e=e.parentNode;e&&e!=t;e=e.parentNode){if(c(e))return!1;if(l(e))return!0}return!0}function o(e){return c(e)?t.reduce(e.getElementsByTagName("*"),function(e,t){return e||l(t)},!1)!==!0:!1}function a(e){return h(e)||o(e)}function s(e,t){return r(e)&&i(e,t)}var l=e.isContentEditableTrue,c=e.isContentEditableFalse,u=e.isBr,d=e.isText,f=e.matchNodeNames("script style textarea"),h=e.matchNodeNames("img input textarea hr iframe video audio object"),p=e.matchNodeNames("table"),m=n.isCaretContainer;return{isCaretCandidate:r,isInEditable:i,isAtomic:a,isEditableCaretCandidate:s}}),r(W,[],function(){function e(e){return e?{left:u(e.left),top:u(e.top),bottom:u(e.bottom),right:u(e.right),width:u(e.width),height:u(e.height)}:{left:0,top:0,bottom:0,right:0,width:0,height:0}}function t(t,n){return t=e(t),n?t.right=t.left:(t.left=t.left+t.width,t.right=t.left),t.width=0,t}function n(e,t){return e.left===t.left&&e.top===t.top&&e.bottom===t.bottom&&e.right===t.right}function r(e,t,n){return e>=0&&e<=Math.min(t.height,n.height)/2}function i(e,t){return e.bottomt.bottom?!1:r(t.top-e.bottom,e,t)}function o(e,t){return e.top>t.bottom?!0:e.bottomt.right}function l(e,t){return i(e,t)?-1:o(e,t)?1:a(e,t)?-1:s(e,t)?1:0}function c(e,t,n){return t>=e.left&&t<=e.right&&n>=e.top&&n<=e.bottom}var u=Math.round;return{clone:e,collapse:t,isEqual:n,isAbove:i,isBelow:o,isLeft:a,isRight:s,compare:l,containsXY:c}}),r(V,[],function(){function e(e){return"string"==typeof e&&e.charCodeAt(0)>=768&&t.test(e)}var t=new RegExp("[\u0300-\u036f\u0483-\u0487\u0488-\u0489\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7-\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e3-\u0902\u093a\u093c\u0941-\u0948\u094d\u0951-\u0957\u0962-\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2-\u09e3\u0a01-\u0a02\u0a3c\u0a41-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51\u0a70-\u0a71\u0a75\u0a81-\u0a82\u0abc\u0ac1-\u0ac5\u0ac7-\u0ac8\u0acd\u0ae2-\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62-\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c00\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56\u0c62-\u0c63\u0c81\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc-\u0ccd\u0cd5-\u0cd6\u0ce2-\u0ce3\u0d01\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62-\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb-\u0ebc\u0ec8-\u0ecd\u0f18-\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039-\u103a\u103d-\u103e\u1058-\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108d\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17b4-\u17b5\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193b\u1a17-\u1a18\u1a1b\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1ab0-\u1abd\u1abe\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80-\u1b81\u1ba2-\u1ba5\u1ba8-\u1ba9\u1bab-\u1bad\u1be6\u1be8-\u1be9\u1bed\u1bef-\u1bf1\u1c2c-\u1c33\u1c36-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1cf4\u1cf8-\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u200c-\u200d\u20d0-\u20dc\u20dd-\u20e0\u20e1\u20e2-\u20e4\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302d\u302e-\u302f\u3099-\u309a\ua66f\ua670-\ua672\ua674-\ua67d\ua69e-\ua69f\ua6f0-\ua6f1\ua802\ua806\ua80b\ua825-\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\ua9e5\uaa29-\uaa2e\uaa31-\uaa32\uaa35-\uaa36\uaa43\uaa4c\uaa7c\uaab0\uaab2-\uaab4\uaab7-\uaab8\uaabe-\uaabf\uaac1\uaaec-\uaaed\uaaf6\uabe5\uabe8\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\uff9e-\uff9f]");return{isExtendingChar:e}}),r($,[z,_,w,T,U,W,V],function(e,t,n,r,i,o,a){function s(e){return"createRange"in e?e.createRange():n.DOM.createRng()}function l(e){return e&&/[\r\n\t ]/.test(e)}function c(e){var t=e.startContainer,n=e.startOffset,r;return!!(l(e.toString())&&v(t.parentNode)&&(r=t.data,l(r[n-1])||l(r[n+1])))}function u(e){function t(e){var t=e.ownerDocument,n=s(t),r=t.createTextNode("\xa0"),i=e.parentNode,a;return i.insertBefore(r,e),n.setStart(r,0),n.setEnd(r,1),a=o.clone(n.getBoundingClientRect()),i.removeChild(r),a}function n(e){var n,r;return r=e.getClientRects(),n=r.length>0?o.clone(r[0]):o.clone(e.getBoundingClientRect()),b(e)&&0===n.left?t(e):n}function r(e,t){return e=o.collapse(e,t),e.width=1,e.right=e.left+1,e}function i(e){0!==e.height&&(u.length>0&&o.isEqual(e,u[u.length-1])||u.push(e))}function l(e,t){var o=s(e.ownerDocument);if(t0&&(o.setStart(e,t-1),o.setEnd(e,t),c(o)||i(r(n(o),!1))),t=t.data.length:n>=t.childNodes.length}function a(){var e;return e=s(t.ownerDocument),e.setStart(t,n),e.setEnd(t,n),e}function l(){return r||(r=u(new d(t,n))),r}function c(){return l().length>0}function f(e){return e&&t===e.container()&&n===e.offset()}function h(e){return x(t,e?n-1:n)}return{container:e.constant(t),offset:e.constant(n),toRange:a,getClientRects:l,isVisible:c,isAtStart:i,isAtEnd:o,isEqual:f,getNode:h}}var f=t.isElement,h=i.isCaretCandidate,p=t.matchStyleValues("display","block table"),m=t.matchStyleValues("float","left right"),g=e.and(f,h,e.negate(m)),v=e.negate(t.matchStyleValues("white-space","pre pre-line pre-wrap")),y=t.isText,b=t.isBr,C=n.nodeIndex,x=r.getNode;return d.fromRangeStart=function(e){return new d(e.startContainer,e.startOffset)},d.fromRangeEnd=function(e){return new d(e.endContainer,e.endOffset)},d.after=function(e){return new d(e.parentNode,C(e)+1)},d.before=function(e){return new d(e.parentNode,C(e))},d}),r(q,[_,w,z,p,$],function(e,t,n,r,i){function o(e){var t=e.parentNode;return v(t)?o(t):t}function a(e){return e?r.reduce(e.childNodes,function(e,t){return v(t)&&"BR"!=t.nodeName?e=e.concat(a(t)):e.push(t),e},[]):[]}function s(e,t){for(;(e=e.previousSibling)&&g(e);)t+=e.data.length;return t}function l(e){return function(t){return e===t}}function c(t){var n,i,s;return n=a(o(t)),i=r.findIndex(n,l(t),t),n=n.slice(0,i+1),s=r.reduce(n,function(e,t,r){return g(t)&&g(n[r-1])&&e++,e},0),n=r.filter(n,e.matchNodeNames(t.nodeName)),i=r.findIndex(n,l(t),t),i-s}function u(e){var t;return t=g(e)?"text()":e.nodeName.toLowerCase(),t+"["+c(e)+"]"}function d(e,t,n){var r=[];for(t=t.parentNode;t!=e&&(!n||!n(t));t=t.parentNode)r.push(t);return r}function f(t,i){var o,a,l=[],c,f,h;return o=i.container(),a=i.offset(),g(o)?c=s(o,a):(f=o.childNodes,a>=f.length?(c="after",a=f.length-1):c="before",o=f[a]),l.push(u(o)),h=d(t,o),h=r.filter(h,n.negate(e.isBogus)),l=l.concat(r.map(h,function(e){return u(e)})),l.reverse().join("/")+","+c}function h(t,n,i){var o=a(t);return o=r.filter(o,function(e,t){return!g(e)||!g(o[t-1])}),o=r.filter(o,e.matchNodeNames(n)),o[i]}function p(e,t){for(var n=e,r=0,o;g(n);){if(o=n.data.length,t>=r&&r+o>=t){e=n,t-=r;break}if(!g(n.nextSibling)){e=n,t=o;break}r+=o,n=n.nextSibling}return t>e.data.length&&(t=e.data.length),new i(e,t)}function m(e,t){var n,o,a;return t?(n=t.split(","),t=n[0].split("/"),a=n.length>1?n[1]:"before",o=r.reduce(t,function(e,t){return(t=/([\w\-\(\)]+)\[([0-9]+)\]/.exec(t))?("text()"===t[1]&&(t[1]="#text"),h(e,t[1],parseInt(t[2],10))):null},e),o?g(o)?p(o,parseInt(a,10)):(a="after"===a?y(o)+1:y(o),new i(o.parentNode,a)):null):null}var g=e.isText,v=e.isBogus,y=t.nodeIndex;return{create:f,resolve:m}}),r(j,[d,m,k,q,$,_],function(e,t,n,r,i,o){function a(a){var l=a.dom;this.getBookmark=function(e,c){function u(e,n){var r=0;return t.each(l.select(e),function(e){return"all"!==e.getAttribute("data-mce-bogus")?e==n?!1:void r++:void 0}),r}function d(e){function t(t){var n,r,i,o=t?"start":"end";n=e[o+"Container"],r=e[o+"Offset"],1==n.nodeType&&"TR"==n.nodeName&&(i=n.childNodes,n=i[Math.min(t?r:r-1,i.length-1)],n&&(r=t?0:n.childNodes.length,e["set"+(t?"Start":"End")](n,r)))}return t(!0),t(),e}function f(e){function t(e,t){var r=e[t?"startContainer":"endContainer"],i=e[t?"startOffset":"endOffset"],o=[],a,s,u=0;if(3==r.nodeType){if(c)for(a=r.previousSibling;a&&3==a.nodeType;a=a.previousSibling)i+=a.nodeValue.length;o.push(i)}else s=r.childNodes,i>=s.length&&s.length&&(u=1,i=Math.max(0,s.length-1)),o.push(l.nodeIndex(s[i],c)+u);for(;r&&r!=n;r=r.parentNode)o.push(l.nodeIndex(r,c));return o}var n=l.getRoot(),r={};return r.start=t(e,!0),a.isCollapsed()||(r.end=t(e)),r}function h(e){function t(e){var t;if(n.isCaretContainer(e)){if(o.isText(e)&&n.isCaretContainerBlock(e)&&(e=e.parentNode),t=e.previousSibling,s(t))return t;if(t=e.nextSibling,s(t))return t}}return t(e.startContainer)||t(e.endContainer)}var p,m,g,v,y,b,C="",x;if(2==e)return b=a.getNode(),y=b?b.nodeName:null,p=a.getRng(),s(b)||"IMG"==y?{name:y,index:u(y,b)}:a.tridentSel?a.tridentSel.getBookmark(e):(b=h(p),b?(y=b.tagName,{name:y,index:u(y,b)}):f(p));if(3==e)return p=a.getRng(),{start:r.create(l.getRoot(),i.fromRangeStart(p)),end:r.create(l.getRoot(),i.fromRangeEnd(p))};if(e)return{rng:a.getRng()};if(p=a.getRng(),g=l.uniqueId(),v=a.isCollapsed(),x="overflow:hidden;line-height:0px",p.duplicate||p.item){if(p.item)return b=p.item(0),y=b.nodeName,{name:y,index:u(y,b)};m=p.duplicate();try{p.collapse(),p.pasteHTML(''+C+""),v||(m.collapse(!1),p.moveToElementText(m.parentElement()),0===p.compareEndPoints("StartToEnd",m)&&m.move("character",-1),m.pasteHTML(''+C+""))}catch(w){return null}}else{if(b=a.getNode(),y=b.nodeName,"IMG"==y)return{name:y,index:u(y,b)};m=d(p.cloneRange()),v||(m.collapse(!1),m.insertNode(l.create("span",{"data-mce-type":"bookmark",id:g+"_end",style:x},C))),p=d(p),p.collapse(!0),p.insertNode(l.create("span",{"data-mce-type":"bookmark",id:g+"_start",style:x},C))}return a.moveToBookmark({id:g,keep:1}),{id:g}},this.moveToBookmark=function(n){function i(e){var t=n[e?"start":"end"],r,i,o,a;if(t){for(o=t[0],i=d,r=t.length-1;r>=1;r--){if(a=i.childNodes,t[r]>a.length-1)return;i=a[t[r]]}3===i.nodeType&&(o=Math.min(t[0],i.nodeValue.length)),1===i.nodeType&&(o=Math.min(t[0],i.childNodes.length)),e?u.setStart(i,o):u.setEnd(i,o)}return!0}function o(r){var i=l.get(n.id+"_"+r),o,a,s,c,u=n.keep;if(i&&(o=i.parentNode,"start"==r?(u?(o=i.firstChild,a=1):a=l.nodeIndex(i),f=h=o,p=m=a):(u?(o=i.firstChild,a=1):a=l.nodeIndex(i),h=o,m=a),!u)){for(c=i.previousSibling,s=i.nextSibling,t.each(t.grep(i.childNodes),function(e){3==e.nodeType&&(e.nodeValue=e.nodeValue.replace(/\uFEFF/g,""))});i=l.get(n.id+"_"+r);)l.remove(i,1);c&&s&&c.nodeType==s.nodeType&&3==c.nodeType&&!e.opera&&(a=c.nodeValue.length,c.appendData(s.nodeValue),l.remove(s),"start"==r?(f=h=c,p=m=a):(h=c,m=a))}}function s(t){return!l.isBlock(t)||t.innerHTML||e.ie||(t.innerHTML='
    '),t}function c(){var e,t;return e=l.createRng(),t=r.resolve(l.getRoot(),n.start),e.setStart(t.container(),t.offset()),t=r.resolve(l.getRoot(),n.end),e.setEnd(t.container(),t.offset()),e}var u,d,f,h,p,m;if(n)if(t.isArray(n.start)){if(u=l.createRng(),d=l.getRoot(),a.tridentSel)return a.tridentSel.moveToBookmark(n);i(!0)&&i()&&a.setRng(u)}else"string"==typeof n.start?a.setRng(c(n)):n.id?(o("start"),o("end"),f&&(u=l.createRng(),u.setStart(s(f),p),u.setEnd(s(h),m),a.setRng(u))):n.name?a.select(l.select(n.name)[n.index]):n.rng&&a.setRng(n.rng)}}var s=o.isContentEditableFalse;return a.isBookmarkNode=function(e){return e&&"SPAN"===e.tagName&&"bookmark"===e.getAttribute("data-mce-type")},a}),r(Y,[y,H,F,T,j,_,d,m,$],function(e,n,r,i,o,a,s,l,c){function u(e,t,i,a){var s=this;s.dom=e,s.win=t,s.serializer=i,s.editor=a,s.bookmarkManager=new o(s),s.controlSelection=new r(s,a),s.win.getSelection||(s.tridentSel=new n(s))}var d=l.each,f=l.trim,h=s.ie;return u.prototype={setCursorLocation:function(e,t){var n=this,r=n.dom.createRng();e?(r.setStart(e,t),r.setEnd(e,t),n.setRng(r),n.collapse(!1)):(n._moveEndPoint(r,n.editor.getBody(),!0),n.setRng(r))},getContent:function(e){var n=this,r=n.getRng(),i=n.dom.create("body"),o=n.getSel(),a,s,l;return e=e||{},a=s="",e.get=!0,e.format=e.format||"html",e.selection=!0,n.editor.fire("BeforeGetContent",e),"text"==e.format?n.isCollapsed()?"":r.text||(o.toString?o.toString():""):(r.cloneContents?(l=r.cloneContents(),l&&i.appendChild(l)):r.item!==t||r.htmlText!==t?(i.innerHTML="
    "+(r.item?r.item(0).outerHTML:r.htmlText),i.removeChild(i.firstChild)):i.innerHTML=r.toString(),/^\s/.test(i.innerHTML)&&(a=" "),/\s+$/.test(i.innerHTML)&&(s=" "),e.getInner=!0,e.content=n.isCollapsed()?"":a+n.serializer.serialize(i,e)+s,n.editor.fire("GetContent",e),e.content)},setContent:function(e,t){var n=this,r=n.getRng(),i,o=n.win.document,a,s;if(t=t||{format:"html"},t.set=!0,t.selection=!0,t.content=e,t.no_events||n.editor.fire("BeforeSetContent",t),e=t.content,r.insertNode){e+='_',r.startContainer==o&&r.endContainer==o?o.body.innerHTML=e:(r.deleteContents(),0===o.body.childNodes.length?o.body.innerHTML=e:r.createContextualFragment?r.insertNode(r.createContextualFragment(e)):(a=o.createDocumentFragment(),s=o.createElement("div"),a.appendChild(s),s.outerHTML=e,r.insertNode(a))),i=n.dom.get("__caret"),r=o.createRange(),r.setStartBefore(i),r.setEndBefore(i),n.setRng(r),n.dom.remove("__caret");try{n.setRng(r)}catch(l){}}else r.item&&(o.execCommand("Delete",!1,null),r=n.getRng()),/^\s+/.test(e)?(r.pasteHTML('_'+e),n.dom.remove("__mce_tmp")):r.pasteHTML(e);t.no_events||n.editor.fire("SetContent",t)},getStart:function(e){var t=this,n=t.getRng(),r,i,o,a;if(n.duplicate||n.item){if(n.item)return n.item(0);for(o=n.duplicate(),o.collapse(1),r=o.parentElement(),r.ownerDocument!==t.dom.doc&&(r=t.dom.getRoot()),i=a=n.parentElement();a=a.parentNode;)if(a==r){r=i;break}return r}return r=n.startContainer,1==r.nodeType&&r.hasChildNodes()&&(e&&n.collapsed||(r=r.childNodes[Math.min(r.childNodes.length-1,n.startOffset)])),r&&3==r.nodeType?r.parentNode:r},getEnd:function(e){var t=this,n=t.getRng(),r,i;return n.duplicate||n.item?n.item?n.item(0):(n=n.duplicate(),n.collapse(0),r=n.parentElement(),r.ownerDocument!==t.dom.doc&&(r=t.dom.getRoot()),r&&"BODY"==r.nodeName?r.lastChild||r:r):(r=n.endContainer,i=n.endOffset,1==r.nodeType&&r.hasChildNodes()&&(e&&n.collapsed||(r=r.childNodes[i>0?i-1:i])),r&&3==r.nodeType?r.parentNode:r)},getBookmark:function(e,t){return this.bookmarkManager.getBookmark(e,t)},moveToBookmark:function(e){return this.bookmarkManager.moveToBookmark(e)},select:function(e,t){var n=this,r=n.dom,i=r.createRng(),o;if(n.lastFocusBookmark=null,e){if(!t&&n.controlSelection.controlSelect(e))return;o=r.nodeIndex(e),i.setStart(e.parentNode,o),i.setEnd(e.parentNode,o+1),t&&(n._moveEndPoint(i,e,!0),n._moveEndPoint(i,e)),n.setRng(i)}return e},isCollapsed:function(){var e=this,t=e.getRng(),n=e.getSel();return!t||t.item?!1:t.compareEndPoints?0===t.compareEndPoints("StartToEnd",t):!n||t.collapsed},collapse:function(e){var t=this,n=t.getRng(),r;n.item&&(r=n.item(0),n=t.win.document.body.createTextRange(),n.moveToElementText(r)),n.collapse(!!e),t.setRng(n)},getSel:function(){var e=this.win;return e.getSelection?e.getSelection():e.document.selection},getRng:function(e){function t(e,t,n){try{return t.compareBoundaryPoints(e,n)}catch(r){return-1}}var n=this,r,i,o,a,s,l;if(!n.win)return null;if(a=n.win.document,!e&&n.lastFocusBookmark){var c=n.lastFocusBookmark;return c.startContainer?(i=a.createRange(),i.setStart(c.startContainer,c.startOffset),i.setEnd(c.endContainer,c.endOffset)):i=c,i}if(e&&n.tridentSel)return n.tridentSel.getRangeAt(0);try{(r=n.getSel())&&(i=r.rangeCount>0?r.getRangeAt(0):r.createRange?r.createRange():a.createRange())}catch(u){}if(l=n.editor.fire("GetSelectionRange",{range:i}),l.range!==i)return l.range;if(h&&i&&i.setStart&&a.selection){try{s=a.selection.createRange()}catch(u){}s&&s.item&&(o=s.item(0),i=a.createRange(),i.setStartBefore(o),i.setEndAfter(o))}return i||(i=a.createRange?a.createRange():a.body.createTextRange()),i.setStart&&9===i.startContainer.nodeType&&i.collapsed&&(o=n.dom.getRoot(),i.setStart(o,0),i.setEnd(o,0)),n.selectedRange&&n.explicitRange&&(0===t(i.START_TO_START,i,n.selectedRange)&&0===t(i.END_TO_END,i,n.selectedRange)?i=n.explicitRange:(n.selectedRange=null,n.explicitRange=null)),i},setRng:function(e,t){var n=this,r,i,o;if(e)if(e.select){n.explicitRange=null;try{e.select()}catch(a){}}else if(n.tridentSel){if(e.cloneRange)try{n.tridentSel.addRange(e)}catch(a){}}else{if(r=n.getSel(),o=n.editor.fire("SetSelectionRange",{range:e}),e=o.range,r){n.explicitRange=e;try{r.removeAllRanges(),r.addRange(e)}catch(a){}t===!1&&r.extend&&(r.collapse(e.endContainer,e.endOffset),r.extend(e.startContainer,e.startOffset)),n.selectedRange=r.rangeCount>0?r.getRangeAt(0):null}e.collapsed||e.startContainer!=e.endContainer||!r.setBaseAndExtent||s.ie||e.endOffset-e.startOffset<2&&e.startContainer.hasChildNodes()&&(i=e.startContainer.childNodes[e.startOffset],i&&"IMG"==i.tagName&&n.getSel().setBaseAndExtent(i,0,i,1))}},setNode:function(e){var t=this;return t.setContent(t.dom.getOuterHTML(e)),e},getNode:function(){function e(e,t){for(var n=e;e&&3===e.nodeType&&0===e.length;)e=t?e.nextSibling:e.previousSibling;return e||n}var t=this,n=t.getRng(),r,i,o,a,s,l=t.dom.getRoot();return n?(i=n.startContainer,o=n.endContainer,a=n.startOffset,s=n.endOffset,n.setStart?(r=n.commonAncestorContainer,!n.collapsed&&(i==o&&2>s-a&&i.hasChildNodes()&&(r=i.childNodes[a]),3===i.nodeType&&3===o.nodeType&&(i=i.length===a?e(i.nextSibling,!0):i.parentNode,o=0===s?e(o.previousSibling,!1):o.parentNode,i&&i===o))?i:r&&3==r.nodeType?r.parentNode:r):(r=n.item?n.item(0):n.parentElement(),r.ownerDocument!==t.win.document&&(r=l),r)):l},getSelectedBlocks:function(t,n){var r=this,i=r.dom,o,a,s=[];if(a=i.getRoot(),t=i.getParent(t||r.getStart(),i.isBlock),n=i.getParent(n||r.getEnd(),i.isBlock),t&&t!=a&&s.push(t),t&&n&&t!=n){o=t;for(var l=new e(t,a);(o=l.next())&&o!=n;)i.isBlock(o)&&s.push(o)}return n&&t!=n&&n!=a&&s.push(n),s},isForward:function(){var e=this.dom,t=this.getSel(),n,r;return t&&t.anchorNode&&t.focusNode?(n=e.createRng(),n.setStart(t.anchorNode,t.anchorOffset),n.collapse(!0),r=e.createRng(),r.setStart(t.focusNode,t.focusOffset),r.collapse(!0),n.compareBoundaryPoints(n.START_TO_START,r)<=0):!0},normalize:function(){var e=this,t=e.getRng();return s.range&&new i(e.dom).normalize(t)&&e.setRng(t,e.isForward()),t},selectorChanged:function(e,t){var n=this,r;return n.selectorChangedData||(n.selectorChangedData={},r={},n.editor.on("NodeChange",function(e){var t=e.element,i=n.dom,o=i.getParents(t,null,i.getRoot()),a={};d(n.selectorChangedData,function(e,t){d(o,function(n){return i.is(n,t)?(r[t]||(d(e,function(e){e(!0,{node:n,selector:t,parents:o})}),r[t]=e),a[t]=e,!1):void 0})}),d(r,function(e,n){a[n]||(delete r[n],d(e,function(e){e(!1,{node:t,selector:n,parents:o})}))})})),n.selectorChangedData[e]||(n.selectorChangedData[e]=[]),n.selectorChangedData[e].push(t),n},getScrollContainer:function(){for(var e,t=this.dom.getRoot();t&&"BODY"!=t.nodeName;){if(t.scrollHeight>t.clientHeight){e=t;break}t=t.parentNode}return e},scrollIntoView:function(e,t){function n(e){for(var t=0,n=0,r=e;r&&r.nodeType;)t+=r.offsetLeft||0,n+=r.offsetTop||0,r=r.offsetParent;return{x:t,y:n}}var r,i,o=this,s=o.dom,l=s.getRoot(),c,u,d=0;if(a.isElement(e)){if(t===!1&&(d=e.offsetHeight),"BODY"!=l.nodeName){var f=o.getScrollContainer();if(f)return r=n(e).y-n(f).y+d,u=f.clientHeight,c=f.scrollTop,void((c>r||r+25>c+u)&&(f.scrollTop=c>r?r:r-u+25))}i=s.getViewPort(o.editor.getWin()),r=s.getPos(e).y+d,c=i.y,u=i.h,(rc+u)&&o.editor.getWin().scrollTo(0,c>r?r:r-u+25)}},placeCaretAt:function(e,t){this.setRng(i.getCaretRangeFromPoint(e,t,this.editor.getDoc()))},_moveEndPoint:function(t,n,r){var i=n,o=new e(n,i),a=this.dom.schema.getNonEmptyElements();do{if(3==n.nodeType&&0!==f(n.nodeValue).length)return void(r?t.setStart(n,0):t.setEnd(n,n.nodeValue.length));if(a[n.nodeName]&&!/^(TD|TH)$/.test(n.nodeName))return void(r?t.setStartBefore(n):"BR"==n.nodeName?t.setEndBefore(n):t.setEndAfter(n));if(s.ie&&s.ie<11&&this.dom.isBlock(n)&&this.dom.isEmpty(n))return void(r?t.setStart(n,0):t.setEnd(n,0))}while(n=r?o.next():o.prev());"BODY"==i.nodeName&&(r?t.setStart(i,0):t.setEnd(i,i.childNodes.length))},getBoundingClientRect:function(){var e=this.getRng();return e.collapsed?c.fromRangeStart(e).getClientRects()[0]:e.getBoundingClientRect()},destroy:function(){this.win=null,this.controlSelection.destroy()}},u}),r(X,[j,m],function(e,t){function n(t){this.compare=function(n,i){function o(e){var n={};return r(t.getAttribs(e),function(r){var i=r.nodeName.toLowerCase();0!==i.indexOf("_")&&"style"!==i&&0!==i.indexOf("data-")&&(n[i]=t.getAttrib(e,i))}),n}function a(e,t){var n,r;for(r in e)if(e.hasOwnProperty(r)){if(n=t[r],"undefined"==typeof n)return!1;if(e[r]!=n)return!1;delete t[r]}for(r in t)if(t.hasOwnProperty(r))return!1;return!0}return n.nodeName!=i.nodeName?!1:a(o(n),o(i))&&a(t.parseStyle(t.getAttrib(n,"style")),t.parseStyle(t.getAttrib(i,"style")))?!e.isBookmarkNode(n)&&!e.isBookmarkNode(i):!1}}var r=t.each;return n}),r(K,[m],function(e){function t(e,t){function r(e){return e.replace(/%(\w+)/g,"")}var i,o,a=e.dom,s="",l,c;if(c=e.settings.preview_styles,c===!1)return"";if(c||(c="font-family font-size font-weight font-style text-decoration text-transform color background-color border border-radius outline text-shadow"),"string"==typeof t){if(t=e.formatter.get(t),!t)return;t=t[0]}return i=t.block||t.inline||"span",o=a.create(i),n(t.styles,function(e,t){e=r(e),e&&a.setStyle(o,t,e)}),n(t.attributes,function(e,t){e=r(e),e&&a.setAttrib(o,t,e)}),n(t.classes,function(e){e=r(e),a.hasClass(o,e)||a.addClass(o,e)}),e.fire("PreviewFormats"),a.setStyles(o,{position:"absolute",left:-65535}),e.getBody().appendChild(o),l=a.getStyle(e.getBody(),"fontSize",!0),l=/px$/.test(l)?parseInt(l,10):0,n(c.split(" "),function(t){var n=a.getStyle(o,t,!0);if(!("background-color"==t&&/transparent|rgba\s*\([^)]+,\s*0\)/.test(n)&&(n=a.getStyle(e.getBody(),t,!0),"#ffffff"==a.toHex(n).toLowerCase())||"color"==t&&"#000000"==a.toHex(n).toLowerCase())){if("font-size"==t&&/em|%$/.test(n)){if(0===l)return;n=parseFloat(n,10)/(/%$/.test(n)?100:1),n=n*l+"px"}"border"==t&&n&&(s+="padding:0 2px;"),s+=t+":"+n+";"}}),e.fire("AfterPreviewFormats"),a.remove(o),s}var n=e.each;return{getCssText:t}}),r(G,[p,_,g],function(e,t,n){function r(e,t){var n=o[e];n||(o[e]=n=[]),o[e].push(t)}function i(e,t){s(o[e],function(e){e(t)})}var o=[],a=e.filter,s=e.each;return r("pre",function(r){function i(t){return c(t.previousSibling)&&-1!=e.indexOf(u,t.previousSibling)}function o(e,t){n(t).remove(),n(e).append("

    ").append(t.childNodes)}var l=r.selection.getRng(),c,u;c=t.matchNodeNames("pre"),l.collapsed||(u=r.selection.getSelectedBlocks(),s(a(a(u,c),i),function(e){o(e.previousSibling,e)}))}),{postProcess:i}}),r(J,[y,T,j,X,m,K,G],function(e,t,n,r,i,o,a){return function(s){function l(e){return e.nodeType&&(e=e.nodeName),!!s.schema.getTextBlockElements()[e.toLowerCase()]}function c(e){return/^(TH|TD)$/.test(e.nodeName)}function u(e){return e&&/^(IMG)$/.test(e.nodeName)}function d(e,t){return Y.getParents(e,t,Y.getRoot())}function f(e){return 1===e.nodeType&&"_mce_caret"===e.id}function h(){g({valigntop:[{selector:"td,th",styles:{verticalAlign:"top"}}],valignmiddle:[{selector:"td,th",styles:{verticalAlign:"middle"}}],valignbottom:[{selector:"td,th",styles:{verticalAlign:"bottom"}}],alignleft:[{selector:"figure.image",collapsed:!1,classes:"align-left",ceFalseOverride:!0},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"left"},inherit:!1,defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"left"}}],aligncenter:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"center"},inherit:!1,defaultBlock:"div"},{selector:"figure.image",collapsed:!1,classes:"align-center",ceFalseOverride:!0},{selector:"img",collapsed:!1,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:!1,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"figure.image",collapsed:!1,classes:"align-right",ceFalseOverride:!0},{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"right"},inherit:!1,defaultBlock:"div"},{selector:"img,table",collapsed:!1,styles:{"float":"right"}}],alignjustify:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,tr,div,ul,ol,li",styles:{textAlign:"justify"},inherit:!1,defaultBlock:"div"}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:!0},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:!0},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},links:!0,remove_similar:!0},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},links:!0,remove_similar:!0},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},code:{inline:"code"},link:{inline:"a",selector:"a",remove:"all",split:!0,deep:!0,onmatch:function(){return!0},onformat:function(e,t,n){ue(n,function(t,n){Y.setAttrib(e,n,t)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike,sub,sup,dfn,code,samp,kbd,var,cite,mark,q,del,ins",remove:"all",split:!0,expand:!1,block_expand:!0,deep:!0},{selector:"span",attributes:["style","class"],remove:"empty",split:!0,expand:!1,deep:!0},{selector:"*",attributes:["style","class"],split:!1,expand:!1,deep:!0}]}),ue("p h1 h2 h3 h4 h5 h6 div address pre div dt dd samp".split(/\s/),function(e){g(e,{block:e,remove:"all"})}),g(s.settings.formats)}function p(){s.addShortcut("meta+b","bold_desc","Bold"),s.addShortcut("meta+i","italic_desc","Italic"),s.addShortcut("meta+u","underline_desc","Underline");for(var e=1;6>=e;e++)s.addShortcut("access+"+e,"",["FormatBlock",!1,"h"+e]);s.addShortcut("access+7","",["FormatBlock",!1,"p"]),s.addShortcut("access+8","",["FormatBlock",!1,"div"]),s.addShortcut("access+9","",["FormatBlock",!1,"address"])}function m(e){return e?j[e]:j}function g(e,t){e&&("string"!=typeof e?ue(e,function(e,t){g(t,e)}):(t=t.length?t:[t],ue(t,function(e){e.deep===oe&&(e.deep=!e.selector),e.split===oe&&(e.split=!e.selector||e.inline),e.remove===oe&&e.selector&&!e.inline&&(e.remove="none"),e.selector&&e.inline&&(e.mixed=!0,e.block_expand=!0),"string"==typeof e.classes&&(e.classes=e.classes.split(/\s+/))}),j[e]=t))}function v(e){return e&&j[e]&&delete j[e],j}function y(e,t){var n=m(t);if(n)for(var r=0;r0)return r;if(r.childNodes.length>1||r==t||"BR"==r.tagName)return r}}var n=s.selection.getRng(),i=n.startContainer,o=n.endContainer;if(i!=o&&0===n.endOffset){var a=t(i,o),l=3==a.nodeType?a.data.length:a.childNodes.length;n.setEnd(a,l)}return n}function c(e,r,o){var a=[],s,c,h=!0;s=d.inline||d.block,c=Y.create(s),i(c),K.walk(e,function(e){function r(e){var m,v,y,b,C;return C=h,m=e.nodeName.toLowerCase(),v=e.parentNode.nodeName.toLowerCase(),1===e.nodeType&&ae(e)&&(C=h,h="true"===ae(e),b=!0),B(m,"br")?(p=0,void(d.block&&Y.remove(e))):d.wrapper&&E(e,t,n)?void(p=0):h&&!b&&d.block&&!d.wrapper&&l(m)&&G(v,s)?(e=Y.rename(e,s),i(e),a.push(e),void(p=0)):d.selector&&(ue(u,function(t){return"collapsed"in t&&t.collapsed!==g?void 0:Y.is(e,t.selector)&&!f(e)?(i(e,t),y=!0,!1):void 0}),!d.inline||y)?void(p=0):void(!h||b||!G(s,m)||!G(v,s)||!o&&3===e.nodeType&&1===e.nodeValue.length&&65279===e.nodeValue.charCodeAt(0)||f(e)||d.inline&&J(e)?(p=0,ue(de(e.childNodes),r),b&&(h=C),p=0):(p||(p=Y.clone(c,ne),e.parentNode.insertBefore(p,e),a.push(p)),p.appendChild(e)))}var p;ue(e,r)}),d.links===!0&&ue(a,function(e){function t(e){"A"===e.nodeName&&i(e,d),ue(de(e.childNodes),t)}t(e)}),ue(a,function(e){function r(e){var t=0;return ue(e.childNodes,function(e){P(e)||ce(e)||t++}),t}function o(e){var t,n;return ue(e.childNodes,function(e){return 1!=e.nodeType||ce(e)||f(e)?void 0:(t=e,ne)}),t&&!ce(t)&&A(t,d)&&(n=Y.clone(t,ne),i(n),Y.replace(n,e,re),Y.remove(t,1)),n||e}var s;if(s=r(e),(a.length>1||!J(e))&&0===s)return void Y.remove(e,1);if(d.inline||d.wrapper){if(d.exact||1!==s||(e=o(e)),ue(u,function(t){ue(Y.select(t.inline,e),function(e){ce(e)||F(t,n,e,t.exact?e:null)})}),E(e.parentNode,t,n))return Y.remove(e,1),e=0,re;d.merge_with_parents&&Y.getParent(e.parentNode,function(r){return E(r,t,n)?(Y.remove(e,1),e=0,re):void 0}),e&&d.merge_siblings!==!1&&(e=W(U(e),e),e=W(e,U(e,re)))}})}var u=m(t),d=u[0],h,p,g=!r&&X.isCollapsed();if("false"!==ae(X.getNode())){if(d){if(r)r.nodeType?(p=Y.createRng(),p.setStartBefore(r),p.setEndAfter(r),c(H(p,u),null,!0)):c(r,null,!0);else if(g&&d.inline&&!Y.select("td[data-mce-selected],th[data-mce-selected]").length)$("apply",t,n);else{var v=s.selection.getNode();Q||!u[0].defaultBlock||Y.getParent(v,Y.isBlock)||x(u[0].defaultBlock),s.selection.setRng(o()),h=X.getBookmark(),c(H(X.getRng(re),u),h),d.styles&&(d.styles.color||d.styles.textDecoration)&&(fe(v,C,"childNodes"),C(v)),X.moveToBookmark(h),q(X.getRng(re)),s.nodeChanged()}a.postProcess(t,s)}}else{r=X.getNode();for(var y=0,b=u.length;b>y;y++)if(u[y].ceFalseOverride&&Y.is(r,u[y].selector))return void i(r,u[y])}}function w(e,t,n,r){function i(e){var n,r,o,a,s;if(1===e.nodeType&&ae(e)&&(a=y,y="true"===ae(e),s=!0),n=de(e.childNodes),y&&!s)for(r=0,o=h.length;o>r&&!F(h[r],t,e,e);r++);if(p.deep&&n.length){for(r=0,o=n.length;o>r;r++)i(n[r]);s&&(y=a)}}function o(n){var i;return ue(d(n.parentNode).reverse(),function(n){var o;i||"_start"==n.id||"_end"==n.id||(o=E(n,e,t,r),o&&o.split!==!1&&(i=n))}),i}function a(e,n,r,i){var o,a,s,l,c,u;if(e){for(u=e.parentNode, +o=n.parentNode;o&&o!=u;o=o.parentNode){for(a=Y.clone(o,ne),c=0;cC&&(!h[C].ceFalseOverride||!F(h[C],t,n,n));C++);}}function N(e,t,n){var r=m(e);!_(e,t,n)||"toggle"in r[0]&&!r[0].toggle?x(e,t,n):w(e,t,n)}function E(e,t,n,r){function i(e,t,i){var o,a,s=t[i],l;if(t.onmatch)return t.onmatch(e,t,i);if(s)if(s.length===oe){for(o in s)if(s.hasOwnProperty(o)){if(a="attributes"===i?Y.getAttrib(e,o):D(e,o),r&&!a&&!t.exact)return;if((!r||t.exact)&&!B(a,L(M(s[o],n),o)))return}}else for(l=0;l=0;o--){if(a=t[o].selector,!a||t[o].defaultBlock)return re;for(i=r.length-1;i>=0;i--)if(Y.is(r[i],a))return re}return ne}function T(e,t,n){var r;return ie||(ie={},r={},s.on("NodeChange",function(e){var t=d(e.element),n={};t=i.grep(t,function(e){return 1==e.nodeType&&!e.getAttribute("data-mce-bogus")}),ue(ie,function(e,i){ue(t,function(o){return E(o,i,{},e.similar)?(r[i]||(ue(e,function(e){e(!0,{node:o,format:i,parents:t})}),r[i]=e),n[i]=e,!1):y(o,i)?!1:void 0})}),ue(r,function(i,o){n[o]||(delete r[o],ue(i,function(n){n(!1,{node:e.element,format:o,parents:t})}))})})),ue(e.split(","),function(e){ie[e]||(ie[e]=[],ie[e].similar=n),ie[e].push(t)}),this}function R(e){return o.getCssText(s,e)}function A(e,t){return B(e,t.inline)?re:B(e,t.block)?re:t.selector?1==e.nodeType&&Y.is(e,t.selector):void 0}function B(e,t){return e=e||"",t=t||"",e=""+(e.nodeName||e),t=""+(t.nodeName||t),e.toLowerCase()==t.toLowerCase()}function D(e,t){return L(Y.getStyle(e,t),t)}function L(e,t){return"color"!=t&&"backgroundColor"!=t||(e=Y.toHex(e)),"fontWeight"==t&&700==e&&(e="bold"),"fontFamily"==t&&(e=e.replace(/[\'\"]/g,"").replace(/,\s+/g,",")),""+e}function M(e,t){return"string"!=typeof e?e=e(t):t&&(e=e.replace(/%(\w+)/g,function(e,n){return t[n]||e})),e}function P(e){return e&&3===e.nodeType&&/^([\t \r\n]+|)$/.test(e.nodeValue)}function O(e,t,n){var r=Y.create(t,n);return e.parentNode.insertBefore(r,e),r.appendChild(e),r}function H(t,n,r){function i(e){function t(e){return"BR"==e.nodeName&&e.getAttribute("data-mce-bogus")&&!e.nextSibling}var r,i,o,a,s;if(r=i=e?g:y,a=e?"previousSibling":"nextSibling",s=Y.getRoot(),3==r.nodeType&&!P(r)&&(e?v>0:bo?n:o,-1===n||r||n++):(n=a.indexOf(" ",t),o=a.indexOf("\xa0",t),n=-1!==n&&(-1===o||o>n)?n:o),n}var a,l,c,u;if(3===t.nodeType){if(c=o(t,n),-1!==c)return{container:t,offset:c};u=t}for(a=new e(t,Y.getParent(t,J)||s.getBody());l=a[i?"prev":"next"]();)if(3===l.nodeType){if(u=l,c=o(l),-1!==c)return{container:l,offset:c}}else if(J(l))break;return u?(n=i?0:u.length,{container:u,offset:n}):void 0}function u(e,r){var i,o,a,s;for(3==e.nodeType&&0===e.nodeValue.length&&e[r]&&(e=e[r]),i=d(e),o=0;oh?h:v],3==g.nodeType&&(v=0)),1==y.nodeType&&y.hasChildNodes()&&(h=y.childNodes.length-1,y=y.childNodes[b>h?h:b-1],3==y.nodeType&&(b=y.nodeValue.length)),g=a(g),y=a(y),(ce(g.parentNode)||ce(g))&&(g=ce(g)?g:g.parentNode,g=g.nextSibling||g,3==g.nodeType&&(v=0)),(ce(y.parentNode)||ce(y))&&(y=ce(y)?y:y.parentNode,y=y.previousSibling||y,3==y.nodeType&&(b=y.length)),n[0].inline&&(t.collapsed&&(m=c(g,v,!0),m&&(g=m.container,v=m.offset),m=c(y,b),m&&(y=m.container,b=m.offset)),p=o(y,b),p.node)){for(;p.node&&0===p.offset&&p.node.previousSibling;)p=o(p.node.previousSibling);p.node&&p.offset>0&&3===p.node.nodeType&&" "===p.node.nodeValue.charAt(p.offset-1)&&p.offset>1&&(y=p.node,y.splitText(p.offset-1))}return(n[0].inline||n[0].block_expand)&&(n[0].inline&&3==g.nodeType&&0!==v||(g=i(!0)),n[0].inline&&3==y.nodeType&&b!==y.nodeValue.length||(y=i())),n[0].selector&&n[0].expand!==ne&&!n[0].inline&&(g=u(g,"previousSibling"),y=u(y,"nextSibling")),(n[0].block||n[0].selector)&&(g=f(g,"previousSibling"),y=f(y,"nextSibling"),n[0].block&&(J(g)||(g=i(!0)),J(y)||(y=i()))),1==g.nodeType&&(v=Z(g),g=g.parentNode),1==y.nodeType&&(b=Z(y)+1,y=y.parentNode),{startContainer:g,startOffset:v,endContainer:y,endOffset:b}}function I(e,t){return t.links&&"A"==e.tagName}function F(e,t,n,r){var i,o,a;if(!A(n,e)&&!I(n,e))return ne;if("all"!=e.remove)for(ue(e.styles,function(i,o){i=L(M(i,t),o),"number"==typeof o&&(o=i,r=0),(e.remove_similar||!r||B(D(r,o),i))&&Y.setStyle(n,o,""),a=1}),a&&""===Y.getAttrib(n,"style")&&(n.removeAttribute("style"),n.removeAttribute("data-mce-style")),ue(e.attributes,function(e,i){var o;if(e=M(e,t),"number"==typeof i&&(i=e,r=0),!r||B(Y.getAttrib(r,i),e)){if("class"==i&&(e=Y.getAttrib(n,i),e&&(o="",ue(e.split(/\s+/),function(e){/mce\-\w+/.test(e)&&(o+=(o?" ":"")+e)}),o)))return void Y.setAttrib(n,i,o);"class"==i&&n.removeAttribute("className"),te.test(i)&&n.removeAttribute("data-mce-"+i),n.removeAttribute(i)}}),ue(e.classes,function(e){e=M(e,t),r&&!Y.hasClass(r,e)||Y.removeClass(n,e)}),o=Y.getAttribs(n),i=0;io?o:i]),3===r.nodeType&&n&&i>=r.nodeValue.length&&(r=new e(r,s.getBody()).next()||r),3!==r.nodeType||n||0!==i||(r=new e(r,s.getBody()).prev()||r),r}function $(t,n,r,i){function o(e){var t=Y.create("span",{id:g,"data-mce-bogus":!0,style:v?"color:red":""});return e&&t.appendChild(s.getDoc().createTextNode(ee)),t}function a(e,t){for(;e;){if(3===e.nodeType&&e.nodeValue!==ee||e.childNodes.length>1)return!1;t&&1===e.nodeType&&t.push(e),e=e.firstChild}return!0}function c(e){for(;e;){if(e.id===g)return e;e=e.parentNode}}function u(t){var n;if(t)for(n=new e(t,t),t=n.current();t;t=n.next())if(3===t.nodeType)return t}function d(e,t){var n,r;if(e)r=X.getRng(!0),a(e)?(t!==!1&&(r.setStartBefore(e),r.setEndBefore(e)),Y.remove(e)):(n=u(e),n.nodeValue.charAt(0)===ee&&(n.deleteData(0,1),r.startContainer==n&&r.startOffset>0&&r.setStart(n,r.startOffset-1),r.endContainer==n&&r.endOffset>0&&r.setEnd(n,r.endOffset-1)),Y.remove(e,1)),X.setRng(r);else if(e=c(X.getStart()),!e)for(;e=Y.get(g);)d(e,!1)}function f(){var e,t,i,a,s,l,d;e=X.getRng(!0),a=e.startOffset,l=e.startContainer,d=l.nodeValue,t=c(X.getStart()),t&&(i=u(t)),d&&a>0&&a=0;h--)u.appendChild(Y.clone(f[h],!1)),u=u.firstChild;u.appendChild(Y.doc.createTextNode(ee)),u=u.firstChild;var g=Y.getParent(d,l);g&&Y.isEmpty(g)?d.parentNode.replaceChild(p,d):Y.insertAfter(p,d),X.setCursorLocation(u,1),Y.isEmpty(d)&&Y.remove(d)}}function p(){var e;e=c(X.getStart()),e&&!Y.isEmpty(e)&&fe(e,function(e){1!=e.nodeType||e.id===g||Y.isEmpty(e)||Y.setAttrib(e,"data-mce-bogus",null)},"childNodes")}var g="_mce_caret",v=s.settings.caret_debug;s._hasCaretEvents||(le=function(){var e=[],t;if(a(c(X.getStart()),e))for(t=e.length;t--;)Y.setAttrib(e[t],"data-mce-bogus","1")},se=function(e){var t=e.keyCode;d(),8==t&&X.isCollapsed()&&X.getStart().innerHTML==ee&&d(c(X.getStart())),37!=t&&39!=t||d(c(X.getStart())),p()},s.on("SetContent",function(e){e.selection&&p()}),s._hasCaretEvents=!0),"apply"==t?f():h()}function q(t){var n=t.startContainer,r=t.startOffset,i,o,a,s,l;if((t.startContainer!=t.endContainer||!u(t.startContainer.childNodes[t.startOffset]))&&(3==n.nodeType&&r>=n.nodeValue.length&&(r=Z(n),n=n.parentNode,i=!0),1==n.nodeType))for(s=n.childNodes,n=s[Math.min(r,s.length-1)],o=new e(n,Y.getParent(n,Y.isBlock)),(r>s.length-1||i)&&o.next(),a=o.current();a;a=o.next())if(3==a.nodeType&&!P(a))return l=Y.create("a",{"data-mce-bogus":"all"},ee),a.parentNode.insertBefore(l,a),t.setStart(a,0),X.setRng(t),void Y.remove(l)}var j={},Y=s.dom,X=s.selection,K=new t(Y),G=s.schema.isValidChild,J=Y.isBlock,Q=s.settings.forced_root_block,Z=Y.nodeIndex,ee="\ufeff",te=/^(src|href|style)$/,ne=!1,re=!0,ie,oe,ae=Y.getContentEditable,se,le,ce=n.isBookmarkNode,ue=i.each,de=i.grep,fe=i.walk,he=i.extend;he(this,{get:m,register:g,unregister:v,apply:x,remove:w,toggle:N,match:_,matchAll:S,matchNode:E,canApply:k,formatChanged:T,getCssText:R}),h(),p(),s.on("BeforeGetContent",function(e){le&&"raw"!=e.format&&le()}),s.on("mouseup keydown",function(e){se&&se(e)})}}),r(Q,[I,d],function(e,t){return function(e){function n(){return e.serializer.getTrimmedContent()}function r(t){e.setDirty(t)}function i(e){o.typing=!1,o.add({},e)}var o=this,a=0,s=[],l,c,u=0;return e.on("init",function(){o.add()}),e.on("BeforeExecCommand",function(e){var t=e.command;"Undo"!=t&&"Redo"!=t&&"mceRepaint"!=t&&o.beforeChange()}),e.on("ExecCommand",function(e){var t=e.command;"Undo"!=t&&"Redo"!=t&&"mceRepaint"!=t&&i(e)}),e.on("ObjectResizeStart Cut",function(){o.beforeChange()}),e.on("SaveContent ObjectResized blur",i),e.on("DragEnd",i),e.on("KeyUp",function(a){var l=a.keyCode;a.isDefaultPrevented()||((l>=33&&36>=l||l>=37&&40>=l||45==l||13==l||a.ctrlKey)&&(i(),e.nodeChanged()),(46==l||8==l||t.mac&&(91==l||93==l))&&e.nodeChanged(),c&&o.typing&&(e.isDirty()||(r(s[0]&&n()!=s[0].content),e.isDirty()&&e.fire("change",{level:s[0],lastLevel:null})),e.fire("TypingUndo"),c=!1,e.nodeChanged()))}),e.on("KeyDown",function(e){var t=e.keyCode;if(!e.isDefaultPrevented()){if(t>=33&&36>=t||t>=37&&40>=t||45==t)return void(o.typing&&i(e));var n=e.ctrlKey&&!e.altKey||e.metaKey;!(16>t||t>20)||224==t||91==t||o.typing||n||(o.beforeChange(),o.typing=!0,o.add({},e),c=!0)}}),e.on("MouseDown",function(e){o.typing&&i(e)}),e.addShortcut("meta+z","","Undo"),e.addShortcut("meta+y,meta+shift+z","","Redo"),e.on("AddUndo Undo Redo ClearUndos",function(t){t.isDefaultPrevented()||e.nodeChanged()}),o={data:s,typing:!1,beforeChange:function(){u||(l=e.selection.getBookmark(2,!0))},add:function(t,i){var o,c=e.settings,d;if(t=t||{},t.content=n(),u||e.removed)return null;if(d=s[a],e.fire("BeforeAddUndo",{level:t,lastLevel:d,originalEvent:i}).isDefaultPrevented())return null;if(d&&d.content==t.content)return null;if(s[a]&&(s[a].beforeBookmark=l),c.custom_undo_redo_levels&&s.length>c.custom_undo_redo_levels){for(o=0;o0&&(r(!0),e.fire("change",f)),t},undo:function(){var t;return o.typing&&(o.add(),o.typing=!1),a>0&&(t=s[--a],e.setContent(t.content,{format:"raw"}),e.selection.moveToBookmark(t.beforeBookmark),r(!0),e.fire("undo",{level:t})),t},redo:function(){var t;return a0||o.typing&&s[0]&&n()!=s[0].content},hasRedo:function(){return aP)&&(u=a.create("br"),t.parentNode.insertBefore(u,t)),l.setStartBefore(t),l.setEndBefore(t)):(l.setStartAfter(t),l.setEndAfter(t)):(l.setStart(t,0),l.setEnd(t,0));s.setRng(l),a.remove(u),s.scrollIntoView(t)}}function y(e){var t=l.forced_root_block;t&&t.toLowerCase()===e.tagName.toLowerCase()&&a.setAttribs(e,l.forced_root_block_attrs)}function b(e){e.innerHTML=r?"":'
    '}function C(e){var t=D,n,i,o,s=u.getTextInlineElements();if(e||"TABLE"==z?(n=a.create(e||W),y(n)):n=M.cloneNode(!1),o=n,l.keep_styles!==!1)do if(s[t.nodeName]){if("_mce_caret"==t.id)continue;i=t.cloneNode(!1),a.setAttrib(i,"id",""),n.hasChildNodes()?(i.appendChild(n.firstChild),n.appendChild(i)):(o=i,n.appendChild(i))}while((t=t.parentNode)&&t!=B);return r||(o.innerHTML='
    '),n}function x(t){var n,r,i;if(3==D.nodeType&&(t?L>0:LD.childNodes.length-1,D=D.childNodes[Math.min(L,D.childNodes.length-1)]||D,L=V&&3==D.nodeType?D.nodeValue.length:0),B=S(D)){if(c.beforeChange(),!a.isBlock(B)&&B!=a.getRoot())return void(W&&!O||E());if((W&&!O||!W&&O)&&(D=w(D,L)),M=a.getParent(D,a.isBlock),F=M?a.getParent(M.parentNode,a.isBlock):null,z=M?M.nodeName.toUpperCase():"",U=F?F.nodeName.toUpperCase():"","LI"!=U||o.ctrlKey||(M=F,z=U),/^(LI|DT|DD)$/.test(z)){if(!W&&O)return void E();if(a.isEmpty(M))return void N()}if("PRE"==z&&l.br_in_pre!==!1){if(!O)return void E()}else if(!W&&!O&&"LI"!=z||W&&O)return void E();W&&M===i.getBody()||(W=W||"P",x()?T():x(!0)?(H=M.parentNode.insertBefore(C(),M),m(H),v(M)):(A=R.cloneRange(),A.setEndAfter(M),I=A.extractContents(),_(I),H=I.firstChild,a.insertAfter(I,M),g(H),k(M),a.isEmpty(M)&&b(M),H.normalize(),a.isEmpty(H)?(a.remove(H),T()):v(H)),a.setAttrib(H,"id",""),i.fire("NewBlock",{newBlock:H}),c.add())}}}var a=i.dom,s=i.selection,l=i.settings,c=i.undoManager,u=i.schema,d=u.getNonEmptyElements(),f=u.getMoveCaretBeforeOnEnterElements();i.on("keydown",function(e){13==e.keyCode&&o(e)!==!1&&e.preventDefault()})}}),r(ee,[],function(){return function(e){function t(){var t=i.getStart(),s=e.getBody(),l,c,u,d,f,h,p,m=-16777215,g,v,y,b,C;if(C=n.forced_root_block,t&&1===t.nodeType&&C){for(;t&&t!=s;){if(a[t.nodeName])return;t=t.parentNode}if(l=i.getRng(),l.setStart){c=l.startContainer,u=l.startOffset,d=l.endContainer,f=l.endOffset;try{v=e.getDoc().activeElement===s}catch(x){}}else l.item&&(t=l.item(0),l=e.getDoc().body.createTextRange(),l.moveToElementText(t)),v=l.parentElement().ownerDocument===e.getDoc(),y=l.duplicate(),y.collapse(!0),u=-1*y.move("character",m),y.collapsed||(y=l.duplicate(),y.collapse(!1),f=-1*y.move("character",m)-u);for(t=s.firstChild,b=s.nodeName.toLowerCase();t;)if((3===t.nodeType||1==t.nodeType&&!a[t.nodeName])&&o.isValidChild(b,C.toLowerCase())){if(3===t.nodeType&&0===t.nodeValue.length){p=t,t=t.nextSibling,r.remove(p);continue}h||(h=r.create(C,e.settings.forced_root_block_attrs),t.parentNode.insertBefore(h,t),g=!0),p=t,t=t.nextSibling,h.appendChild(p)}else h=null,t=t.nextSibling;if(g&&v){if(l.setStart)l.setStart(c,u),l.setEnd(d,f),i.setRng(l);else try{l=e.getDoc().body.createTextRange(),l.moveToElementText(s),l.collapse(!0),l.moveStart("character",u),f>0&&l.moveEnd("character",f),l.select()}catch(x){}e.nodeChanged()}}}var n=e.settings,r=e.dom,i=e.selection,o=e.schema,a=o.getBlockElements();n.forced_root_block&&e.on("NodeChange",t)}}),r(te,[z,y,_,$,k,U],function(e,t,n,r,i,o){function a(e){return e>0}function s(e){return 0>e}function l(e,n,r,i,o){var l=new t(e,i);if(s(n)){if(C(e)&&(e=l.prev(!0),r(e)))return e;for(;e=l.prev(o);)if(r(e))return e}if(a(n)){if(C(e)&&(e=l.next(!0),r(e)))return e;for(;e=l.next(o);)if(r(e))return e}return null}function c(e,t){for(e=e.parentNode;e&&e!=t;e=e.parentNode)if(b(e))return e;return t}function u(e,t){for(;e&&e!=t;){if(x(e))return e;e=e.parentNode}return null}function d(e,t,n){return u(e.container(),n)==u(t.container(),n)}function f(e,t,n){return c(e.container(),n)==c(t.container(),n)}function h(e,t){var n,r;return t?(n=t.container(),r=t.offset(),E(n)?n.childNodes[r+e]:null):null}function p(e,t){var n=t.ownerDocument.createRange();return e?(n.setStartBefore(t),n.setEndBefore(t)):(n.setStartAfter(t),n.setEndAfter(t)),n}function m(e,t,n){return u(t,e)==u(n,e)}function g(e,t,n){var r,i;for(i=e?"previousSibling":"nextSibling";n&&n!=t;){if(r=n[i],w(r)&&(r=r[i]),C(r)){if(m(t,r,n))return r;break}if(_(r))break;n=n.parentNode}return null}function v(e,t,r){var o,a,s,l,c=N(g,!0,t),u=N(g,!1,t);if(a=r.startContainer,s=r.startOffset,i.isCaretContainerBlock(a)){if(E(a)||(a=a.parentNode),l=a.getAttribute("data-mce-caret"),"before"==l&&(o=a.nextSibling,C(o)))return S(o);if("after"==l&&(o=a.previousSibling,C(o)))return k(o)}if(!r.collapsed)return r;if(n.isText(a)){if(w(a)){if(1===e){if(o=u(a))return S(o);if(o=c(a))return k(o)}if(-1===e){if(o=c(a))return k(o);if(o=u(a))return S(o)}return r}if(i.endsWithCaretContainer(a)&&s>=a.data.length-1)return 1===e&&(o=u(a))?S(o):r;if(i.startsWithCaretContainer(a)&&1>=s)return-1===e&&(o=c(a))?k(o):r;if(s===a.data.length)return o=u(a),o?S(o):r;if(0===s)return o=c(a),o?k(o):r}return r}function y(e,t){return C(h(e,t))}var b=n.isContentEditableTrue,C=n.isContentEditableFalse,x=n.matchStyleValues("display","block table table-cell table-caption"),w=i.isCaretContainer,N=e.curry,E=n.isElement,_=o.isCaretCandidate,S=N(p,!0),k=N(p,!1);return{isForwards:a,isBackwards:s,findNode:l,getEditingHost:c,getParentBlock:u,isInSameBlock:d,isInSameEditingHost:f,isBeforeContentEditableFalse:N(y,0),isAfterContentEditableFalse:N(y,-1),normalizeRange:v}}),r(ne,[_,U,$,te,p,z],function(e,t,n,r,i,o){function a(e,t){for(var n=[];e&&e!=t;)n.push(e),e=e.parentNode;return n}function s(e,t){return e.hasChildNodes()&&t0)return n(C,--x);if(m(e)&&x0&&(N=s(C,x-1),v(N)))return!y(N)&&(E=r.findNode(N,e,b,N))?f(E)?n(E,E.data.length):n.after(E):f(N)?n(N,N.data.length):n.before(N);if(m(e)&&x0&&s(e[e.length-1])?e.slice(0,-1):e},c=function(e,t){var n=e.getParent(t,e.isBlock);return n&&"LI"===n.nodeName?n:null},u=function(e,t){return!!c(e,t)},d=function(e,t){var n=t.cloneRange(),r=t.cloneRange();return n.setStartBefore(e),r.setEndAfter(e),[n.cloneContents(),r.cloneContents()]},f=function(e,r){var i=n.before(e),o=new t(r),a=o.next(i);return a?a.toRange():null},h=function(e,r){var i=n.after(e),o=new t(r),a=o.prev(i);return a?a.toRange():null},p=function(t,n,r,i){var o=d(t,i),a=t.parentNode;return a.insertBefore(o[0],t),e.each(n,function(e){a.insertBefore(e,t)}),a.insertBefore(o[1],t),a.removeChild(t),h(n[n.length-1],r)},m=function(t,n,r){var i=t.parentNode;return e.each(n,function(e){i.insertBefore(e,t)}),f(t,r)},g=function(e,t,n,r){return r.insertAfter(t.reverse(),e),h(t[0],n)},v=function(e,r,i,s){var u=o(r,e,s),d=c(r,i.startContainer),f=l(a(u.firstChild)),h=1,v=2,y=r.getRoot(),b=function(e){var o=n.fromRangeStart(i),a=new t(r.getRoot()),s=e===h?a.prev(o):a.next(o);return s?c(r,s.getNode())!==d:!0};return b(h)?m(d,f,y):b(v)?g(d,f,y,r):p(d,f,y,i)};return{isListFragment:r,insertAtCaret:v,isParentBlockLi:u,trimListItems:l,listItems:a}}),r(ie,[d,m,P,ne,$,X,_,re],function(e,t,n,r,i,o,a,s){var l=a.matchNodeNames("td th"),c=function(a,c,u){function d(e){function t(e){return r[e]&&3==r[e].nodeType}var n,r,i;return n=D.getRng(!0),r=n.startContainer,i=n.startOffset,3==r.nodeType&&(i>0?e=e.replace(/^ /," "):t("previousSibling")||(e=e.replace(/^ /," ")),i|)$/," "):t("nextSibling")||(e=e.replace(/( | )(
    |)$/," "))),e}function f(){var e,t,n;e=D.getRng(!0),t=e.startContainer,n=e.startOffset,3==t.nodeType&&e.collapsed&&("\xa0"===t.data[n]?(t.deleteData(n,1),/[\u00a0| ]$/.test(c)||(c+=" ")):"\xa0"===t.data[n-1]&&(t.deleteData(n-1,1),/[\u00a0| ]$/.test(c)||(c=" "+c)))}function h(){if(A){var e=a.getBody(),n=new o(L);t.each(L.select("*[data-mce-fragment]"),function(t){for(var r=t.parentNode;r&&r!=e;r=r.parentNode)B[t.nodeName.toLowerCase()]&&n.compare(r,t)&&L.remove(t,!0)})}}function p(e){for(var t=e;t=t.walk();)1===t.type&&t.attr("data-mce-fragment","1")}function m(e){t.each(e.getElementsByTagName("*"),function(e){e.removeAttribute("data-mce-fragment")})}function g(e){return!!e.getAttribute("data-mce-fragment")}function v(e){return e&&!a.schema.getShortEndedElements()[e.nodeName]}function y(t){function n(e){for(var t=a.getBody();e&&e!==t;e=e.parentNode)if("false"===a.dom.getContentEditable(e))return e;return null}function o(e){var t=i.fromRangeStart(e),n=new r(a.getBody());return t=n.next(t),t?t.toRange():void 0}var s,c,u;if(t){if(D.scrollIntoView(t),s=n(t))return L.remove(t),void D.select(s);S=L.createRng(),k=t.previousSibling,k&&3==k.nodeType?(S.setStart(k,k.nodeValue.length),e.ie||(T=t.nextSibling,T&&3==T.nodeType&&(k.appendData(T.data),T.parentNode.removeChild(T)))):(S.setStartBefore(t),S.setEndBefore(t)),c=L.getParent(t,L.isBlock),L.remove(t),c&&L.isEmpty(c)&&(a.$(c).empty(),S.setStart(c,0),S.setEnd(c,0),l(c)||g(c)||!(u=o(S))?L.add(c,L.create("br",{"data-mce-bogus":"1"})):(S=u,L.remove(c))),D.setRng(S)}}var b,C,x,w,N,E,_,S,k,T,R,A,B=a.schema.getTextInlineElements(),D=a.selection,L=a.dom;/^ | $/.test(c)&&(c=d(c)),b=a.parser,A=u.merge,C=new n({validate:a.settings.validate},a.schema),R='​',E={content:c,format:"html",selection:!0},a.fire("BeforeSetContent",E),c=E.content,-1==c.indexOf("{$caret}")&&(c+="{$caret}"),c=c.replace(/\{\$caret\}/,R),S=D.getRng();var M=S.startContainer||(S.parentElement?S.parentElement():null),P=a.getBody();M===P&&D.isCollapsed()&&L.isBlock(P.firstChild)&&v(P.firstChild)&&L.isEmpty(P.firstChild)&&(S=L.createRng(),S.setStart(P.firstChild,0),S.setEnd(P.firstChild,0),D.setRng(S)),D.isCollapsed()||(a.selection.setRng(a.selection.getRng()),a.getDoc().execCommand("Delete",!1,null), +f()),x=D.getNode();var O={context:x.nodeName.toLowerCase(),data:u.data};if(N=b.parse(c,O),u.paste===!0&&s.isListFragment(N)&&s.isParentBlockLi(L,x))return S=s.insertAtCaret(C,L,a.selection.getRng(!0),N),a.selection.setRng(S),void a.fire("SetContent",E);if(p(N),k=N.lastChild,"mce_marker"==k.attr("id"))for(_=k,k=k.prev;k;k=k.walk(!0))if(3==k.type||!L.isBlock(k.name)){a.schema.isValidChild(k.parent.name,"span")&&k.parent.insert(_,k,"br"===k.name);break}if(a._selectionOverrides.showBlockCaretContainer(x),O.invalid){for(D.setContent(R),x=D.getNode(),w=a.getBody(),9==x.nodeType?x=k=w:k=x;k!==w;)x=k,k=k.parentNode;c=x==w?w.innerHTML:L.getOuterHTML(x),c=C.serialize(b.parse(c.replace(//i,function(){return C.serialize(N)}))),x==w?L.setHTML(w,c):L.setOuterHTML(x,c)}else c=C.serialize(N),k=x.firstChild,T=x.lastChild,!k||k===T&&"BR"===k.nodeName?L.setHTML(x,c):D.setContent(c);h(),y(L.get("mce_marker")),m(a.getBody()),a.fire("SetContent",E),a.addVisual()},u=function(e){var n;return"string"!=typeof e?(n=t.extend({paste:e.paste,data:{paste:e.paste}},e),{content:e.content,details:n}):{content:e,details:{}}},d=function(e,t){var n=u(t);c(e,n.content,n.details)};return{insertAtCaret:d}}),r(oe,[d,m,T,y,ie],function(e,n,r,i,o){var a=n.each,s=n.extend,l=n.map,c=n.inArray,u=n.explode,d=e.ie&&e.ie<11,f=!0,h=!1;return function(n){function p(e,t,r,i){var o,s,l=0;if(/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(e)||i&&i.skip_focus||n.focus(),i=n.fire("BeforeExecCommand",{command:e,ui:t,value:r}),i.isDefaultPrevented())return!1;if(s=e.toLowerCase(),o=B.exec[s])return o(s,t,r),n.fire("ExecCommand",{command:e,ui:t,value:r}),!0;if(a(n.plugins,function(i){return i.execCommand&&i.execCommand(e,t,r)?(n.fire("ExecCommand",{command:e,ui:t,value:r}),l=!0,!1):void 0}),l)return l;if(n.theme&&n.theme.execCommand&&n.theme.execCommand(e,t,r))return n.fire("ExecCommand",{command:e,ui:t,value:r}),!0;try{l=n.getDoc().execCommand(e,t,r)}catch(c){}return l?(n.fire("ExecCommand",{command:e,ui:t,value:r}),!0):!1}function m(e){var t;if(!n.quirks.isHidden()){if(e=e.toLowerCase(),t=B.state[e])return t(e);try{return n.getDoc().queryCommandState(e)}catch(r){}return!1}}function g(e){var t;if(!n.quirks.isHidden()){if(e=e.toLowerCase(),t=B.value[e])return t(e);try{return n.getDoc().queryCommandValue(e)}catch(r){}}}function v(e,t){t=t||"exec",a(e,function(e,n){a(n.toLowerCase().split(","),function(n){B[t][n]=e})})}function y(e,t,r){e=e.toLowerCase(),B.exec[e]=function(e,i,o,a){return t.call(r||n,i,o,a)}}function b(e){if(e=e.toLowerCase(),B.exec[e])return!0;try{return n.getDoc().queryCommandSupported(e)}catch(t){}return!1}function C(e,t,r){e=e.toLowerCase(),B.state[e]=function(){return t.call(r||n)}}function x(e,t,r){e=e.toLowerCase(),B.value[e]=function(){return t.call(r||n)}}function w(e){return e=e.toLowerCase(),!!B.exec[e]}function N(e,r,i){return r===t&&(r=h),i===t&&(i=null),n.getDoc().execCommand(e,r,i)}function E(e){return A.match(e)}function _(e,r){A.toggle(e,r?{value:r}:t),n.nodeChanged()}function S(e){L=R.getBookmark(e)}function k(){R.moveToBookmark(L)}var T,R,A,B={state:{},exec:{},value:{}},D=n.settings,L;n.on("PreInit",function(){T=n.dom,R=n.selection,D=n.settings,A=n.formatter}),s(this,{execCommand:p,queryCommandState:m,queryCommandValue:g,queryCommandSupported:b,addCommands:v,addCommand:y,addQueryStateHandler:C,addQueryValueHandler:x,hasCustomCommand:w}),v({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(t){var r=n.getDoc(),i;try{N(t)}catch(o){i=f}if("paste"!==t||r.queryCommandEnabled(t)||(i=!0),i||!r.queryCommandSupported(t)){var a=n.translate("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.");e.mac&&(a=a.replace(/Ctrl\+/g,"\u2318+")),n.notificationManager.open({text:a,type:"error"})}},unlink:function(){if(R.isCollapsed()){var e=R.getNode();return void("A"==e.tagName&&n.dom.remove(e,!0))}A.remove("link")},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,JustifyNone":function(e){var t=e.substring(7);"full"==t&&(t="justify"),a("left,center,right,justify".split(","),function(e){t!=e&&A.remove("align"+e)}),"none"!=t&&_("align"+t)},"InsertUnorderedList,InsertOrderedList":function(e){var t,n;N(e),t=T.getParent(R.getNode(),"ol,ul"),t&&(n=t.parentNode,/^(H[1-6]|P|ADDRESS|PRE)$/.test(n.nodeName)&&(S(),T.split(n,t),k()))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){_(e)},"ForeColor,HiliteColor,FontName":function(e,t,n){_(e,n)},FontSize:function(e,t,n){var r,i;n>=1&&7>=n&&(i=u(D.font_size_style_values),r=u(D.font_size_classes),n=r?r[n-1]||n:i[n-1]||n),_(e,n)},RemoveFormat:function(e){A.remove(e)},mceBlockQuote:function(){_("blockquote")},FormatBlock:function(e,t,n){return _(n||"p")},mceCleanup:function(){var e=R.getBookmark();n.setContent(n.getContent({cleanup:f}),{cleanup:f}),R.moveToBookmark(e)},mceRemoveNode:function(e,t,r){var i=r||R.getNode();i!=n.getBody()&&(S(),n.dom.remove(i,f),k())},mceSelectNodeDepth:function(e,t,r){var i=0;T.getParent(R.getNode(),function(e){return 1==e.nodeType&&i++==r?(R.select(e),h):void 0},n.getBody())},mceSelectNode:function(e,t,n){R.select(n)},mceInsertContent:function(e,t,r){o.insertAtCaret(n,r)},mceInsertRawHTML:function(e,t,r){R.setContent("tiny_mce_marker"),n.setContent(n.getContent().replace(/tiny_mce_marker/g,function(){return r}))},mceToggleFormat:function(e,t,n){_(n)},mceSetContent:function(e,t,r){n.setContent(r)},"Indent,Outdent":function(e){var t,r,i;t=D.indentation,r=/[a-z%]+$/i.exec(t),t=parseInt(t,10),m("InsertUnorderedList")||m("InsertOrderedList")?N(e):(D.forced_root_block||T.getParent(R.getNode(),T.isBlock)||A.apply("div"),a(R.getSelectedBlocks(),function(o){if("false"!==T.getContentEditable(o)&&"LI"!=o.nodeName){var a=n.getParam("indent_use_margin",!1)?"margin":"padding";a+="rtl"==T.getStyle(o,"direction",!0)?"Right":"Left","outdent"==e?(i=Math.max(0,parseInt(o.style[a]||0,10)-t),T.setStyle(o,a,i?i+r:"")):(i=parseInt(o.style[a]||0,10)+t+r,T.setStyle(o,a,i))}}))},mceRepaint:function(){},InsertHorizontalRule:function(){n.execCommand("mceInsertContent",!1,"
    ")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual,n.addVisual()},mceReplaceContent:function(e,t,r){n.execCommand("mceInsertContent",!1,r.replace(/\{\$selection\}/g,R.getContent({format:"text"})))},mceInsertLink:function(e,t,n){var r;"string"==typeof n&&(n={href:n}),r=T.getParent(R.getNode(),"a"),n.href=n.href.replace(" ","%20"),r&&n.href||A.remove("link"),n.href&&A.apply("link",n,r)},selectAll:function(){var e=T.getRoot(),t;R.getRng().setStart?(t=T.createRng(),t.setStart(e,0),t.setEnd(e,e.childNodes.length),R.setRng(t)):(t=R.getRng(),t.item||(t.moveToElementText(e),t.select()))},"delete":function(){N("Delete");var e=n.getBody();T.isEmpty(e)&&(n.setContent(""),e.firstChild&&T.isBlock(e.firstChild)?n.selection.setCursorLocation(e.firstChild,0):n.selection.setCursorLocation(e,0))},mceNewDocument:function(){n.setContent("")},InsertLineBreak:function(e,t,o){function a(){for(var e=new i(m,v),t,r=n.schema.getNonEmptyElements();t=e.next();)if(r[t.nodeName.toLowerCase()]||t.length>0)return!0}var s=o,l,c,u,h=R.getRng(!0);new r(T).normalize(h);var p=h.startOffset,m=h.startContainer;if(1==m.nodeType&&m.hasChildNodes()){var g=p>m.childNodes.length-1;m=m.childNodes[Math.min(p,m.childNodes.length-1)]||m,p=g&&3==m.nodeType?m.nodeValue.length:0}var v=T.getParent(m,T.isBlock),y=v?v.nodeName.toUpperCase():"",b=v?T.getParent(v.parentNode,T.isBlock):null,C=b?b.nodeName.toUpperCase():"",x=s&&s.ctrlKey;"LI"!=C||x||(v=b,y=C),m&&3==m.nodeType&&p>=m.nodeValue.length&&(d||a()||(l=T.create("br"),h.insertNode(l),h.setStartAfter(l),h.setEndAfter(l),c=!0)),l=T.create("br"),h.insertNode(l);var w=T.doc.documentMode;return d&&"PRE"==y&&(!w||8>w)&&l.parentNode.insertBefore(T.doc.createTextNode("\r"),l),u=T.create("span",{}," "),l.parentNode.insertBefore(u,l),R.scrollIntoView(u),T.remove(u),c?(h.setStartBefore(l),h.setEndBefore(l)):(h.setStartAfter(l),h.setEndAfter(l)),R.setRng(h),n.undoManager.add(),f}}),v({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(e){var t="align"+e.substring(7),n=R.isCollapsed()?[T.getParent(R.getNode(),T.isBlock)]:R.getSelectedBlocks(),r=l(n,function(e){return!!A.matchNode(e,t)});return-1!==c(r,f)},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){return E(e)},mceBlockQuote:function(){return E("blockquote")},Outdent:function(){var e;if(D.inline_styles){if((e=T.getParent(R.getStart(),T.isBlock))&&parseInt(e.style.paddingLeft,10)>0)return f;if((e=T.getParent(R.getEnd(),T.isBlock))&&parseInt(e.style.paddingLeft,10)>0)return f}return m("InsertUnorderedList")||m("InsertOrderedList")||!D.inline_styles&&!!T.getParent(R.getNode(),"BLOCKQUOTE")},"InsertUnorderedList,InsertOrderedList":function(e){var t=T.getParent(R.getNode(),"ul,ol");return t&&("insertunorderedlist"===e&&"UL"===t.tagName||"insertorderedlist"===e&&"OL"===t.tagName)}},"state"),v({"FontSize,FontName":function(e){var t=0,n;return(n=T.getParent(R.getNode(),"span"))&&(t="fontsize"==e?n.style.fontSize:n.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()),t}},"value"),v({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}}),r(ae,[m],function(e){function t(e,o){var a=this,s,l;if(e=r(e),o=a.settings=o||{},s=o.base_uri,/^([\w\-]+):([^\/]{2})/i.test(e)||/^\s*#/.test(e))return void(a.source=e);var c=0===e.indexOf("//");0!==e.indexOf("/")||c||(e=(s?s.protocol||"http":"http")+"://mce_host"+e),/^[\w\-]*:?\/\//.test(e)||(l=o.base_uri?o.base_uri.path:new t(location.href).directory,""===o.base_uri.protocol?e="//mce_host"+a.toAbsPath(l,e):(e=/([^#?]*)([#?]?.*)/.exec(e),e=(s&&s.protocol||"http")+"://mce_host"+a.toAbsPath(l,e[1])+e[2])),e=e.replace(/@@/g,"(mce_at)"),e=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(e),n(i,function(t,n){var r=e[n];r&&(r=r.replace(/\(mce_at\)/g,"@@")),a[t]=r}),s&&(a.protocol||(a.protocol=s.protocol),a.userInfo||(a.userInfo=s.userInfo),a.port||"mce_host"!==a.host||(a.port=s.port),a.host&&"mce_host"!==a.host||(a.host=s.host),a.source=""),c&&(a.protocol="")}var n=e.each,r=e.trim,i="source protocol authority userInfo user password host port relative path directory file query anchor".split(" "),o={ftp:21,http:80,https:443,mailto:25};return t.prototype={setPath:function(e){var t=this;e=/^(.*?)\/?(\w+)?$/.exec(e),t.path=e[0],t.directory=e[1],t.file=e[2],t.source="",t.getURI()},toRelative:function(e){var n=this,r;if("./"===e)return e;if(e=new t(e,{base_uri:n}),"mce_host"!=e.host&&n.host!=e.host&&e.host||n.port!=e.port||n.protocol!=e.protocol&&""!==e.protocol)return e.getURI();var i=n.getURI(),o=e.getURI();return i==o||"/"==i.charAt(i.length-1)&&i.substr(0,i.length-1)==o?i:(r=n.toRelPath(n.path,e.path),e.query&&(r+="?"+e.query),e.anchor&&(r+="#"+e.anchor),r)},toAbsolute:function(e,n){return e=new t(e,{base_uri:this}),e.getURI(n&&this.isSameOrigin(e))},isSameOrigin:function(e){if(this.host==e.host&&this.protocol==e.protocol){if(this.port==e.port)return!0;var t=o[this.protocol];if(t&&(this.port||t)==(e.port||t))return!0}return!1},toRelPath:function(e,t){var n,r=0,i="",o,a;if(e=e.substring(0,e.lastIndexOf("/")),e=e.split("/"),n=t.split("/"),e.length>=n.length)for(o=0,a=e.length;a>o;o++)if(o>=n.length||e[o]!=n[o]){r=o+1;break}if(e.lengtho;o++)if(o>=e.length||e[o]!=n[o]){r=o+1;break}if(1===r)return t;for(o=0,a=e.length-(r-1);a>o;o++)i+="../";for(o=r-1,a=n.length;a>o;o++)i+=o!=r-1?"/"+n[o]:n[o];return i},toAbsPath:function(e,t){var r,i=0,o=[],a,s;for(a=/\/$/.test(t)?"/":"",e=e.split("/"),t=t.split("/"),n(e,function(e){e&&o.push(e)}),e=o,r=t.length-1,o=[];r>=0;r--)0!==t[r].length&&"."!==t[r]&&(".."!==t[r]?i>0?i--:o.push(t[r]):i++);return r=e.length-i,s=0>=r?o.reverse().join("/"):e.slice(0,r).join("/")+"/"+o.reverse().join("/"),0!==s.indexOf("/")&&(s="/"+s),a&&s.lastIndexOf("/")!==s.length-1&&(s+=a),s},getURI:function(e){var t,n=this;return n.source&&!e||(t="",e||(t+=n.protocol?n.protocol+"://":"//",n.userInfo&&(t+=n.userInfo+"@"),n.host&&(t+=n.host),n.port&&(t+=":"+n.port)),n.path&&(t+=n.path),n.query&&(t+="?"+n.query),n.anchor&&(t+="#"+n.anchor),n.source=t),n.source}},t.parseDataUri=function(e){var t,n;return e=decodeURIComponent(e).split(","),n=/data:([^;]+)/.exec(e[0]),n&&(t=n[1]),{type:t,data:e[1]}},t.getDocumentBaseUrl=function(e){var t;return t=0!==e.protocol.indexOf("http")&&"file:"!==e.protocol?e.href:e.protocol+"//"+e.host+e.pathname,/^[^:]+:\/\/\/?[^\/]+\//.test(t)&&(t=t.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(t)||(t+="/")),t},t}),r(se,[m],function(e){function t(){}var n=e.each,r=e.extend,i,o;return t.extend=i=function(e){function t(){var e,t,n,r=this;if(!o&&(r.init&&r.init.apply(r,arguments),t=r.Mixins))for(e=t.length;e--;)n=t[e],n.init&&n.init.apply(r,arguments)}function a(){return this}function s(e,t){return function(){var n=this,r=n._super,i;return n._super=c[e],i=t.apply(n,arguments),n._super=r,i}}var l=this,c=l.prototype,u,d,f;o=!0,u=new l,o=!1,e.Mixins&&(n(e.Mixins,function(t){for(var n in t)"init"!==n&&(e[n]=t[n])}),c.Mixins&&(e.Mixins=c.Mixins.concat(e.Mixins))),e.Methods&&n(e.Methods.split(","),function(t){e[t]=a}),e.Properties&&n(e.Properties.split(","),function(t){var n="_"+t;e[t]=function(e){var t=this,r;return e!==r?(t[n]=e,t):t[n]}}),e.Statics&&n(e.Statics,function(e,n){t[n]=e}),e.Defaults&&c.Defaults&&(e.Defaults=r({},c.Defaults,e.Defaults));for(d in e)f=e[d],"function"==typeof f&&c[d]?u[d]=s(d,f):u[d]=f;return t.prototype=u,t.constructor=t,t.extend=i,t},t}),r(le,[m],function(e){function t(t){function n(){return!1}function r(){return!0}function i(e,i){var o,s,l,c;if(e=e.toLowerCase(),i=i||{},i.type=e,i.target||(i.target=u),i.preventDefault||(i.preventDefault=function(){i.isDefaultPrevented=r},i.stopPropagation=function(){i.isPropagationStopped=r},i.stopImmediatePropagation=function(){i.isImmediatePropagationStopped=r},i.isDefaultPrevented=n,i.isPropagationStopped=n,i.isImmediatePropagationStopped=n),t.beforeFire&&t.beforeFire(i),o=d[e])for(s=0,l=o.length;l>s;s++){if(c=o[s],c.once&&a(e,c.func),i.isImmediatePropagationStopped())return i.stopPropagation(),i;if(c.func.call(u,i)===!1)return i.preventDefault(),i}return i}function o(t,r,i,o){var a,s,l;if(r===!1&&(r=n),r)for(r={func:r},o&&e.extend(r,o),s=t.toLowerCase().split(" "),l=s.length;l--;)t=s[l],a=d[t],a||(a=d[t]=[],f(t,!0)),i?a.unshift(r):a.push(r);return c}function a(e,t){var n,r,i,o,a;if(e)for(o=e.toLowerCase().split(" "),n=o.length;n--;){if(e=o[n],r=d[e],!e){for(i in d)f(i,!1),delete d[i];return c}if(r){if(t)for(a=r.length;a--;)r[a].func===t&&(r=r.slice(0,a).concat(r.slice(a+1)),d[e]=r);else r.length=0;r.length||(f(e,!1),delete d[e])}}else{for(e in d)f(e,!1);d={}}return c}function s(e,t,n){return o(e,t,n,{once:!0})}function l(e){return e=e.toLowerCase(),!(!d[e]||0===d[e].length)}var c=this,u,d={},f;t=t||{},u=t.scope||c,f=t.toggleEvent||n,c.fire=i,c.on=o,c.off=a,c.once=s,c.has=l}var n=e.makeMap("focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover draggesture dragdrop drop drag submit compositionstart compositionend compositionupdate touchstart touchmove touchend"," ");return t.isNative=function(e){return!!n[e.toLowerCase()]},t}),r(ce,[],function(){function e(e){this.create=e.create}return e.create=function(t,n){return new e({create:function(e,r){function i(t){e.set(r,t.value)}function o(e){t.set(n,e.value)}var a;return e.on("change:"+r,o),t.on("change:"+n,i),a=e._bindings,a||(a=e._bindings=[],e.on("destroy",function(){for(var e=a.length;e--;)a[e]()})),a.push(function(){t.off("change:"+n,i)}),t.get(n)}})},e}),r(ue,[le],function(e){function t(t){return t._eventDispatcher||(t._eventDispatcher=new e({scope:t,toggleEvent:function(n,r){e.isNative(n)&&t.toggleNativeEvent&&t.toggleNativeEvent(n,r)}})),t._eventDispatcher}return{fire:function(e,n,r){var i=this;if(i.removed&&"remove"!==e)return n;if(n=t(i).fire(e,n,r),r!==!1&&i.parent)for(var o=i.parent();o&&!n.isPropagationStopped();)o.fire(e,n,!1),o=o.parent();return n},on:function(e,n,r){return t(this).on(e,n,r)},off:function(e,n){return t(this).off(e,n)},once:function(e,n){return t(this).once(e,n)},hasEventListeners:function(e){return t(this).has(e)}}}),r(de,[ce,ue,se,m],function(e,t,n,r){function i(e){return e.nodeType>0}function o(e,t){var n,a;if(e===t)return!0;if(null===e||null===t)return e===t;if("object"!=typeof e||"object"!=typeof t)return e===t;if(r.isArray(t)){if(e.length!==t.length)return!1;for(n=e.length;n--;)if(!o(e[n],t[n]))return!1}if(i(e)||i(t))return e===t;a={};for(n in t){if(!o(e[n],t[n]))return!1;a[n]=!0}for(n in e)if(!a[n]&&!o(e[n],t[n]))return!1;return!0}return n.extend({Mixins:[t],init:function(t){var n,r;t=t||{};for(n in t)r=t[n],r instanceof e&&(t[n]=r.create(this,n));this.data=t},set:function(t,n){var r,i,a=this.data[t];if(n instanceof e&&(n=n.create(this,t)),"object"==typeof t){for(r in t)this.set(r,t[r]);return this}return o(a,n)||(this.data[t]=n,i={target:this,name:t,value:n,oldValue:a},this.fire("change:"+t,i),this.fire("change",i)),this},get:function(e){return this.data[e]},has:function(e){return e in this.data},bind:function(t){return e.create(this,t)},destroy:function(){this.fire("destroy")}})}),r(fe,[se],function(e){function t(e){for(var t=[],n=e.length,r;n--;)r=e[n],r.__checked||(t.push(r),r.__checked=1);for(n=t.length;n--;)delete t[n].__checked;return t}var n=/^([\w\\*]+)?(?:#([\w\-\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i,r=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,i=/^\s*|\s*$/g,o,a=e.extend({init:function(e){function t(e){return e?(e=e.toLowerCase(),function(t){return"*"===e||t.type===e}):void 0}function o(e){return e?function(t){return t._name===e}:void 0}function a(e){return e?(e=e.split("."),function(t){for(var n=e.length;n--;)if(!t.classes.contains(e[n]))return!1;return!0}):void 0}function s(e,t,n){return e?function(r){var i=r[e]?r[e]():"";return t?"="===t?i===n:"*="===t?i.indexOf(n)>=0:"~="===t?(" "+i+" ").indexOf(" "+n+" ")>=0:"!="===t?i!=n:"^="===t?0===i.indexOf(n):"$="===t?i.substr(i.length-n.length)===n:!1:!!n}:void 0}function l(e){var t;return e?(e=/(?:not\((.+)\))|(.+)/i.exec(e),e[1]?(t=u(e[1],[]),function(e){return!d(e,t)}):(e=e[2],function(t,n,r){return"first"===e?0===n:"last"===e?n===r-1:"even"===e?n%2===0:"odd"===e?n%2===1:t[e]?t[e]():!1})):void 0}function c(e,r,c){function u(e){e&&r.push(e)}var d;return d=n.exec(e.replace(i,"")),u(t(d[1])),u(o(d[2])),u(a(d[3])),u(s(d[4],d[5],d[6])),u(l(d[7])),r.pseudo=!!d[7],r.direct=c,r}function u(e,t){var n=[],i,o,a;do if(r.exec(""),o=r.exec(e),o&&(e=o[3],n.push(o[1]),o[2])){i=o[3];break}while(o);for(i&&u(i,t),e=[],a=0;a"!=n[a]&&e.push(c(n[a],[],">"===n[a-1]));return t.push(e),t}var d=this.match;this._selectors=u(e,[])},match:function(e,t){var n,r,i,o,a,s,l,c,u,d,f,h,p;for(t=t||this._selectors,n=0,r=t.length;r>n;n++){for(a=t[n],o=a.length,p=e,h=0,i=o-1;i>=0;i--)for(c=a[i];p;){if(c.pseudo)for(f=p.parent().items(),u=d=f.length;u--&&f[u]!==p;);for(s=0,l=c.length;l>s;s++)if(!c[s](p,u,d)){s=l+1;break}if(s===l){h++;break}if(i===o-1)break;p=p.parent()}if(h===o)return!0}return!1},find:function(e){function n(e,t,i){var o,a,s,l,c,u=t[i];for(o=0,a=e.length;a>o;o++){for(c=e[o],s=0,l=u.length;l>s;s++)if(!u[s](c,o,a)){s=l+1;break}if(s===l)i==t.length-1?r.push(c):c.items&&n(c.items(),t,i+1);else if(u.direct)return;c.items&&n(c.items(),t,i)}}var r=[],i,s,l=this._selectors;if(e.items){for(i=0,s=l.length;s>i;i++)n(e.items(),l[i],0);s>1&&(r=t(r))}return o||(o=a.Collection),new o(r)}});return a}),r(he,[m,fe,se],function(e,t,n){var r,i,o=Array.prototype.push,a=Array.prototype.slice;return i={length:0,init:function(e){e&&this.add(e)},add:function(t){var n=this;return e.isArray(t)?o.apply(n,t):t instanceof r?n.add(t.toArray()):o.call(n,t),n},set:function(e){var t=this,n=t.length,r;for(t.length=0,t.add(e),r=t.length;n>r;r++)delete t[r];return t},filter:function(e){var n=this,i,o,a=[],s,l;for("string"==typeof e?(e=new t(e),l=function(t){return e.match(t)}):l=e,i=0,o=n.length;o>i;i++)s=n[i],l(s)&&a.push(s);return new r(a)},slice:function(){return new r(a.apply(this,arguments))},eq:function(e){return-1===e?this.slice(e):this.slice(e,+e+1)},each:function(t){return e.each(this,t),this},toArray:function(){return e.toArray(this)},indexOf:function(e){for(var t=this,n=t.length;n--&&t[n]!==e;);return n},reverse:function(){return new r(e.toArray(this).reverse())},hasClass:function(e){return this[0]?this[0].classes.contains(e):!1},prop:function(e,t){var n=this,r,i;return t!==r?(n.each(function(n){n[e]&&n[e](t)}),n):(i=n[0],i&&i[e]?i[e]():void 0)},exec:function(t){var n=this,r=e.toArray(arguments).slice(1);return n.each(function(e){e[t]&&e[t].apply(e,r)}),n},remove:function(){for(var e=this.length;e--;)this[e].remove();return this},addClass:function(e){return this.each(function(t){t.classes.add(e)})},removeClass:function(e){return this.each(function(t){t.classes.remove(e)})}},e.each("fire on off show hide append prepend before after reflow".split(" "),function(t){i[t]=function(){var n=e.toArray(arguments);return this.each(function(e){t in e&&e[t].apply(e,n)}),this}}),e.each("text name disabled active selected checked visible parent value data".split(" "),function(e){i[e]=function(t){return this.prop(e,t)}}),r=n.extend(i),t.Collection=r,r}),r(pe,[d,m,w],function(e,t,n){var r=0,i={id:function(){return"mceu_"+r++},create:function(e,r,i){var o=document.createElement(e);return n.DOM.setAttribs(o,r),"string"==typeof i?o.innerHTML=i:t.each(i,function(e){e.nodeType&&o.appendChild(e)}),o},createFragment:function(e){return n.DOM.createFragment(e)},getWindowSize:function(){return n.DOM.getViewPort()},getSize:function(e){var t,n;if(e.getBoundingClientRect){var r=e.getBoundingClientRect();t=Math.max(r.width||r.right-r.left,e.offsetWidth),n=Math.max(r.height||r.bottom-r.bottom,e.offsetHeight)}else t=e.offsetWidth,n=e.offsetHeight;return{width:t,height:n}},getPos:function(e,t){return n.DOM.getPos(e,t||i.getContainer())},getContainer:function(){return e.container?e.container:document.body},getViewPort:function(e){return n.DOM.getViewPort(e)},get:function(e){return document.getElementById(e)},addClass:function(e,t){return n.DOM.addClass(e,t)},removeClass:function(e,t){return n.DOM.removeClass(e,t)},hasClass:function(e,t){return n.DOM.hasClass(e,t)},toggleClass:function(e,t,r){return n.DOM.toggleClass(e,t,r)},css:function(e,t,r){return n.DOM.setStyle(e,t,r)},getRuntimeStyle:function(e,t){return n.DOM.getStyle(e,t,!0)},on:function(e,t,r,i){return n.DOM.bind(e,t,r,i)},off:function(e,t,r){return n.DOM.unbind(e,t,r)},fire:function(e,t,r){return n.DOM.fire(e,t,r)},innerHtml:function(e,t){n.DOM.setHTML(e,t)}};return i}),r(me,[],function(){return{parseBox:function(e){var t,n=10;if(e)return"number"==typeof e?(e=e||0,{top:e,left:e,bottom:e,right:e}):(e=e.split(" "),t=e.length,1===t?e[1]=e[2]=e[3]=e[0]:2===t?(e[2]=e[0],e[3]=e[1]):3===t&&(e[3]=e[1]),{top:parseInt(e[0],n)||0,right:parseInt(e[1],n)||0,bottom:parseInt(e[2],n)||0,left:parseInt(e[3],n)||0})},measureBox:function(e,t){function n(t){var n=document.defaultView;return n?(t=t.replace(/[A-Z]/g,function(e){return"-"+e}),n.getComputedStyle(e,null).getPropertyValue(t)):e.currentStyle[t]}function r(e){var t=parseFloat(n(e),10);return isNaN(t)?0:t}return{top:r(t+"TopWidth"),right:r(t+"RightWidth"),bottom:r(t+"BottomWidth"),left:r(t+"LeftWidth")}}}}),r(ge,[m],function(e){function t(){}function n(e){this.cls=[],this.cls._map={},this.onchange=e||t,this.prefix=""}return e.extend(n.prototype,{add:function(e){return e&&!this.contains(e)&&(this.cls._map[e]=!0,this.cls.push(e),this._change()),this},remove:function(e){if(this.contains(e)){for(var t=0;t0&&(e+=" "),e+=this.prefix+this.cls[t];return e},n}),r(ve,[u],function(e){var t={},n;return{add:function(r){var i=r.parent();if(i){if(!i._layout||i._layout.isNative())return;t[i._id]||(t[i._id]=i),n||(n=!0,e.requestAnimationFrame(function(){var e,r;n=!1;for(e in t)r=t[e],r.state.get("rendered")&&r.reflow();t={}},document.body))}},remove:function(e){t[e._id]&&delete t[e._id]}}}),r(ye,[se,m,le,de,he,pe,g,me,ge,ve],function(e,t,n,r,i,o,a,s,l,c){function u(e){return e._eventDispatcher||(e._eventDispatcher=new n({scope:e,toggleEvent:function(t,r){r&&n.isNative(t)&&(e._nativeEvents||(e._nativeEvents={}),e._nativeEvents[t]=!0,e.state.get("rendered")&&d(e))}})),e._eventDispatcher}function d(e){function t(t){var n=e.getParentCtrl(t.target);n&&n.fire(t.type,t)}function n(){var e=c._lastHoverCtrl;e&&(e.fire("mouseleave",{target:e.getEl()}),e.parents().each(function(e){e.fire("mouseleave",{target:e.getEl()})}),c._lastHoverCtrl=null)}function r(t){var n=e.getParentCtrl(t.target),r=c._lastHoverCtrl,i=0,o,a,s;if(n!==r){if(c._lastHoverCtrl=n,a=n.parents().toArray().reverse(),a.push(n),r){for(s=r.parents().toArray().reverse(),s.push(r),i=0;i=i;o--)r=s[o],r.fire("mouseleave",{target:r.getEl()})}for(o=i;oo;o++)c=l[o]._eventsRoot;for(c||(c=l[l.length-1]||e),e._eventsRoot=c,s=o,o=0;s>o;o++)l[o]._eventsRoot=c;var p=c._delegates;p||(p=c._delegates={});for(d in u){if(!u)return!1;"wheel"!==d||h?("mouseenter"===d||"mouseleave"===d?c._hasMouseEnter||(a(c.getEl()).on("mouseleave",n).on("mouseover",r),c._hasMouseEnter=1):p[d]||(a(c.getEl()).on(d,t),p[d]=!0),u[d]=!1):f?a(e.getEl()).on("mousewheel",i):a(e.getEl()).on("DOMMouseScroll",i)}}}var f="onmousewheel"in document,h=!1,p="mce-",m,g=0,v={Statics:{classPrefix:p},isRtl:function(){return m.rtl},classPrefix:p,init:function(e){function n(e){var t;for(e=e.split(" "),t=0;tn.maxW?n.maxW:i,n.w=i,n.innerW=i-o),i=e.h,i!==s&&(i=in.maxH?n.maxH:i,n.h=i,n.innerH=i-a),i=e.innerW,i!==s&&(i=in.maxW-o?n.maxW-o:i,n.innerW=i,n.w=i+o),i=e.innerH,i!==s&&(i=in.maxH-a?n.maxH-a:i,n.innerH=i,n.h=i+a),e.contentW!==s&&(n.contentW=e.contentW),e.contentH!==s&&(n.contentH=e.contentH),r=t._lastLayoutRect,r.x===n.x&&r.y===n.y&&r.w===n.w&&r.h===n.h||(l=m.repaintControls,l&&l.map&&!l.map[t._id]&&(l.push(t),l.map[t._id]=!0),r.x=n.x,r.y=n.y,r.w=n.w,r.h=n.h),t):n},repaint:function(){var e=this,t,n,r,i,o,a,s,l,c,u;c=document.createRange?function(e){return e}:Math.round,t=e.getEl().style,i=e._layoutRect,l=e._lastRepaintRect||{},o=e.borderBox,a=o.left+o.right,s=o.top+o.bottom,i.x!==l.x&&(t.left=c(i.x)+"px",l.x=i.x),i.y!==l.y&&(t.top=c(i.y)+"px",l.y=i.y),i.w!==l.w&&(u=c(i.w-a),t.width=(u>=0?u:0)+"px",l.w=i.w),i.h!==l.h&&(u=c(i.h-s),t.height=(u>=0?u:0)+"px",l.h=i.h),e._hasBody&&i.innerW!==l.innerW&&(u=c(i.innerW),r=e.getEl("body"),r&&(n=r.style,n.width=(u>=0?u:0)+"px"),l.innerW=i.innerW),e._hasBody&&i.innerH!==l.innerH&&(u=c(i.innerH),r=r||e.getEl("body"),r&&(n=n||r.style,n.height=(u>=0?u:0)+"px"),l.innerH=i.innerH),e._lastRepaintRect=l,e.fire("repaint",{},!1)},updateLayoutRect:function(){var e=this;e.parent()._lastRect=null,o.css(e.getEl(),{width:"",height:""}),e._layoutRect=e._lastRepaintRect=e._lastLayoutRect=null,e.initLayoutRect()},on:function(e,t){function n(e){var t,n;return"string"!=typeof e?e:function(i){return t||r.parentsAndSelf().each(function(r){var i=r.settings.callbacks;return i&&(t=i[e])?(n=r,!1):void 0}),t?t.call(n,i):(i.action=e,void this.fire("execute",i))}}var r=this;return u(r).on(e,n(t)),r},off:function(e,t){return u(this).off(e,t),this},fire:function(e,t,n){var r=this;if(t=t||{},t.control||(t.control=r),t=u(r).fire(e,t),n!==!1&&r.parent)for(var i=r.parent();i&&!t.isPropagationStopped();)i.fire(e,t,!1),i=i.parent();return t},hasEventListeners:function(e){return u(this).has(e)},parents:function(e){var t=this,n,r=new i;for(n=t.parent();n;n=n.parent())r.add(n);return e&&(r=r.filter(e)),r},parentsAndSelf:function(e){return new i(this).add(this.parents(e))},next:function(){var e=this.parent().items();return e[e.indexOf(this)+1]},prev:function(){var e=this.parent().items();return e[e.indexOf(this)-1]},innerHtml:function(e){return this.$el.html(e),this},getEl:function(e){var t=e?this._id+"-"+e:this._id;return this._elmCache[t]||(this._elmCache[t]=a("#"+t)[0]),this._elmCache[t]},show:function(){return this.visible(!0)},hide:function(){return this.visible(!1)},focus:function(){try{this.getEl().focus()}catch(e){}return this},blur:function(){return this.getEl().blur(),this},aria:function(e,t){var n=this,r=n.getEl(n.ariaTarget);return"undefined"==typeof t?n._aria[e]:(n._aria[e]=t,n.state.get("rendered")&&r.setAttribute("role"==e?e:"aria-"+e,t),n)},encode:function(e,t){return t!==!1&&(e=this.translate(e)),(e||"").replace(/[&<>"]/g,function(e){return"&#"+e.charCodeAt(0)+";"})},translate:function(e){return m.translate?m.translate(e):e},before:function(e){var t=this,n=t.parent();return n&&n.insert(e,n.items().indexOf(t),!0),t},after:function(e){var t=this,n=t.parent();return n&&n.insert(e,n.items().indexOf(t)),t},remove:function(){var e=this,t=e.getEl(),n=e.parent(),r,i; +if(e.items){var o=e.items().toArray();for(i=o.length;i--;)o[i].remove()}n&&n.items&&(r=[],n.items().each(function(t){t!==e&&r.push(t)}),n.items().set(r),n._lastRect=null),e._eventsRoot&&e._eventsRoot==e&&a(t).off();var s=e.getRoot().controlIdLookup;return s&&delete s[e._id],t&&t.parentNode&&t.parentNode.removeChild(t),e.state.set("rendered",!1),e.state.destroy(),e.fire("remove"),e},renderBefore:function(e){return a(e).before(this.renderHtml()),this.postRender(),this},renderTo:function(e){return a(e||this.getContainerElm()).append(this.renderHtml()),this.postRender(),this},preRender:function(){},render:function(){},renderHtml:function(){return'
    '},postRender:function(){var e=this,t=e.settings,n,r,i,o,s;e.$el=a(e.getEl()),e.state.set("rendered",!0);for(o in t)0===o.indexOf("on")&&e.on(o.substr(2),t[o]);if(e._eventsRoot){for(i=e.parent();!s&&i;i=i.parent())s=i._eventsRoot;if(s)for(o in s._nativeEvents)e._nativeEvents[o]=!0}d(e),t.style&&(n=e.getEl(),n&&(n.setAttribute("style",t.style),n.style.cssText=t.style)),e.settings.border&&(r=e.borderBox,e.$el.css({"border-top-width":r.top,"border-right-width":r.right,"border-bottom-width":r.bottom,"border-left-width":r.left}));var l=e.getRoot();l.controlIdLookup||(l.controlIdLookup={}),l.controlIdLookup[e._id]=e;for(var u in e._aria)e.aria(u,e._aria[u]);e.state.get("visible")===!1&&(e.getEl().style.display="none"),e.bindStates(),e.state.on("change:visible",function(t){var n=t.value,r;e.state.get("rendered")&&(e.getEl().style.display=n===!1?"none":"",e.getEl().getBoundingClientRect()),r=e.parent(),r&&(r._lastRect=null),e.fire(n?"show":"hide"),c.add(e)}),e.fire("postrender",{},!1)},bindStates:function(){},scrollIntoView:function(e){function t(e,t){var n,r,i=e;for(n=r=0;i&&i!=t&&i.nodeType;)n+=i.offsetLeft||0,r+=i.offsetTop||0,i=i.offsetParent;return{x:n,y:r}}var n=this.getEl(),r=n.parentNode,i,o,a,s,l,c,u=t(n,r);return i=u.x,o=u.y,a=n.offsetWidth,s=n.offsetHeight,l=r.clientWidth,c=r.clientHeight,"end"==e?(i-=l-a,o-=c-s):"center"==e&&(i-=l/2-a/2,o-=c/2-s/2),r.scrollLeft=i,r.scrollTop=o,this},getRoot:function(){for(var e=this,t,n=[];e;){if(e.rootControl){t=e.rootControl;break}n.push(e),t=e,e=e.parent()}t||(t=this);for(var r=n.length;r--;)n[r].rootControl=t;return t},reflow:function(){c.remove(this);var e=this.parent();return e._layout&&!e._layout.isNative()&&e.reflow(),this}};return t.each("text title visible disabled active value".split(" "),function(e){v[e]=function(t){return 0===arguments.length?this.state.get(e):("undefined"!=typeof t&&this.state.set(e,t),this)}}),m=e.extend(v)}),r(be,[],function(){var e={},t;return{add:function(t,n){e[t.toLowerCase()]=n},has:function(t){return!!e[t.toLowerCase()]},create:function(n,r){var i,o,a;if(!t){a=tinymce.ui;for(o in a)e[o.toLowerCase()]=a[o];t=!0}if("string"==typeof n?(r=r||{},r.type=n):(r=n,n=r.type),n=n.toLowerCase(),i=e[n],!i)throw new Error("Could not find control by type: "+n);return i=new i(r),i.type=n,i}}}),r(Ce,[],function(){return function(e){function t(e){return e&&1===e.nodeType}function n(e){return e=e||C,t(e)?e.getAttribute("role"):null}function r(e){for(var t,r=e||C;r=r.parentNode;)if(t=n(r))return t}function i(e){var n=C;return t(n)?n.getAttribute("aria-"+e):void 0}function o(e){var t=e.tagName.toUpperCase();return"INPUT"==t||"TEXTAREA"==t||"SELECT"==t}function a(e){return o(e)&&!e.hidden?!0:!!/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell|slider)$/.test(n(e))}function s(e){function t(e){if(1==e.nodeType&&"none"!=e.style.display){a(e)&&n.push(e);for(var r=0;re?e=t.length-1:e>=t.length&&(e=0),t[e]&&t[e].focus(),e}function d(e,t){var n=-1,r=l();t=t||s(r.getEl());for(var i=0;i=0&&(n=t.getEl(),n&&n.parentNode.removeChild(n),n=e.getEl(),n&&n.parentNode.removeChild(n)),t.parent(this)},create:function(t){var n=this,i,a=[];return o.isArray(t)||(t=[t]),o.each(t,function(t){t&&(t instanceof e||("string"==typeof t&&(t={type:t}),i=o.extend({},n.settings.defaults,t),t.type=i.type=i.type||t.type||n.settings.defaultType||(i.defaults?i.defaults.type:null),t=r.create(i)),a.push(t))}),a},renderNew:function(){var e=this;return e.items().each(function(t,n){var r;t.parent(e),t.state.get("rendered")||(r=e.getEl("body"),r.hasChildNodes()&&n<=r.childNodes.length-1?a(r.childNodes[n]).before(t.renderHtml()):a(r).append(t.renderHtml()),t.postRender(),l.add(t))}),e._layout.applyClasses(e.items().filter(":visible")),e._lastRect=null,e},append:function(e){return this.add(e).renderNew()},prepend:function(e){var t=this;return t.items().set(t.create(e).concat(t.items().toArray())),t.renderNew()},insert:function(e,t,n){var r=this,i,o,a;return e=r.create(e),i=r.items(),!n&&t=0&&t
    '+(e.settings.html||"")+t.renderHtml(e)+"
    "},postRender:function(){var e=this,t;return e.items().exec("postRender"),e._super(),e._layout.postRender(e),e.state.set("rendered",!0),e.settings.style&&e.$el.css(e.settings.style),e.settings.border&&(t=e.borderBox,e.$el.css({"border-top-width":t.top,"border-right-width":t.right,"border-bottom-width":t.bottom,"border-left-width":t.left})),e.parent()||(e.keyboardNav=new i({root:e})),e},initLayoutRect:function(){var e=this,t=e._super();return e._layout.recalc(e),t},recalc:function(){var e=this,t=e._layoutRect,n=e._lastRect;return n&&n.w==t.w&&n.h==t.h?void 0:(e._layout.recalc(e),t=e.layoutRect(),e._lastRect={x:t.x,y:t.y,w:t.w,h:t.h},!0)},reflow:function(){var t;if(l.remove(this),this.visible()){for(e.repaintControls=[],e.repaintControls.map={},this.recalc(),t=e.repaintControls.length;t--;)e.repaintControls[t].repaint();"flow"!==this.settings.layout&&"stack"!==this.settings.layout&&this.repaint(),e.repaintControls=[]}return this}})}),r(we,[g],function(e){function t(e){var t,n,r,i,o,a,s,l,c=Math.max;return t=e.documentElement,n=e.body,r=c(t.scrollWidth,n.scrollWidth),i=c(t.clientWidth,n.clientWidth),o=c(t.offsetWidth,n.offsetWidth),a=c(t.scrollHeight,n.scrollHeight),s=c(t.clientHeight,n.clientHeight),l=c(t.offsetHeight,n.offsetHeight),{width:o>r?i:r,height:l>a?s:a}}function n(e){var t,n;if(e.changedTouches)for(t="screenX screenY pageX pageY clientX clientY".split(" "),n=0;n").css({position:"absolute",top:0,left:0,width:c.width,height:c.height,zIndex:2147483647,opacity:1e-4,cursor:m}).appendTo(s.body),e(s).on("mousemove touchmove",d).on("mouseup touchend",u),i.start(r)},d=function(e){return n(e),e.button!==l?u(e):(e.deltaX=e.screenX-f,e.deltaY=e.screenY-h,e.preventDefault(),void i.drag(e))},u=function(t){n(t),e(s).off("mousemove touchmove",d).off("mouseup touchend",u),a.remove(),i.stop&&i.stop(t)},this.destroy=function(){e(o()).off()},e(o()).on("mousedown touchstart",c)}}),r(Ne,[g,we],function(e,t){return{init:function(){var e=this;e.on("repaint",e.renderScroll)},renderScroll:function(){function n(){function t(t,a,s,l,c,u){var d,f,h,p,m,g,v,y,b;if(f=i.getEl("scroll"+t)){if(y=a.toLowerCase(),b=s.toLowerCase(),e(i.getEl("absend")).css(y,i.layoutRect()[l]-1),!c)return void e(f).css("display","none");e(f).css("display","block"),d=i.getEl("body"),h=i.getEl("scroll"+t+"t"),p=d["client"+s]-2*o,p-=n&&r?f["client"+u]:0,m=d["scroll"+s],g=p/m,v={},v[y]=d["offset"+a]+o,v[b]=p,e(f).css(v),v={},v[y]=d["scroll"+a]*g,v[b]=p*g,e(h).css(v)}}var n,r,a;a=i.getEl("body"),n=a.scrollWidth>a.clientWidth,r=a.scrollHeight>a.clientHeight,t("h","Left","Width","contentW",n,"Height"),t("v","Top","Height","contentH",r,"Width")}function r(){function n(n,r,a,s,l){var c,u=i._id+"-scroll"+n,d=i.classPrefix;e(i.getEl()).append('
    '),i.draghelper=new t(u+"t",{start:function(){c=i.getEl("body")["scroll"+r],e("#"+u).addClass(d+"active")},drag:function(e){var t,u,d,f,h=i.layoutRect();u=h.contentW>h.innerW,d=h.contentH>h.innerH,f=i.getEl("body")["client"+a]-2*o,f-=u&&d?i.getEl("scroll"+n)["client"+l]:0,t=f/i.getEl("body")["scroll"+a],i.getEl("body")["scroll"+r]=c+e["delta"+s]/t},stop:function(){e("#"+u).removeClass(d+"active")}})}i.classes.add("scroll"),n("v","Top","Height","Y","Width"),n("h","Left","Width","X","Height")}var i=this,o=2;i.settings.autoScroll&&(i._hasScroll||(i._hasScroll=!0,r(),i.on("wheel",function(e){var t=i.getEl("body");t.scrollLeft+=10*(e.deltaX||0),t.scrollTop+=10*e.deltaY,n()}),e(i.getEl("body")).on("scroll",n)),n())}}}),r(Ee,[xe,Ne],function(e,t){return e.extend({Defaults:{layout:"fit",containerCls:"panel"},Mixins:[t],renderHtml:function(){var e=this,t=e._layout,n=e.settings.html;return e.preRender(),t.preRender(e),"undefined"==typeof n?n='
    '+t.renderHtml(e)+"
    ":("function"==typeof n&&(n=n.call(e)),e._hasBody=!1),'
    '+(e._preBodyHtml||"")+n+"
    "}})}),r(_e,[pe],function(e){function t(t,n,r){var i,o,a,s,l,c,u,d,f,h;return f=e.getViewPort(),o=e.getPos(n),a=o.x,s=o.y,t.state.get("fixed")&&"static"==e.getRuntimeStyle(document.body,"position")&&(a-=f.x,s-=f.y),i=t.getEl(),h=e.getSize(i),l=h.width,c=h.height,h=e.getSize(n),u=h.width,d=h.height,r=(r||"").split(""),"b"===r[0]&&(s+=d),"r"===r[1]&&(a+=u),"c"===r[0]&&(s+=Math.round(d/2)),"c"===r[1]&&(a+=Math.round(u/2)),"b"===r[3]&&(s-=c),"r"===r[4]&&(a-=l),"c"===r[3]&&(s-=Math.round(c/2)),"c"===r[4]&&(a-=Math.round(l/2)),{x:a,y:s,w:l,h:c}}return{testMoveRel:function(n,r){for(var i=e.getViewPort(),o=0;o0&&a.x+a.w0&&a.y+a.hi.x&&a.x+a.wi.y&&a.y+a.he?0:e+n>t?(e=t-n,0>e?0:e):e}var i=this;if(i.settings.constrainToViewport){var o=e.getViewPort(window),a=i.layoutRect();t=r(t,o.w+o.x,a.w),n=r(n,o.h+o.y,a.h)}return i.state.get("rendered")?i.layoutRect({x:t,y:n}).repaint():(i.settings.x=t,i.settings.y=n),i.fire("move",{x:t,y:n}),i}}}),r(Se,[pe],function(e){return{resizeToContent:function(){this._layoutRect.autoResize=!0,this._lastRect=null,this.reflow()},resizeTo:function(t,n){if(1>=t||1>=n){var r=e.getWindowSize();t=1>=t?t*r.w:t,n=1>=n?n*r.h:n}return this._layoutRect.autoResize=!1,this.layoutRect({minW:t,minH:n,w:t,h:n}).reflow()},resizeBy:function(e,t){var n=this,r=n.layoutRect();return n.resizeTo(r.w+e,r.h+t)}}}),r(ke,[Ee,_e,Se,pe,g,u],function(e,t,n,r,i,o){function a(e,t){for(;e;){if(e==t)return!0;e=e.parent()}}function s(e){for(var t=v.length;t--;){var n=v[t],r=n.getParentCtrl(e.target);if(n.settings.autohide){if(r&&(a(r,n)||n.parent()===r))continue;e=n.fire("autohide",{target:e.target}),e.isDefaultPrevented()||n.hide()}}}function l(){p||(p=function(e){2!=e.button&&s(e)},i(document).on("click touchstart",p))}function c(){m||(m=function(){var e;for(e=v.length;e--;)d(v[e])},i(window).on("scroll",m))}function u(){if(!g){var e=document.documentElement,t=e.clientWidth,n=e.clientHeight;g=function(){document.all&&t==e.clientWidth&&n==e.clientHeight||(t=e.clientWidth,n=e.clientHeight,C.hideAll())},i(window).on("resize",g)}}function d(e){function t(t,n){for(var r,i=0;in&&(e.fixed(!1).layoutRect({y:e._autoFixY}).repaint(),t(!1,e._autoFixY-n)):(e._autoFixY=e.layoutRect().y,e._autoFixY
    ').appendTo(t.getContainerElm())),o.setTimeout(function(){n.addClass(r+"in"),i(t.getEl()).addClass(r+"in")}),b=!0),f(!0,t)}}),t.on("show",function(){t.parents().each(function(e){return e.state.get("fixed")?(t.fixed(!0),!1):void 0})}),e.popover&&(t._preBodyHtml='
    ',t.classes.add("popover").add("bottom").add(t.isRtl()?"end":"start")),t.aria("label",e.ariaLabel),t.aria("labelledby",t._id),t.aria("describedby",t.describedBy||t._id+"-none")},fixed:function(e){var t=this;if(t.state.get("fixed")!=e){if(t.state.get("rendered")){var n=r.getViewPort();e?t.layoutRect().y-=n.y:t.layoutRect().y+=n.y}t.classes.toggle("fixed",e),t.state.set("fixed",e)}return t},show:function(){var e=this,t,n=e._super();for(t=v.length;t--&&v[t]!==e;);return-1===t&&v.push(e),n},hide:function(){return h(this),f(!1,this),this._super()},hideAll:function(){C.hideAll()},close:function(){var e=this;return e.fire("close").isDefaultPrevented()||(e.remove(),f(!1,e)),e},remove:function(){h(this),this._super()},postRender:function(){var e=this;return e.settings.bodyRole&&this.getEl("body").setAttribute("role",e.settings.bodyRole),e._super()}});return C.hideAll=function(){for(var e=v.length;e--;){var t=v[e];t&&t.settings.autohide&&(t.hide(),v.splice(e,1))}},C}),r(Te,[ke,Ee,pe,g,we,me,d,u],function(e,t,n,r,i,o,a,s){function l(e){var t="width=device-width,initial-scale=1.0,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0",n=r("meta[name=viewport]")[0],i;a.overrideViewPort!==!1&&(n||(n=document.createElement("meta"),n.setAttribute("name","viewport"),document.getElementsByTagName("head")[0].appendChild(n)),i=n.getAttribute("content"),i&&"undefined"!=typeof f&&(f=i),n.setAttribute("content",e?t:f))}function c(e){for(var t=0;tr.w&&(o=r.x-Math.max(0,i/2),e.layoutRect({w:i,x:o}),a=!0)),t&&(t.layoutRect({w:e.layoutRect().innerW}).recalc(),i=t.layoutRect().minW+r.deltaW,i>r.w&&(o=r.x-Math.max(0,i-r.w),e.layoutRect({w:i,x:o}),a=!0)),a&&e.recalc()},initLayoutRect:function(){var e=this,t=e._super(),r=0,i;if(e.settings.title&&!e._fullscreen){i=e.getEl("head");var o=n.getSize(i);t.headerW=o.width,t.headerH=o.height,r+=t.headerH}e.statusbar&&(r+=e.statusbar.layoutRect().h),t.deltaH+=r,t.minH+=r,t.h+=r;var a=n.getWindowSize();return t.x=e.settings.x||Math.max(0,a.w/2-t.w/2),t.y=e.settings.y||Math.max(0,a.h/2-t.h/2),t},renderHtml:function(){var e=this,t=e._layout,n=e._id,r=e.classPrefix,i=e.settings,o="",a="",s=i.html;return e.preRender(),t.preRender(e),i.title&&(o='
    '+e.encode(i.title)+'
    '),i.url&&(s=''),"undefined"==typeof s&&(s=t.renderHtml(e)),e.statusbar&&(a=e.statusbar.renderHtml()),'
    '+o+'
    '+s+"
    "+a+"
    "},fullscreen:function(e){var t=this,i=document.documentElement,a,l=t.classPrefix,c;if(e!=t._fullscreen)if(r(window).on("resize",function(){var e;if(t._fullscreen)if(a)t._timer||(t._timer=s.setTimeout(function(){var e=n.getWindowSize();t.moveTo(0,0).resizeTo(e.w,e.h),t._timer=0},50));else{e=(new Date).getTime();var r=n.getWindowSize();t.moveTo(0,0).resizeTo(r.w,r.h),(new Date).getTime()-e>50&&(a=!0)}}),c=t.layoutRect(),t._fullscreen=e,e){t._initial={x:c.x,y:c.y,w:c.w,h:c.h},t.borderBox=o.parseBox("0"),t.getEl("head").style.display="none",c.deltaH-=c.headerH+2,r([i,document.body]).addClass(l+"fullscreen"),t.classes.add("fullscreen");var u=n.getWindowSize();t.moveTo(0,0).resizeTo(u.w,u.h)}else t.borderBox=o.parseBox(t.settings.border),t.getEl("head").style.display="",c.deltaH+=c.headerH,r([i,document.body]).removeClass(l+"fullscreen"),t.classes.remove("fullscreen"),t.moveTo(t._initial.x,t._initial.y).resizeTo(t._initial.w,t._initial.h);return t.reflow()},postRender:function(){var e=this,t;setTimeout(function(){e.classes.add("in"),e.fire("open")},0),e._super(),e.statusbar&&e.statusbar.postRender(),e.focus(),this.dragHelper=new i(e._id+"-dragh",{start:function(){t={x:e.layoutRect().x,y:e.layoutRect().y}},drag:function(n){e.moveTo(t.x+n.deltaX,t.y+n.deltaY)}}),e.on("submit",function(t){t.isDefaultPrevented()||e.close()}),d.push(e),l(!0)},submit:function(){return this.fire("submit",{data:this.toJSON()})},remove:function(){var e=this,t;for(e.dragHelper.destroy(),e._super(),e.statusbar&&this.statusbar.remove(),t=d.length;t--;)d[t]===e&&d.splice(t,1);l(d.length>0),c(e.classPrefix)},getContentWindow:function(){var e=this.getEl().getElementsByTagName("iframe")[0];return e?e.contentWindow:null}});return u(),h}),r(Re,[Te],function(e){var t=e.extend({init:function(e){e={border:1,padding:20,layout:"flex",pack:"center",align:"center",containerCls:"panel",autoScroll:!0,buttons:{type:"button",text:"Ok",action:"ok"},items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200}},this._super(e)},Statics:{OK:1,OK_CANCEL:2,YES_NO:3,YES_NO_CANCEL:4,msgBox:function(n){function r(e,t,n){return{type:"button",text:e,subtype:n?"primary":"",onClick:function(e){e.control.parents()[1].close(),o(t)}}}var i,o=n.callback||function(){};switch(n.buttons){case t.OK_CANCEL:i=[r("Ok",!0,!0),r("Cancel",!1)];break;case t.YES_NO:case t.YES_NO_CANCEL:i=[r("Yes",1,!0),r("No",0)],n.buttons==t.YES_NO_CANCEL&&i.push(r("Cancel",-1));break;default:i=[r("Ok",!0,!0)]}return new e({padding:20,x:n.x,y:n.y,minWidth:300,minHeight:100,layout:"flex",pack:"center",align:"center",buttons:i,title:n.title,role:"alertdialog",items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200,text:n.text},onPostRender:function(){this.aria("describedby",this.items()[0]._id)},onClose:n.onClose,onCancel:function(){o(!1)}}).renderTo(document.body).reflow()},alert:function(e,n){return"string"==typeof e&&(e={text:e}),e.callback=n,t.msgBox(e)},confirm:function(e,n){return"string"==typeof e&&(e={text:e}),e.callback=n,e.buttons=t.OK_CANCEL,t.msgBox(e)}}});return t}),r(Ae,[Te,Re],function(e,t){return function(n){function r(){return s.length?s[s.length-1]:void 0}function i(e){n.fire("OpenWindow",{win:e})}function o(e){n.fire("CloseWindow",{win:e})}var a=this,s=[];a.windows=s,n.on("remove",function(){for(var e=s.length;e--;)s[e].close()}),a.open=function(t,r){var a;return n.editorManager.setActive(n),t.title=t.title||" ",t.url=t.url||t.file,t.url&&(t.width=parseInt(t.width||320,10),t.height=parseInt(t.height||240,10)),t.body&&(t.items={defaults:t.defaults,type:t.bodyType||"form",items:t.body,data:t.data,callbacks:t.commands}),t.url||t.buttons||(t.buttons=[{text:"Ok",subtype:"primary",onclick:function(){a.find("form")[0].submit()}},{text:"Cancel",onclick:function(){a.close()}}]),a=new e(t),s.push(a),a.on("close",function(){for(var e=s.length;e--;)s[e]===a&&s.splice(e,1);s.length||n.focus(),o(a)}),t.data&&a.on("postRender",function(){this.find("*").each(function(e){var n=e.name();n in t.data&&e.value(t.data[n])})}),a.features=t||{},a.params=r||{},1===s.length&&n.nodeChanged(),a=a.renderTo().reflow(),i(a),a},a.alert=function(e,r,a){var s;s=t.alert(e,function(){r?r.call(a||this):n.focus()}),s.on("close",function(){o(s)}),i(s)},a.confirm=function(e,n,r){var a;a=t.confirm(e,function(e){n.call(r||this,e)}),a.on("close",function(){o(a)}),i(a)},a.close=function(){r()&&r().close()},a.getParams=function(){return r()?r().params:null},a.setParams=function(e){r()&&(r().params=e)},a.getWindows=function(){return s}}}),r(Be,[ye,_e],function(e,t){return e.extend({Mixins:[t],Defaults:{classes:"widget tooltip tooltip-n"},renderHtml:function(){var e=this,t=e.classPrefix;return'"},bindStates:function(){var e=this;return e.state.on("change:text",function(t){e.getEl().lastChild.innerHTML=e.encode(t.value)}),e._super()},repaint:function(){var e=this,t,n;t=e.getEl().style,n=e._layoutRect,t.left=n.x+"px",t.top=n.y+"px",t.zIndex=131070}})}),r(De,[ye,Be],function(e,t){var n,r=e.extend({init:function(e){var t=this;t._super(e),e=t.settings,t.canFocus=!0,e.tooltip&&r.tooltips!==!1&&(t.on("mouseenter",function(n){var r=t.tooltip().moveTo(-65535);if(n.control==t){var i=r.text(e.tooltip).show().testMoveRel(t.getEl(),["bc-tc","bc-tl","bc-tr"]);r.classes.toggle("tooltip-n","bc-tc"==i),r.classes.toggle("tooltip-nw","bc-tl"==i),r.classes.toggle("tooltip-ne","bc-tr"==i),r.moveRel(t.getEl(),i)}else r.hide()}),t.on("mouseleave mousedown click",function(){t.tooltip().hide()})),t.aria("label",e.ariaLabel||e.tooltip)},tooltip:function(){return n||(n=new t({type:"tooltip"}),n.renderTo()),n},postRender:function(){var e=this,t=e.settings;e._super(),e.parent()||!t.width&&!t.height||(e.initLayoutRect(),e.repaint()),t.autofocus&&e.focus()},bindStates:function(){function e(e){n.aria("disabled",e),n.classes.toggle("disabled",e)}function t(e){n.aria("pressed",e),n.classes.toggle("active",e)}var n=this;return n.state.on("change:disabled",function(t){e(t.value)}),n.state.on("change:active",function(e){t(e.value)}),n.state.get("disabled")&&e(!0),n.state.get("active")&&t(!0),n._super()},remove:function(){this._super(),n&&(n.remove(),n=null)}});return r}),r(Le,[De],function(e){return e.extend({Defaults:{value:0},init:function(e){var t=this;t._super(e),t.classes.add("progress"),t.settings.filter||(t.settings.filter=function(e){return Math.round(e)})},renderHtml:function(){var e=this,t=e._id,n=this.classPrefix;return'
    0%
    '},postRender:function(){var e=this;return e._super(),e.value(e.settings.value),e},bindStates:function(){function e(e){e=t.settings.filter(e),t.getEl().lastChild.innerHTML=e+"%",t.getEl().firstChild.firstChild.style.width=e+"%"}var t=this;return t.state.on("change:value",function(t){e(t.value)}),e(t.state.get("value")),t._super()}})}),r(Me,[ye,_e,Le,u],function(e,t,n,r){return e.extend({Mixins:[t],Defaults:{classes:"widget notification"},init:function(e){var t=this;t._super(e),e.text&&t.text(e.text),e.icon&&(t.icon=e.icon),e.color&&(t.color=e.color),e.type&&t.classes.add("notification-"+e.type),e.timeout&&(e.timeout<0||e.timeout>0)&&!e.closeButton?t.closeButton=!1:(t.classes.add("has-close"),t.closeButton=!0),e.progressBar&&(t.progressBar=new n),t.on("click",function(e){-1!=e.target.className.indexOf(t.classPrefix+"close")&&t.close()})},renderHtml:function(){var e=this,t=e.classPrefix,n="",r="",i="",o="";return e.icon&&(n=''),e.color&&(o=' style="background-color: '+e.color+'"'),e.closeButton&&(r=''),e.progressBar&&(i=e.progressBar.renderHtml()),'"},postRender:function(){var e=this;return r.setTimeout(function(){e.$el.addClass(e.classPrefix+"in")}),e._super()},bindStates:function(){var e=this;return e.state.on("change:text",function(t){e.getEl().childNodes[1].innerHTML=t.value}),e.progressBar&&e.progressBar.bindStates(),e._super()},close:function(){var e=this;return e.fire("close").isDefaultPrevented()||e.remove(),e},repaint:function(){var e=this,t,n;t=e.getEl().style,n=e._layoutRect,t.left=n.x+"px",t.top=n.y+"px",t.zIndex=131070}})}),r(Pe,[Me,u],function(e,t){return function(n){function r(){return l.length?l[l.length-1]:void 0}function i(){t.requestAnimationFrame(function(){o(),a()})}function o(){for(var e=0;e0){var e=l.slice(0,1)[0],t=n.inline?n.getElement():n.getContentAreaContainer();if(e.moveRel(t,"tc-tc"),l.length>1)for(var r=1;r0&&(r.timer=setTimeout(function(){r.close()},t.timeout)),r.on("close",function(){var e=l.length;for(r.timer&&n.getWin().clearTimeout(r.timer);e--;)l[e]===r&&l.splice(e,1);a()}),r.renderTo(),a(),r},s.close=function(){r()&&r().close()},s.getNotifications=function(){return l},n.on("SkinLoaded",function(){var e=n.settings.service_message;e&&n.notificationManager.open({text:e,type:"warning",timeout:0,icon:""})})}}),r(Oe,[w],function(e){function t(t,n,r){for(var i=[];n&&n!=t;n=n.parentNode)i.push(e.nodeIndex(n,r));return i}function n(e,t){var n,r,i;for(r=e,n=t.length-1;n>=0;n--){if(i=r.childNodes,t[n]>i.length-1)return null;r=i[t[n]]}return r}return{create:t,resolve:n}}),r(He,[I,T,y,Oe,A,C,d,m,u,k,$,ne],function(e,t,n,r,i,o,a,s,l,c,u,d){return function(f){function h(e,t){try{f.getDoc().execCommand(e,!1,t)}catch(n){}}function p(){var e=f.getDoc().documentMode;return e?e:6}function m(e){return e.isDefaultPrevented()}function g(e){var t,n;e.dataTransfer&&(f.selection.isCollapsed()&&"IMG"==e.target.tagName&&re.select(e.target),t=f.selection.getContent(),t.length>0&&(n=ue+escape(f.id)+","+escape(t),e.dataTransfer.setData(de,n)))}function v(e){var t;return e.dataTransfer&&(t=e.dataTransfer.getData(de),t&&t.indexOf(ue)>=0)?(t=t.substr(ue.length).split(","),{id:unescape(t[0]),html:unescape(t[1])}):null}function y(e){f.queryCommandSupported("mceInsertClipboardContent")?f.execCommand("mceInsertClipboardContent",!1,{content:e}):f.execCommand("mceInsertContent",!1,e)}function b(){function i(e){var t=C.schema.getBlockElements(),n=f.getBody();if("BR"!=e.nodeName)return!1;for(;e!=n&&!t[e.nodeName];e=e.parentNode)if(e.nextSibling)return!1;return!0}function o(e,t){var n;for(n=e.nextSibling;n&&n!=t;n=n.nextSibling)if((3!=n.nodeType||0!==Z.trim(n.data).length)&&n!==t)return!1;return n===t}function a(e,t,r){var o,a,s;for(s=C.schema.getNonEmptyElements(),o=new n(r||e,e);a=o[t?"next":"prev"]();){if(s[a.nodeName]&&!i(a))return a;if(3==a.nodeType&&a.data.length>0)return a}}function c(e){var n,r,i,o,s;if(!e.collapsed&&(n=C.getParent(t.getNode(e.startContainer,e.startOffset),C.isBlock), +r=C.getParent(t.getNode(e.endContainer,e.endOffset),C.isBlock),s=f.schema.getTextBlockElements(),n!=r&&s[n.nodeName]&&s[r.nodeName]&&"false"!==C.getContentEditable(n)&&"false"!==C.getContentEditable(r)))return e.deleteContents(),i=a(n,!1),o=a(r,!0),C.isEmpty(r)||Z(n).append(r.childNodes),Z(r).remove(),i?1==i.nodeType?"BR"==i.nodeName?(e.setStartBefore(i),e.setEndBefore(i)):(e.setStartAfter(i),e.setEndAfter(i)):(e.setStart(i,i.data.length),e.setEnd(i,i.data.length)):o&&(1==o.nodeType?(e.setStartBefore(o),e.setEndBefore(o)):(e.setStart(o,0),e.setEnd(o,0))),x.setRng(e),!0}function u(e,n){var r,i,s,l,c,u;if(!e.collapsed)return e;if(c=e.startContainer,u=e.startOffset,3==c.nodeType)if(n){if(u0)return e;if(r=t.getNode(e.startContainer,e.startOffset),s=C.getParent(r,C.isBlock),i=a(f.getBody(),n,r),l=C.getParent(i,C.isBlock),!r||!i)return e;if(l&&s!=l)if(n){if(!o(s,l))return e;1==r.nodeType?"BR"==r.nodeName?e.setStartBefore(r):e.setStartAfter(r):e.setStart(r,r.data.length),1==i.nodeType?e.setEnd(i,0):e.setEndBefore(i)}else{if(!o(l,s))return e;1==i.nodeType?"BR"==i.nodeName?e.setStartBefore(i):e.setStartAfter(i):e.setStart(i,i.data.length),1==r.nodeType?e.setEnd(r,0):e.setEndBefore(r)}return e}function d(e){var t=x.getRng();return t=u(t,e),c(t)?!0:void 0}function h(e,t){function n(e,n){return m=Z(n).parents().filter(function(e,t){return!!f.schema.getTextInlineElements()[t.nodeName]}),l=e.cloneNode(!1),m=s.map(m,function(e){return e=e.cloneNode(!1),l.hasChildNodes()?(e.appendChild(l.firstChild),l.appendChild(e)):l.appendChild(e),l.appendChild(e),e}),m.length?(p=C.create("br"),m[0].appendChild(p),C.replace(l,e),t.setStartBefore(p),t.setEndBefore(p),f.selection.setRng(t),p):null}function i(e){return e&&f.schema.getTextBlockElements()[e.tagName]}var o,a,l,c,u,d,h,p,m;if(t.collapsed&&(d=t.startContainer,h=t.startOffset,a=C.getParent(d,C.isBlock),i(a)))if(1==d.nodeType){if(d=d.childNodes[h],d&&"BR"!=d.tagName)return;if(u=e?a.nextSibling:a.previousSibling,C.isEmpty(a)&&i(u)&&C.isEmpty(u)&&n(a,d))return C.remove(u),!0}else if(3==d.nodeType){if(o=r.create(a,d),c=a.cloneNode(!0),d=r.resolve(c,o),e){if(h>=d.data.length)return;d.deleteData(h,1)}else{if(0>=h)return;d.deleteData(h-1,1)}if(C.isEmpty(c))return n(a,d)}}function p(e){var t,n,r;d(e)||(s.each(f.getBody().getElementsByTagName("*"),function(e){"SPAN"==e.tagName&&e.setAttribute("mce-data-marked",1),!e.hasAttribute("data-mce-style")&&e.hasAttribute("style")&&f.dom.setAttrib(e,"style",f.dom.getAttrib(e,"style"))}),t=new w(function(){}),t.observe(f.getDoc(),{childList:!0,attributes:!0,subtree:!0,attributeFilter:["style"]}),f.getDoc().execCommand(e?"ForwardDelete":"Delete",!1,null),n=f.selection.getRng(),r=n.startContainer.parentNode,s.each(t.takeRecords(),function(e){if(C.isChildOf(e.target,f.getBody())){if("style"==e.attributeName){var t=e.target.getAttribute("data-mce-style");t?e.target.setAttribute("style",t):e.target.removeAttribute("style")}s.each(e.addedNodes,function(e){if("SPAN"==e.nodeName&&!e.getAttribute("mce-data-marked")){var t,i;e==r&&(t=n.startOffset,i=e.firstChild),C.remove(e,!0),i&&(n.setStart(i,t),n.setEnd(i,t),f.selection.setRng(n))}})}}),t.disconnect(),s.each(f.dom.select("span[mce-data-marked]"),function(e){e.removeAttribute("mce-data-marked")}))}var b=f.getDoc(),C=f.dom,x=f.selection,w=window.MutationObserver,N,E;w||(N=!0,w=function(){function e(e){var t=e.relatedNode||e.target;n.push({target:t,addedNodes:[t]})}function t(e){var t=e.relatedNode||e.target;n.push({target:t,attributeName:e.attrName})}var n=[],r;this.observe=function(n){r=n,r.addEventListener("DOMSubtreeModified",e,!1),r.addEventListener("DOMNodeInsertedIntoDocument",e,!1),r.addEventListener("DOMNodeInserted",e,!1),r.addEventListener("DOMAttrModified",t,!1)},this.disconnect=function(){r.removeEventListener("DOMSubtreeModified",e,!1),r.removeEventListener("DOMNodeInsertedIntoDocument",e,!1),r.removeEventListener("DOMNodeInserted",e,!1),r.removeEventListener("DOMAttrModified",t,!1)},this.takeRecords=function(){return n}}),f.on("keydown",function(e){var t=e.keyCode==te,n=e.ctrlKey||e.metaKey;if(!m(e)&&(t||e.keyCode==ee)){var r=f.selection.getRng(),i=r.startContainer,o=r.startOffset;if(t&&e.shiftKey)return;if(h(t,r))return void e.preventDefault();if(!n&&r.collapsed&&3==i.nodeType&&(t?o0))return;e.preventDefault(),n&&f.selection.getSel().modify("extend",t?"forward":"backward",e.metaKey?"lineboundary":"word"),p(t)}}),f.on("keypress",function(t){if(!m(t)&&!x.isCollapsed()&&t.charCode>31&&!e.metaKeyPressed(t)){var n,r,i,o,a,s;n=f.selection.getRng(),s=String.fromCharCode(t.charCode),t.preventDefault(),r=Z(n.startContainer).parents().filter(function(e,t){return!!f.schema.getTextInlineElements()[t.nodeName]}),p(!0),r=r.filter(function(e,t){return!Z.contains(f.getBody(),t)}),r.length?(i=C.createFragment(),r.each(function(e,t){t=t.cloneNode(!1),i.hasChildNodes()?(t.appendChild(i.firstChild),i.appendChild(t)):(a=t,i.appendChild(t)),i.appendChild(t)}),a.appendChild(f.getDoc().createTextNode(s)),o=C.getParent(n.startContainer,C.isBlock),C.isEmpty(o)?Z(o).empty().append(i):n.insertNode(i),n.setStart(a.firstChild,1),n.setEnd(a.firstChild,1),f.selection.setRng(n)):f.selection.setContent(s)}}),f.addCommand("Delete",function(){p()}),f.addCommand("ForwardDelete",function(){p(!0)}),N||(f.on("dragstart",function(e){E=x.getRng(),g(e)}),f.on("drop",function(e){if(!m(e)){var n=v(e);n&&(e.preventDefault(),l.setEditorTimeout(f,function(){var r=t.getCaretRangeFromPoint(e.x,e.y,b);E&&(x.setRng(E),E=null),p(),x.setRng(r),y(n.html)}))}}),f.on("cut",function(e){m(e)||!e.clipboardData||f.selection.isCollapsed()||(e.preventDefault(),e.clipboardData.clearData(),e.clipboardData.setData("text/html",f.selection.getContent()),e.clipboardData.setData("text/plain",f.selection.getContent({format:"text"})),l.setEditorTimeout(f,function(){p(!0)}))}))}function C(){function e(e){var t=ne.create("body"),n=e.cloneContents();return t.appendChild(n),re.serializer.serialize(t,{format:"html"})}function n(n){if(!n.setStart){if(n.item)return!1;var r=n.duplicate();return r.moveToElementText(f.getBody()),t.compareRanges(n,r)}var i=e(n),o=ne.createRng();o.selectNode(f.getBody());var a=e(o);return i===a}f.on("keydown",function(e){var t=e.keyCode,r,i;if(!m(e)&&(t==te||t==ee)){if(r=f.selection.isCollapsed(),i=f.getBody(),r&&!ne.isEmpty(i))return;if(!r&&!n(f.selection.getRng()))return;e.preventDefault(),f.setContent(""),i.firstChild&&ne.isBlock(i.firstChild)?f.selection.setCursorLocation(i.firstChild,0):f.selection.setCursorLocation(i,0),f.nodeChanged()}})}function x(){f.shortcuts.add("meta+a",null,"SelectAll")}function w(){f.settings.content_editable||ne.bind(f.getDoc(),"mousedown mouseup",function(e){var t;if(e.target==f.getDoc().documentElement)if(t=re.getRng(),f.getBody().focus(),"mousedown"==e.type){if(c.isCaretContainer(t.startContainer))return;re.placeCaretAt(e.clientX,e.clientY)}else re.setRng(t)})}function N(){f.on("keydown",function(e){if(!m(e)&&e.keyCode===ee){if(!f.getBody().getElementsByTagName("hr").length)return;if(re.isCollapsed()&&0===re.getRng(!0).startOffset){var t=re.getNode(),n=t.previousSibling;if("HR"==t.nodeName)return ne.remove(t),void e.preventDefault();n&&n.nodeName&&"hr"===n.nodeName.toLowerCase()&&(ne.remove(n),e.preventDefault())}}})}function E(){window.Range.prototype.getClientRects||f.on("mousedown",function(e){if(!m(e)&&"HTML"===e.target.nodeName){var t=f.getBody();t.blur(),l.setEditorTimeout(f,function(){t.focus()})}})}function _(){f.on("click",function(e){var t=e.target;/^(IMG|HR)$/.test(t.nodeName)&&"false"!==ne.getContentEditableParent(t)&&(e.preventDefault(),re.getSel().setBaseAndExtent(t,0,t,1),f.nodeChanged()),"A"==t.nodeName&&ne.hasClass(t,"mce-item-anchor")&&(e.preventDefault(),re.select(t))})}function S(){function e(){var e=ne.getAttribs(re.getStart().cloneNode(!1));return function(){var t=re.getStart();t!==f.getBody()&&(ne.setAttrib(t,"style",null),Q(e,function(e){t.setAttributeNode(e.cloneNode(!0))}))}}function t(){return!re.isCollapsed()&&ne.getParent(re.getStart(),ne.isBlock)!=ne.getParent(re.getEnd(),ne.isBlock)}f.on("keypress",function(n){var r;return m(n)||8!=n.keyCode&&46!=n.keyCode||!t()?void 0:(r=e(),f.getDoc().execCommand("delete",!1,null),r(),n.preventDefault(),!1)}),ne.bind(f.getDoc(),"cut",function(n){var r;!m(n)&&t()&&(r=e(),l.setEditorTimeout(f,function(){r()}))})}function k(){document.body.setAttribute("role","application")}function T(){f.on("keydown",function(e){if(!m(e)&&e.keyCode===ee&&re.isCollapsed()&&0===re.getRng(!0).startOffset){var t=re.getNode().previousSibling;if(t&&t.nodeName&&"table"===t.nodeName.toLowerCase())return e.preventDefault(),!1}})}function R(){p()>7||(h("RespectVisibilityInDesign",!0),f.contentStyles.push(".mceHideBrInPre pre br {display: none}"),ne.addClass(f.getBody(),"mceHideBrInPre"),oe.addNodeFilter("pre",function(e){for(var t=e.length,n,r,o,a;t--;)for(n=e[t].getAll("br"),r=n.length;r--;)o=n[r],a=o.prev,a&&3===a.type&&"\n"!=a.value.charAt(a.value-1)?a.value+="\n":o.parent.insert(new i("#text",3),o,!0).value="\n"}),ae.addNodeFilter("pre",function(e){for(var t=e.length,n,r,i,o;t--;)for(n=e[t].getAll("br"),r=n.length;r--;)i=n[r],o=i.prev,o&&3==o.type&&(o.value=o.value.replace(/\r?\n$/,""))}))}function A(){ne.bind(f.getBody(),"mouseup",function(){var e,t=re.getNode();"IMG"==t.nodeName&&((e=ne.getStyle(t,"width"))&&(ne.setAttrib(t,"width",e.replace(/[^0-9%]+/g,"")),ne.setStyle(t,"width","")),(e=ne.getStyle(t,"height"))&&(ne.setAttrib(t,"height",e.replace(/[^0-9%]+/g,"")),ne.setStyle(t,"height","")))})}function B(){f.on("keydown",function(t){var n,r,i,o,a;if(!m(t)&&t.keyCode==e.BACKSPACE&&(n=re.getRng(),r=n.startContainer,i=n.startOffset,o=ne.getRoot(),a=r,n.collapsed&&0===i)){for(;a&&a.parentNode&&a.parentNode.firstChild==a&&a.parentNode!=o;)a=a.parentNode;"BLOCKQUOTE"===a.tagName&&(f.formatter.toggle("blockquote",null,a),n=ne.createRng(),n.setStart(r,0),n.setEnd(r,0),re.setRng(n))}})}function D(){function e(){K(),h("StyleWithCSS",!1),h("enableInlineTableEditing",!1),ie.object_resizing||h("enableObjectResizing",!1)}ie.readonly||f.on("BeforeExecCommand MouseDown",e)}function L(){function e(){Q(ne.select("a"),function(e){var t=e.parentNode,n=ne.getRoot();if(t.lastChild===e){for(;t&&!ne.isBlock(t);){if(t.parentNode.lastChild!==t||t===n)return;t=t.parentNode}ne.add(t,"br",{"data-mce-bogus":1})}})}f.on("SetContent ExecCommand",function(t){"setcontent"!=t.type&&"mceInsertLink"!==t.command||e()})}function M(){ie.forced_root_block&&f.on("init",function(){h("DefaultParagraphSeparator",ie.forced_root_block)})}function P(){f.on("keydown",function(e){var t;m(e)||e.keyCode!=ee||(t=f.getDoc().selection.createRange(),t&&t.item&&(e.preventDefault(),f.undoManager.beforeChange(),ne.remove(t.item(0)),f.undoManager.add()))})}function O(){var e;p()>=10&&(e="",Q("p div h1 h2 h3 h4 h5 h6".split(" "),function(t,n){e+=(n>0?",":"")+t+":empty"}),f.contentStyles.push(e+"{padding-right: 1px !important}"))}function H(){p()<9&&(oe.addNodeFilter("noscript",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.firstChild,r&&n.attr("data-mce-innertext",r.value)}),ae.addNodeFilter("noscript",function(e){for(var t=e.length,n,r,a;t--;)n=e[t],r=e[t].firstChild,r?r.value=o.decode(r.value):(a=n.attributes.map["data-mce-innertext"],a&&(n.attr("data-mce-innertext",null),r=new i("#text",3),r.value=a,r.raw=!0,n.append(r)))}))}function I(){function e(e,t){var n=i.createTextRange();try{n.moveToPoint(e,t)}catch(r){n=null}return n}function t(t){var r;t.button?(r=e(t.x,t.y),r&&(r.compareEndPoints("StartToStart",a)>0?r.setEndPoint("StartToStart",a):r.setEndPoint("EndToEnd",a),r.select())):n()}function n(){var e=r.selection.createRange();a&&!e.item&&0===e.compareEndPoints("StartToEnd",e)&&a.select(),ne.unbind(r,"mouseup",n),ne.unbind(r,"mousemove",t),a=o=0}var r=ne.doc,i=r.body,o,a,s;r.documentElement.unselectable=!0,ne.bind(r,"mousedown contextmenu",function(i){if("HTML"===i.target.nodeName){if(o&&n(),s=r.documentElement,s.scrollHeight>s.clientHeight)return;o=1,a=e(i.x,i.y),a&&(ne.bind(r,"mouseup",n),ne.bind(r,"mousemove",t),ne.getRoot().focus(),a.select())}})}function F(){f.on("keyup focusin mouseup",function(t){65==t.keyCode&&e.metaKeyPressed(t)||re.normalize()},!0)}function z(){f.contentStyles.push("img:-moz-broken {-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}")}function U(){f.inline||f.on("keydown",function(){document.activeElement==document.body&&f.getWin().focus()})}function W(){f.inline||(f.contentStyles.push("body {min-height: 150px}"),f.on("click",function(e){var t;if("HTML"==e.target.nodeName){if(a.ie>11)return void f.getBody().focus();t=f.selection.getRng(),f.getBody().focus(),f.selection.setRng(t),f.selection.normalize(),f.nodeChanged()}}))}function V(){a.mac&&f.on("keydown",function(t){!e.metaKeyPressed(t)||t.shiftKey||37!=t.keyCode&&39!=t.keyCode||(t.preventDefault(),f.selection.getSel().modify("move",37==t.keyCode?"backward":"forward","lineboundary"))})}function $(){h("AutoUrlDetect",!1)}function q(){f.on("click",function(e){var t=e.target;do if("A"===t.tagName)return void e.preventDefault();while(t=t.parentNode)}),f.contentStyles.push(".mce-content-body {-webkit-touch-callout: none}")}function j(){f.on("init",function(){f.dom.bind(f.getBody(),"submit",function(e){e.preventDefault()})})}function Y(){oe.addNodeFilter("br",function(e){for(var t=e.length;t--;)"Apple-interchange-newline"==e[t].attr("class")&&e[t].remove()})}function X(){f.on("dragstart",function(e){g(e)}),f.on("drop",function(e){if(!m(e)){var n=v(e);if(n&&n.id!=f.id){e.preventDefault();var r=t.getCaretRangeFromPoint(e.x,e.y,f.getDoc());re.setRng(r),y(n.html)}}})}function K(){var e,t;G()&&(e=f.getBody(),t=e.parentNode,t.removeChild(e),t.appendChild(e),e.focus())}function G(){var e;return se?(e=f.selection.getSel(),!e||!e.rangeCount||0===e.rangeCount):0}function J(){function t(e){var t=new d(e.getBody()),n=e.selection.getRng(),r=u.fromRangeStart(n),i=u.fromRangeEnd(n);return!e.selection.isCollapsed()&&!t.prev(r)&&!t.next(i)}f.on("keypress",function(n){!m(n)&&!re.isCollapsed()&&n.charCode>31&&!e.metaKeyPressed(n)&&t(f)&&(n.preventDefault(),f.setContent(String.fromCharCode(n.charCode)),f.selection.select(f.getBody(),!0),f.selection.collapse(!1),f.nodeChanged())}),f.on("keydown",function(e){var n=e.keyCode;m(e)||n!=te&&n!=ee||t(f)&&(e.preventDefault(),f.setContent(""),f.nodeChanged())})}var Q=s.each,Z=f.$,ee=e.BACKSPACE,te=e.DELETE,ne=f.dom,re=f.selection,ie=f.settings,oe=f.parser,ae=f.serializer,se=a.gecko,le=a.ie,ce=a.webkit,ue="data:text/mce-internal,",de=le?"Text":"URL";return B(),C(),a.windowsPhone||F(),ce&&(J(),b(),w(),_(),M(),j(),T(),Y(),a.iOS?(U(),W(),q()):x()),le&&a.ie<11&&(N(),k(),R(),A(),P(),O(),H(),I()),a.ie>=11&&(W(),T()),a.ie&&(x(),$(),X()),se&&(J(),N(),E(),S(),D(),L(),z(),V(),T()),{refreshContentEditable:K,isHidden:G}}}),r(Ie,[ue,w,m],function(e,t,n){function r(e,t){return"selectionchange"==t?e.getDoc():!e.inline&&/^mouse|touch|click|contextmenu|drop|dragover|dragend/.test(t)?e.getDoc().documentElement:e.settings.event_root?(e.eventRoot||(e.eventRoot=o.select(e.settings.event_root)[0]),e.eventRoot):e.getBody()}function i(e,t){function n(e){return!e.hidden&&!e.readonly}var i=r(e,t),s;if(e.delegates||(e.delegates={}),!e.delegates[t])if(e.settings.event_root){if(a||(a={},e.editorManager.on("removeEditor",function(){var t;if(!e.editorManager.activeEditor&&a){for(t in a)e.dom.unbind(r(e,t));a=null}})),a[t])return;s=function(r){for(var i=r.target,a=e.editorManager.editors,s=a.length;s--;){var l=a[s].getBody();(l===i||o.isChildOf(i,l))&&n(a[s])&&a[s].fire(t,r)}},a[t]=s,o.bind(i,t,s)}else s=function(r){n(e)&&e.fire(t,r)},o.bind(i,t,s),e.delegates[t]=s}var o=t.DOM,a,s={bindPendingEventDelegates:function(){var e=this;n.each(e._pendingNativeEvents,function(t){i(e,t)})},toggleNativeEvent:function(e,t){var n=this;"focus"!=e&&"blur"!=e&&(t?n.initialized?i(n,e):n._pendingNativeEvents?n._pendingNativeEvents.push(e):n._pendingNativeEvents=[e]:n.initialized&&(n.dom.unbind(r(n,e),e,n.delegates[e]),delete n.delegates[e]))},unbindAllNativeEvents:function(){var e=this,t;if(e.delegates){for(t in e.delegates)e.dom.unbind(r(e,t),t,e.delegates[t]);delete e.delegates}e.inline||(e.getBody().onload=null,e.dom.unbind(e.getWin()),e.dom.unbind(e.getDoc())),e.dom.unbind(e.getBody()),e.dom.unbind(e.getContainer())}};return s=n.extend({},e,s)}),r(Fe,[],function(){function e(e,t,n){try{e.getDoc().execCommand(t,!1,n)}catch(r){}}function t(e){var t,n;return t=e.getBody(),n=function(t){e.dom.getParents(t.target,"a").length>0&&t.preventDefault()},e.dom.bind(t,"click",n),{unbind:function(){e.dom.unbind(t,"click",n)}}}function n(n,r){n._clickBlocker&&(n._clickBlocker.unbind(),n._clickBlocker=null),r?(n._clickBlocker=t(n),n.selection.controlSelection.hideResizeRect(),n.readonly=!0,n.getBody().contentEditable=!1):(n.readonly=!1,n.getBody().contentEditable=!0,e(n,"StyleWithCSS",!1),e(n,"enableInlineTableEditing",!1),e(n,"enableObjectResizing",!1),n.focus(),n.nodeChanged())}function r(e,t){var r=e.readonly?"readonly":"design";t!=r&&(e.initialized?n(e,"readonly"==t):e.on("init",function(){n(e,"readonly"==t)}),e.fire("SwitchMode",{mode:t}))}return{setMode:r}}),r(ze,[m,d],function(e,t){var n=e.each,r=e.explode,i={f9:120,f10:121,f11:122},o=e.makeMap("alt,ctrl,shift,meta,access");return function(a){function s(e){var a,s,l={};n(r(e,"+"),function(e){e in o?l[e]=!0:/^[0-9]{2,}$/.test(e)?l.keyCode=parseInt(e,10):(l.charCode=e.charCodeAt(0),l.keyCode=i[e]||e.toUpperCase().charCodeAt(0))}),a=[l.keyCode];for(s in o)l[s]?a.push(s):l[s]=!1;return l.id=a.join(","),l.access&&(l.alt=!0,t.mac?l.ctrl=!0:l.shift=!0),l.meta&&(t.mac?l.meta=!0:(l.ctrl=!0,l.meta=!1)),l}function l(t,n,i,o){var l;return l=e.map(r(t,">"),s),l[l.length-1]=e.extend(l[l.length-1],{func:i,scope:o||a}),e.extend(l[0],{desc:a.translate(n),subpatterns:l.slice(1)})}function c(e){return e.altKey||e.ctrlKey||e.metaKey}function u(e){return e.keyCode>=112&&e.keyCode<=123}function d(e,t){return t?t.ctrl!=e.ctrlKey||t.meta!=e.metaKey?!1:t.alt!=e.altKey||t.shift!=e.shiftKey?!1:e.keyCode==t.keyCode||e.charCode&&e.charCode==t.charCode?(e.preventDefault(),!0):!1:!1}function f(e){return e.func?e.func.call(e.scope):null}var h=this,p={},m=[];a.on("keyup keypress keydown",function(e){!c(e)&&!u(e)||e.isDefaultPrevented()||(n(p,function(t){return d(e,t)?(m=t.subpatterns.slice(0),"keydown"==e.type&&f(t),!0):void 0}),d(e,m[0])&&(1===m.length&&"keydown"==e.type&&f(m[0]),m.shift()))}),h.add=function(t,i,o,s){var c;return c=o,"string"==typeof o?o=function(){a.execCommand(c,!1,null)}:e.isArray(c)&&(o=function(){a.execCommand(c[0],c[1],c[2])}),n(r(e.trim(t.toLowerCase())),function(e){var t=l(e,i,o,s);p[t.id]=t}),!0},h.remove=function(e){var t=l(e);return p[t.id]?(delete p[t.id],!0):!1}}}),r(Ue,[c,m,z],function(e,t,n){return function(r,i){function o(e){var t,n;return n={"image/jpeg":"jpg","image/jpg":"jpg","image/gif":"gif","image/png":"png"},t=n[e.blob().type.toLowerCase()]||"dat",e.id()+"."+t}function a(e,t){return e?e.replace(/\/$/,"")+"/"+t.replace(/^\//,""):t}function s(e){return{id:e.id,blob:e.blob,base64:e.base64,filename:n.constant(o(e))}}function l(e,t,n,r){var s,l;s=new XMLHttpRequest,s.open("POST",i.url),s.withCredentials=i.credentials,s.upload.onprogress=function(e){r(e.loaded/e.total*100)},s.onerror=function(){n("Image upload failed due to a XHR Transport error. Code: "+s.status)},s.onload=function(){var e;return 200!=s.status?void n("HTTP Error: "+s.status):(e=JSON.parse(s.responseText),e&&"string"==typeof e.location?void t(a(i.basePath,e.location)):void n("Invalid JSON: "+s.responseText))},l=new FormData,l.append("file",e.blob(),o(e)),s.send(l)}function c(){return new e(function(e){e([])})}function u(e,t){return{url:t,blobInfo:e,status:!0}}function d(e,t){return{url:"",blobInfo:e,status:!1,error:t}}function f(e,n){t.each(y[e],function(e){e(n)}),delete y[e]}function h(t,n,i){return r.markPending(t.blobUri()),new e(function(e){var o,a,l=function(){};try{var c=function(){o&&(o.close(),a=l)},h=function(n){c(),r.markUploaded(t.blobUri(),n),f(t.blobUri(),u(t,n)),e(u(t,n))},p=function(){c(),r.removeFailed(t.blobUri()),f(t.blobUri(),d(t,p)),e(d(t,p))};a=function(e){0>e||e>100||(o||(o=i()),o.progressBar.value(e))},n(s(t),h,p,a)}catch(m){e(d(t,m.message))}})}function p(e){return e===l}function m(t){var n=t.blobUri();return new e(function(e){y[n]=y[n]||[],y[n].push(e)})}function g(n,o){return n=t.grep(n,function(e){return!r.isUploaded(e.blobUri())}),e.all(t.map(n,function(e){return r.isPending(e.blobUri())?m(e):h(e,i.handler,o)}))}function v(e,t){return!i.url&&p(i.handler)?c():g(e,t)}var y={};return i=t.extend({credentials:!1,handler:l},i),{upload:v}}}),r(We,[c],function(e){function t(t){return new e(function(e){var n=new XMLHttpRequest;n.open("GET",t,!0),n.responseType="blob",n.onload=function(){200==this.status&&e(this.response)},n.send()})}function n(e){var t,n;return e=decodeURIComponent(e).split(","),n=/data:([^;]+)/.exec(e[0]),n&&(t=n[1]),{type:t,data:e[1]}}function r(t){return new e(function(e){var r,i,o;t=n(t);try{r=atob(t.data)}catch(a){return void e(new Blob([]))}for(i=new Uint8Array(r.length),o=0;o0&&(n&&(l*=-1),r.left+=l,r.right+=l),r}function l(){var n,r,o,a,s;for(n=i("*[contentEditable=false]",t),a=0;a').css(l).appendTo(t),o&&m.addClass("mce-visual-caret-before"),d(),c=a.ownerDocument.createRange(),f=g.firstChild,c.setStart(f,0),c.setEnd(f,1),c):(g=e.insertInline(a,o),c=a.ownerDocument.createRange(),s(g.nextSibling)?(c.setStart(g,0),c.setEnd(g,0)):(c.setStart(g,1),c.setEnd(g,1)),c)}function u(){l(),g&&(e.remove(g),g=null),m&&(m.remove(),m=null),clearInterval(p)}function d(){p=a.setInterval(function(){i("div.mce-visual-caret",t).toggleClass("mce-visual-caret-hidden")},500)}function f(){a.clearInterval(p)}function h(){return".mce-visual-caret {position: absolute;background-color: black;background-color: currentcolor;}.mce-visual-caret-hidden {display: none;}*[data-mce-caret] {position: absolute;left: -1000px;right: auto;top: 0;margin: 0;padding: 0;}"}var p,m,g;return{show:c,hide:u,getCss:h,destroy:f}}}),r(Xe,[p,_,W],function(e,t,n){function r(i){function o(t){return e.map(t,function(e){return e=n.clone(e),e.node=i,e})}if(e.isArray(i))return e.reduce(i,function(e,t){return e.concat(r(t))},[]);if(t.isElement(i))return o(i.getClientRects());if(t.isText(i)){var a=i.ownerDocument.createRange();return a.setStart(i,0),a.setEnd(i,i.data.length),o(a.getClientRects())}}return{getClientRects:r}}),r(Ke,[z,p,Xe,U,te,ne,$,W],function(e,t,n,r,i,o,a,s){function l(e,t,n,o){for(;o=i.findNode(o,e,r.isEditableCaretCandidate,t);)if(n(o))return}function c(e,r,i,o,a,s){function c(o){var s,l,c;for(c=n.getClientRects(o),-1==e&&(c=c.reverse()),s=0;s0&&r(l,t.last(f))&&u++,l.line=u,a(l))return!0;f.push(l)}}var u=0,d,f=[],h;return(h=t.last(s.getClientRects()))?(d=s.getNode(),c(d),l(e,o,c,d),f):f}function u(e,t){return t.line>e}function d(e,t){return t.line===e}function f(e,n,r,i){function l(n){return 1==e?t.last(n.getClientRects()):t.last(n.getClientRects())}var c=new o(n),u,d,f,h,p=[],m=0,g,v;1==e?(u=c.next,d=s.isBelow,f=s.isAbove,h=a.after(i)):(u=c.prev,d=s.isAbove,f=s.isBelow,h=a.before(i)),v=l(h);do if(h.isVisible()&&(g=l(h),!f(g,v))){if(p.length>0&&d(g,t.last(p))&&m++,g=s.clone(g),g.position=h,g.line=m,r(g))return p;p.push(g)}while(h=u(h));return p}var h=e.curry,p=h(c,-1,s.isAbove,s.isBelow),m=h(c,1,s.isBelow,s.isAbove);return{upUntil:p,downUntil:m,positionsUntil:f,isAboveLine:h(u),isLine:h(d)}}),r(Ge,[z,p,_,Xe,W,te,U],function(e,t,n,r,i,o,a){function s(e,t){return Math.abs(e.left-t)}function l(e,t){return Math.abs(e.right-t)}function c(e,n){function r(e,t){return e>=t.left&&e<=t.right}return t.reduce(e,function(e,t){var i,o;return i=Math.min(s(e,n),l(e,n)),o=Math.min(s(t,n),l(t,n)),r(n,t)?t:r(n,e)?e:o==i&&m(t.node)?t:i>o?t:e})}function u(e,t,n,r){for(;r=g(r,e,a.isEditableCaretCandidate,t);)if(n(r))return}function d(e,n){function o(e,i){var o;return o=t.filter(r.getClientRects(i),function(t){return!e(t,n)}),a=a.concat(o),0===o.length}var a=[];return a.push(n),u(-1,e,v(o,i.isAbove),n.node),u(1,e,v(o,i.isBelow),n.node),a}function f(e){return t.filter(t.toArray(e.getElementsByTagName("*")),m)}function h(e,t){return{node:e.node,before:s(e,t)=e.top&&i<=e.bottom}),a=c(o,n),a&&(a=c(d(e,a),n),a&&m(a.node))?h(a,n):null}var m=n.isContentEditableFalse,g=o.findNode,v=e.curry;return{findClosestClientRect:c,findLineNodeRects:d,closestCaret:p}}),r(Je,[],function(){var e=function(e){var t,n,r,i;return i=e.getBoundingClientRect(),t=e.ownerDocument,n=t.documentElement,r=t.defaultView,{top:i.top+r.pageYOffset-n.clientTop,left:i.left+r.pageXOffset-n.clientLeft}},t=function(t){return t.inline?e(t.getBody()):{left:0,top:0}},n=function(e){var t=e.getBody();return e.inline?{left:t.scrollLeft,top:t.scrollTop}:{left:0,top:0}},r=function(e){var t=e.getBody(),n=e.getDoc().documentElement,r={left:t.scrollLeft,top:t.scrollTop},i={left:t.scrollLeft||n.scrollLeft,top:t.scrollTop||n.scrollTop};return e.inline?r:i},i=function(t,n){if(n.target.ownerDocument!==t.getDoc()){var i=e(t.getContentAreaContainer()),o=r(t);return{left:n.pageX-i.left+o.left,top:n.pageY-i.top+o.top}}return{left:n.pageX,top:n.pageY}},o=function(e,t,n){return{pageX:n.left-e.left+t.left,pageY:n.top-e.top+t.top}},a=function(e,r){return o(t(e),n(e),i(e,r))};return{calc:a}}),r(Qe,[_,p,z,u,w,Je],function(e,t,n,r,i,o){var a=e.isContentEditableFalse,s=e.isContentEditableTrue,l=function(e){return a(e)},c=function(e,t,n){return t===n||e.dom.isChildOf(t,n)?!1:!a(t)},u=function(e){var t=e.cloneNode(!0);return t.removeAttribute("data-mce-selected"),t},d=function(e,t,n,r){var i=t.cloneNode(!0);e.dom.setStyles(i,{width:n,height:r}),e.dom.setAttrib(i,"data-mce-selected",null);var o=e.dom.create("div",{"class":"mce-drag-container","data-mce-bogus":"all",unselectable:"on",contenteditable:"false"});return e.dom.setStyles(o,{position:"absolute",opacity:.5,overflow:"hidden",border:0,padding:0,margin:0,width:n,height:r}),e.dom.setStyles(i,{margin:0,boxSizing:"border-box"}),o.appendChild(i),o},f=function(e,t){e.parentNode!==t&&t.appendChild(e)},h=function(e,t,n,r,i,o){var a=0,s=0;e.style.left=t.pageX+"px",e.style.top=t.pageY+"px",t.pageX+n>i&&(a=t.pageX+n-i), +t.pageY+r>o&&(s=t.pageY+r-o),e.style.width=n-a+"px",e.style.height=r-s+"px"},p=function(e){e&&e.parentNode&&e.parentNode.removeChild(e)},m=function(e){return 0===e.button},g=function(e){return e.element},v=function(e,t){return{pageX:t.pageX-e.relX,pageY:t.pageY+5}},y=function(e,r){return function(i){if(m(i)){var o=t.find(r.dom.getParents(i.target),n.or(a,s));if(l(o)){var c=r.dom.getPos(o),u=r.getBody(),f=r.getDoc().documentElement;e.element=o,e.screenX=i.screenX,e.screenY=i.screenY,e.maxX=(r.inline?u.scrollWidth:f.offsetWidth)-2,e.maxY=(r.inline?u.scrollHeight:f.offsetHeight)-2,e.relX=i.pageX-c.x,e.relY=i.pageY-c.y,e.width=o.offsetWidth,e.height=o.offsetHeight,e.ghost=d(r,o,e.width,e.height)}}}},b=function(e,t){var n=r.throttle(function(e,n){t._selectionOverrides.hideFakeCaret(),t.selection.placeCaretAt(e,n)},0);return function(r){var i=Math.max(Math.abs(r.screenX-e.screenX),Math.abs(r.screenY-e.screenY));if(g(e)&&!e.dragging&&i>10){var a=t.fire("dragstart",{target:e.element});if(a.isDefaultPrevented())return;e.dragging=!0,t.focus()}if(e.dragging){var s=v(e,o.calc(t,r));f(e.ghost,t.getBody()),h(e.ghost,s,e.width,e.height,e.maxX,e.maxY),n(r.clientX,r.clientY)}}},C=function(e,t){return function(n){if(e.dragging&&c(t,t.selection.getNode(),e.element)){var r=u(e.element),i=t.fire("drop",{targetClone:r,clientX:n.clientX,clientY:n.clientY});i.isDefaultPrevented()||(r=i.targetClone,t.undoManager.transact(function(){p(e.element),t.insertContent(t.dom.getOuterHTML(r)),t._selectionOverrides.hideFakeCaret()}))}w(e)}},x=function(e,t){return function(){w(e),e.dragging&&t.fire("dragend")}},w=function(e){e.dragging=!1,e.element=null,p(e.ghost)},N=function(e){var t={},n,r,o,a,s,l;n=i.DOM,l=document,r=y(t,e),o=b(t,e),a=C(t,e),s=x(t,e),e.on("mousedown",r),e.on("mousemove",o),e.on("mouseup",a),n.bind(l,"mousemove",o),n.bind(l,"mouseup",s),e.on("remove",function(){n.unbind(l,"mousemove",o),n.unbind(l,"mouseup",s)})},E=function(e){e.on("drop",function(t){var n="undefined"!=typeof t.clientX?e.getDoc().elementFromPoint(t.clientX,t.clientY):null;(a(n)||a(e.dom.getContentEditableParent(n)))&&t.preventDefault()})},_=function(e){N(e),E(e)};return{init:_}}),r(Ze,[d,ne,$,k,te,Ye,Ke,Ge,_,T,W,I,z,p,u,Qe,S],function(e,t,n,r,i,o,a,s,l,c,u,d,f,h,p,m,g){function v(e,t){for(;t=e(t);)if(t.isVisible())return t;return t}function y(c){function y(){var e=c.dom.get(le);return e?e.getElementsByTagName("*")[0]:e}function S(e){return c.dom.isBlock(e)}function k(e){e&&c.selection.setRng(e)}function T(){return c.selection.getRng()}function R(e,t){c.selection.scrollIntoView(e,t)}function A(e,t,n){var r;return r=c.fire("ShowCaret",{target:t,direction:e,before:n}),r.isDefaultPrevented()?null:(R(t,-1===e),se.show(n,t))}function B(e){var t;return se.hide(),t=c.fire("BeforeObjectSelected",{target:e}),t.isDefaultPrevented()?null:D(e)}function D(e){var t=e.ownerDocument.createRange();return t.selectNode(e),t}function L(e,t){var n=i.isInSameBlock(e,t);return!n&&l.isBr(e.getNode())?!0:n}function M(e,t){return t=i.normalizeRange(e,re,t),-1==e?n.fromRangeStart(t):n.fromRangeEnd(t)}function P(e){return r.isCaretContainerBlock(e.startContainer)}function O(e,t,n,r){var i,o,a,s;return!r.collapsed&&(i=_(r),x(i))?A(e,i,-1==e):(s=P(r),o=M(e,r),n(o)?B(o.getNode(-1==e)):(o=t(o))?n(o)?A(e,o.getNode(-1==e),1==e):(a=t(o),n(a)&&L(o,a)?A(e,a.getNode(-1==e),1==e):s?$(o.toRange()):null):s?r:null)}function H(e,t,n){var r,i,o,l,c,u,d,f,p;if(p=_(n),r=M(e,n),i=t(re,a.isAboveLine(1),r),o=h.filter(i,a.isLine(1)),c=h.last(r.getClientRects()),E(r)&&(p=r.getNode()),N(r)&&(p=r.getNode(!0)),!c)return null;if(u=c.left,l=s.findClosestClientRect(o,u),l&&x(l.node))return d=Math.abs(u-l.left),f=Math.abs(u-l.right),A(e,l.node,f>d);if(p){var m=a.positionsUntil(e,re,a.isAboveLine(1),p);if(l=s.findClosestClientRect(h.filter(m,a.isLine(1)),u))return $(l.position.toRange());if(l=h.last(h.filter(m,a.isLine(0))))return $(l.position.toRange())}}function I(t,r){function i(){var t=c.dom.create(c.settings.forced_root_block);return(!e.ie||e.ie>=11)&&(t.innerHTML='
    '),t}var o,a,s;if(r.collapsed&&c.settings.forced_root_block){if(o=c.dom.getParent(r.startContainer,"PRE"),!o)return;a=1==t?oe(n.fromRangeStart(r)):ae(n.fromRangeStart(r)),a||(s=i(),1==t?c.$(o).after(s):c.$(o).before(s),c.selection.select(s,!0),c.selection.collapse())}}function F(e,t,n,r){var i;return(i=O(e,t,n,r))?i:(i=I(e,r),i?i:null)}function z(e,t,n){var r;return(r=H(e,t,n))?r:(r=I(e,n),r?r:null)}function U(){return ue("*[data-mce-caret]")[0]}function W(e){e=ue(e),e.attr("data-mce-caret")&&(se.hide(),e.removeAttr("data-mce-caret"),e.removeAttr("data-mce-bogus"),e.removeAttr("style"),k(T()),R(e[0]))}function V(e){var t,r;return e=i.normalizeRange(1,re,e),t=n.fromRangeStart(e),x(t.getNode())?A(1,t.getNode(),!t.isAtEnd()):x(t.getNode(!0))?A(1,t.getNode(!0),!1):(r=c.dom.getParent(t.getNode(),f.or(x,C)),x(r)?A(1,r,!1):(se.hide(),null))}function $(e){var t;return e&&e.collapsed?(t=V(e),t?t:e):e}function q(e){var t,i,o,a;return x(e)?(x(e.previousSibling)&&(o=e.previousSibling),i=ae(n.before(e)),i||(t=oe(n.after(e))),t&&w(t.getNode())&&(a=t.getNode()),r.remove(e.previousSibling),r.remove(e.nextSibling),c.dom.remove(e),ee(),c.dom.isEmpty(c.getBody())?(c.setContent(""),void c.focus()):o?n.after(o).toRange():a?n.before(a).toRange():i?i.toRange():t?t.toRange():null):null}function j(e){var t=c.schema.getTextBlockElements();return e.nodeName in t}function Y(e){return c.dom.isEmpty(e)}function X(e,t,r){var i=c.dom,o,a,s,l;if(o=i.getParent(t.getNode(),i.isBlock),a=i.getParent(r.getNode(),i.isBlock),-1===e){if(l=r.getNode(!0),N(r)&&S(l))return j(o)?(Y(o)&&i.remove(o),n.after(l).toRange()):q(r.getNode(!0))}else if(l=t.getNode(),E(t)&&S(l))return j(a)?(Y(a)&&i.remove(a),n.before(l).toRange()):q(t.getNode());if(o===a||!j(o)||!j(a))return null;for(;s=o.firstChild;)a.appendChild(s);return c.dom.remove(o),r.toRange()}function K(e,t,n,i){var o,a,s,l;return!i.collapsed&&(o=_(i),x(o))?$(q(o)):(a=M(e,i),n(a)&&r.isCaretContainerBlock(i.startContainer)?(l=-1==e?ie.prev(a):ie.next(a),l?$(l.toRange()):i):t(a)?$(q(a.getNode(-1==e))):(s=-1==e?ie.prev(a):ie.next(a),t(s)?-1===e?X(e,a,s):X(e,s,a):void 0))}function G(){function r(e,t){var n=t(T());n&&!e.isDefaultPrevented()&&(e.preventDefault(),k(n))}function i(e){for(var t=c.getBody();e&&e!=t;){if(C(e)||x(e))return e;e=e.parentNode}return null}function o(e,t,n){return n.collapsed?!1:h.reduce(n.getClientRects(),function(n,r){return n||u.containsXY(r,e,t)},!1)}function l(e){var t=!1;e.on("touchstart",function(){t=!1}),e.on("touchmove",function(){t=!0}),e.on("touchend",function(e){var n=i(e.target);x(n)?t||(e.preventDefault(),Z(B(n))):ee()})}function f(){var e,t=i(c.selection.getNode());C(t)&&S(t)&&c.dom.isEmpty(t)&&(e=c.dom.create("br",{"data-mce-bogus":"1"}),c.$(t).empty().append(e),c.selection.setRng(n.before(e).toRange()))}function g(e){var t=U();if(t)return"compositionstart"==e.type?(e.preventDefault(),e.stopPropagation(),void W(t)):void(" "!=t.innerHTML&&W(t))}function v(e){var t;switch(e.keyCode){case d.DELETE:t=f();break;case d.BACKSPACE:t=f()}t&&e.preventDefault()}var w=b(F,1,oe,E),_=b(F,-1,ae,N),R=b(K,1,E,N),D=b(K,-1,N,E),L=b(z,-1,a.upUntil),M=b(z,1,a.downUntil);c.on("mouseup",function(){var e=T();e.collapsed&&k(V(e))}),c.on("click",function(e){var t;t=i(e.target),t&&x(t)&&(e.preventDefault(),c.focus())});var P=function(e){var r=new t(e);if(!e.firstChild)return!1;var i=n.before(e.firstChild),o=r.next(i);return o&&!E(o)&&!N(o)},O=function(e,t){var n=c.dom.getParent(e,c.dom.isBlock),r=c.dom.getParent(t,c.dom.isBlock);return n===r},H=function(e,t){var n=c.dom.getParent(e,c.dom.isBlock),r=c.dom.getParent(t,c.dom.isBlock);return n&&!O(n,r)&&P(n)};l(c),c.on("mousedown",function(e){var t;if(t=i(e.target))x(t)?(e.preventDefault(),Z(B(t))):(ee(),o(e.clientX,e.clientY,c.selection.getRng())||c.selection.placeCaretAt(e.clientX,e.clientY));else{ee(),se.hide();var n=s.closestCaret(re,e.clientX,e.clientY);n&&(H(e.target,n.node)||(e.preventDefault(),c.getBody().focus(),k(A(1,n.node,n.before))))}}),c.on("keydown",function(e){if(!d.modifierPressed(e))switch(e.keyCode){case d.RIGHT:r(e,w);break;case d.DOWN:r(e,M);break;case d.LEFT:r(e,_);break;case d.UP:r(e,L);break;case d.DELETE:r(e,R);break;case d.BACKSPACE:r(e,D);break;default:x(c.selection.getNode())&&e.preventDefault()}}),c.on("keyup compositionstart",function(e){g(e),v(e)},!0),c.on("cut",function(){var e=c.selection.getNode();x(e)&&p.setEditorTimeout(c,function(){k($(q(e)))})}),c.on("getSelectionRange",function(e){var t=e.range;if(ce){if(!ce.parentNode)return void(ce=null);t=t.cloneRange(),t.selectNode(ce),e.range=t}}),c.on("setSelectionRange",function(e){var t;t=Z(e.range),t&&(e.range=t)}),c.on("focus",function(){p.setEditorTimeout(c,function(){c.selection.setRng($(c.selection.getRng()))},0)}),c.on("copy",function(t){var n=t.clipboardData;if(!t.isDefaultPrevented()&&t.clipboardData&&!e.ie){var r=y();r&&(t.preventDefault(),n.clearData(),n.setData("text/html",r.outerHTML),n.setData("text/plain",r.outerText))}}),m.init(c)}function J(){var e=c.contentStyles,t=".mce-content-body";e.push(se.getCss()),e.push(t+" .mce-offscreen-selection {position: absolute;left: -9999999999px;}"+t+" *[contentEditable=false] {cursor: default;}"+t+" *[contentEditable=true] {cursor: text;}")}function Q(e){return r.isCaretContainer(e.startContainer)||r.isCaretContainer(e.endContainer)}function Z(t){var n,r=c.$,i=c.dom,o,a,s,l,u,d,f,h,p;if(!t)return ee(),null;if(t.collapsed){if(ee(),!Q(t)){if(f=M(1,t),x(f.getNode()))return A(1,f.getNode(),!f.isAtEnd());if(x(f.getNode(!0)))return A(1,f.getNode(!0),!1)}return null}return s=t.startContainer,l=t.startOffset,u=t.endOffset,3==s.nodeType&&0==l&&x(s.parentNode)&&(s=s.parentNode,l=i.nodeIndex(s),s=s.parentNode),1!=s.nodeType?(ee(),null):(u==l+1&&(n=s.childNodes[l]),x(n)?(h=p=n.cloneNode(!0),d=c.fire("ObjectSelected",{target:n,targetClone:h}),d.isDefaultPrevented()?(ee(),null):(h=d.targetClone,o=r("#"+le),0===o.length&&(o=r('
    ').attr("id",le),o.appendTo(c.getBody())),t=c.dom.createRng(),h===p&&e.ie?(o.empty().append(g.ZWSP).append(h).append(g.ZWSP),t.setStart(o[0].firstChild,0),t.setEnd(o[0].lastChild,1)):(o.empty().append("\xa0").append(h).append("\xa0"),t.setStart(o[0].firstChild,1),t.setEnd(o[0].lastChild,0)),o.css({top:i.getPos(n,c.getBody()).y}),o[0].focus(),a=c.selection.getSel(),a.removeAllRanges(),a.addRange(t),c.$("*[data-mce-selected]").removeAttr("data-mce-selected"),n.setAttribute("data-mce-selected",1),ce=n,t)):(ee(),null))}function ee(){ce&&(ce.removeAttribute("data-mce-selected"),c.$("#"+le).remove(),ce=null)}function te(){se.destroy(),ce=null}function ne(){se.hide()}var re=c.getBody(),ie=new t(re),oe=b(v,ie.next),ae=b(v,ie.prev),se=new o(c.getBody(),S),le="sel-"+c.dom.uniqueId(),ce,ue=c.$;return e.ceFalse&&(G(),J()),{showBlockCaretContainer:W,hideFakeCaret:ne,destroy:te}}var b=f.curry,C=l.isContentEditableTrue,x=l.isContentEditableFalse,w=l.isElement,N=i.isAfterContentEditableFalse,E=i.isBeforeContentEditableFalse,_=c.getSelectedNode;return y}),r(et,[],function(){var e=0,t=function(){var e=function(){return Math.round(4294967295*Math.random()).toString(36)},t=(new Date).getTime();return"s"+t.toString(36)+e()+e()+e()},n=function(n){return n+e++ +t()};return{uuid:n}}),r(tt,[w,g,E,R,A,O,P,Y,J,Q,Z,ee,oe,ae,N,f,Ae,Pe,B,L,He,d,m,u,Ie,Fe,ze,je,Ze,et],function(e,n,r,i,o,a,s,l,c,u,d,f,h,p,m,g,v,y,b,C,x,w,N,E,_,S,k,T,R,A){function B(e,t,i){var o=this,a,s,l;a=o.documentBaseUrl=i.documentBaseURL,s=i.baseURI,l=i.defaultSettings,t=P({id:e,theme:"modern",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:a,add_form_submit_trigger:!0,submit_patch:!0,add_unload_trigger:!0,convert_urls:!0,relative_urls:!0,remove_script_host:!0,object_resizing:!0,doctype:"",visual:!0,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",forced_root_block:"p",hidden_input:!0,padd_empty_editor:!0,render_ui:!0,indentation:"30px",inline_styles:!0,convert_fonts_to_spans:!0,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist",validate:!0,entity_encoding:"named",url_converter:o.convertURL,url_converter_scope:o,ie7_compat:!0},l,t),l&&l.external_plugins&&t.external_plugins&&(t.external_plugins=P({},l.external_plugins,t.external_plugins)),o.settings=t,r.language=t.language||"en",r.languageLoad=t.language_load,r.baseURL=i.baseURL,o.id=t.id=e,o.setDirty(!1),o.plugins={},o.documentBaseURI=new p(t.document_base_url||a,{base_uri:s}),o.baseURI=s,o.contentCSS=[],o.contentStyles=[],o.shortcuts=new k(o),o.loadedCSS={},o.editorCommands=new h(o),t.target&&(o.targetElm=t.target),o.suffix=i.suffix,o.editorManager=i,o.inline=t.inline,o.settings.content_editable=o.inline,t.cache_suffix&&(w.cacheSuffix=t.cache_suffix.replace(/^[\?\&]+/,"")),t.override_viewport===!1&&(w.overrideViewPort=!1),i.fire("SetupEditor",o),o.execCallback("setup",o),o.$=n.overrideDefaults(function(){return{context:o.inline?o.getBody():o.getDoc(),element:o.getBody()}})}var D=e.DOM,L=r.ThemeManager,M=r.PluginManager,P=N.extend,O=N.each,H=N.explode,I=N.inArray,F=N.trim,z=N.resolve,U=g.Event,W=w.gecko,V=w.ie;return B.prototype={render:function(){function e(){D.unbind(window,"ready",e),n.render()}function t(){var e=m.ScriptLoader;if(r.language&&"en"!=r.language&&!r.language_url&&(r.language_url=n.editorManager.baseURL+"/langs/"+r.language+".js"),r.language_url&&e.add(r.language_url),r.theme&&"function"!=typeof r.theme&&"-"!=r.theme.charAt(0)&&!L.urls[r.theme]){var t=r.theme_url;t=t?n.documentBaseURI.toAbsolute(t):"themes/"+r.theme+"/theme"+o+".js",L.load(r.theme,t)}N.isArray(r.plugins)&&(r.plugins=r.plugins.join(" ")),O(r.external_plugins,function(e,t){M.load(t,e),r.plugins+=" "+t}),O(r.plugins.split(/[ ,]/),function(e){if(e=F(e),e&&!M.urls[e])if("-"==e.charAt(0)){e=e.substr(1,e.length);var t=M.dependencies(e);O(t,function(e){var t={prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"};e=M.createUrl(t,e),M.load(e.resource,e)})}else M.load(e,{prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"})}),e.loadQueue(function(){n.removed||n.init()})}var n=this,r=n.settings,i=n.id,o=n.suffix;if(!U.domLoaded)return void D.bind(window,"ready",e);if(n.getElement()&&w.contentEditable){r.inline?n.inline=!0:(n.orgVisibility=n.getElement().style.visibility,n.getElement().style.visibility="hidden");var a=n.getElement().form||D.getParent(i,"form");a&&(n.formElement=a,r.hidden_input&&!/TEXTAREA|INPUT/i.test(n.getElement().nodeName)&&(D.insertAfter(D.create("input",{type:"hidden",name:i}),i),n.hasHiddenInput=!0),n.formEventDelegate=function(e){n.fire(e.type,e)},D.bind(a,"submit reset",n.formEventDelegate),n.on("reset",function(){n.setContent(n.startContent,{format:"raw"})}),!r.submit_patch||a.submit.nodeType||a.submit.length||a._mceOldSubmit||(a._mceOldSubmit=a.submit,a.submit=function(){return n.editorManager.triggerSave(),n.setDirty(!1),a._mceOldSubmit(a)})),n.windowManager=new v(n),n.notificationManager=new y(n),"xml"==r.encoding&&n.on("GetContent",function(e){e.save&&(e.content=D.encode(e.content))}),r.add_form_submit_trigger&&n.on("submit",function(){n.initialized&&n.save()}),r.add_unload_trigger&&(n._beforeUnload=function(){!n.initialized||n.destroyed||n.isHidden()||n.save({format:"raw",no_events:!0,set_dirty:!1})},n.editorManager.on("BeforeUnload",n._beforeUnload)),n.editorManager.add(n),t()}},init:function(){function e(n){var r=M.get(n),i,o;if(i=M.urls[n]||t.documentBaseUrl.replace(/\/$/,""),n=F(n),r&&-1===I(m,n)){if(O(M.dependencies(n),function(t){e(t)}),t.plugins[n])return;o=new r(t,i,t.$),t.plugins[n]=o,o.init&&(o.init(t,i),m.push(n))}}var t=this,n=t.settings,r=t.getElement(),i,o,a,s,l,c,u,d,f,h,p,m=[];if(t.rtl=n.rtl_ui||t.editorManager.i18n.rtl,t.editorManager.i18n.setCode(n.language),n.aria_label=n.aria_label||D.getAttrib(r,"aria-label",t.getLang("aria.rich_text_area")),t.fire("ScriptsLoaded"),n.theme&&("function"!=typeof n.theme?(n.theme=n.theme.replace(/-/,""),c=L.get(n.theme),t.theme=new c(t,L.urls[n.theme]),t.theme.init&&t.theme.init(t,L.urls[n.theme]||t.documentBaseUrl.replace(/\/$/,""),t.$)):t.theme=n.theme),O(n.plugins.replace(/\-/g,"").split(/[ ,]/),e),n.render_ui&&t.theme&&(t.orgDisplay=r.style.display,"function"!=typeof n.theme?(i=n.width||r.style.width||r.offsetWidth,o=n.height||r.style.height||r.offsetHeight,a=n.min_height||100,h=/^[0-9\.]+(|px)$/i,h.test(""+i)&&(i=Math.max(parseInt(i,10),100)),h.test(""+o)&&(o=Math.max(parseInt(o,10),a)),l=t.theme.renderUI({targetNode:r,width:i,height:o,deltaWidth:n.delta_width,deltaHeight:n.delta_height}),n.content_editable||(o=(l.iframeHeight||o)+("number"==typeof o?l.deltaHeight||0:""),a>o&&(o=a))):(l=n.theme(t,r),l.editorContainer.nodeType&&(l.editorContainer=l.editorContainer.id=l.editorContainer.id||t.id+"_parent"),l.iframeContainer.nodeType&&(l.iframeContainer=l.iframeContainer.id=l.iframeContainer.id||t.id+"_iframecontainer"),o=l.iframeHeight||r.offsetHeight),t.editorContainer=l.editorContainer),n.content_css&&O(H(n.content_css),function(e){t.contentCSS.push(t.documentBaseURI.toAbsolute(e))}),n.content_style&&t.contentStyles.push(n.content_style),n.content_editable)return r=s=l=null,t.initContentBody();if(t.iframeHTML=n.doctype+"",n.document_base_url!=t.documentBaseUrl&&(t.iframeHTML+=''),!w.caretAfter&&n.ie7_compat&&(t.iframeHTML+=''),t.iframeHTML+='',!/#$/.test(document.location.href))for(p=0;p',t.loadedCSS[g]=!0}d=n.body_id||"tinymce",-1!=d.indexOf("=")&&(d=t.getParam("body_id","","hash"),d=d[t.id]||d),f=n.body_class||"",-1!=f.indexOf("=")&&(f=t.getParam("body_class","","hash"),f=f[t.id]||""),n.content_security_policy&&(t.iframeHTML+=''),t.iframeHTML+='
    ';var v='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinymce.get("'+t.id+'");document.write(ed.iframeHTML);document.close();ed.initContentBody(true);})()';document.domain!=location.hostname&&w.ie&&w.ie<12&&(u=v);var y=D.create("iframe",{id:t.id+"_ifr",frameBorder:"0",allowTransparency:"true",title:t.editorManager.translate("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),style:{width:"100%",height:o,display:"block"}});if(y.onload=function(){y.onload=null,t.fire("load")},D.setAttrib(y,"src",u||'javascript:""'),t.contentAreaContainer=l.iframeContainer,t.iframeElement=y,s=D.add(l.iframeContainer,y),V)try{t.getDoc()}catch(b){s.src=u=v}l.editorContainer&&(D.get(l.editorContainer).style.display=t.orgDisplay,t.hidden=D.isHidden(l.editorContainer)),t.getElement().style.display="none",D.setAttrib(t.id,"aria-hidden",!0),u||t.initContentBody(),r=s=l=null},initContentBody:function(t){var n=this,r=n.settings,s=n.getElement(),h=n.getDoc(),p,m;r.inline||(n.getElement().style.visibility=n.orgVisibility),t||r.content_editable||(h.open(),h.write(n.iframeHTML),h.close()),r.content_editable&&(n.on("remove",function(){var e=this.getBody();D.removeClass(e,"mce-content-body"),D.removeClass(e,"mce-edit-focus"),D.setAttrib(e,"contentEditable",null)}),D.addClass(s,"mce-content-body"),n.contentDocument=h=r.content_document||document,n.contentWindow=r.content_window||window,n.bodyElement=s,r.content_document=r.content_window=null,r.root_name=s.nodeName.toLowerCase()),p=n.getBody(),p.disabled=!0,n.readonly=r.readonly,n.readonly||(n.inline&&"static"==D.getStyle(p,"position",!0)&&(p.style.position="relative"),p.contentEditable=n.getParam("content_editable_state",!0)),p.disabled=!1,n.editorUpload=new T(n),n.schema=new b(r),n.dom=new e(h,{keep_values:!0,url_converter:n.convertURL,url_converter_scope:n,hex_colors:r.force_hex_style_colors,class_filter:r.class_filter,update_styles:!0,root_element:n.inline?n.getBody():null,collect:r.content_editable,schema:n.schema,onSetAttrib:function(e){n.fire("SetAttrib",e)}}),n.parser=new C(r,n.schema),n.parser.addAttributeFilter("src,href,style,tabindex",function(e,t){for(var r=e.length,i,o=n.dom,a,s;r--;)if(i=e[r],a=i.attr(t),s="data-mce-"+t,!i.attributes.map[s]){if(0===a.indexOf("data:")||0===a.indexOf("blob:"))continue;"style"===t?(a=o.serializeStyle(o.parseStyle(a),i.name),a.length||(a=null),i.attr(s,a),i.attr(t,a)):"tabindex"===t?(i.attr(s,a),i.attr(t,null)):i.attr(s,n.convertURL(a,t,i.name))}}),n.parser.addNodeFilter("script",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.attr("type")||"no/type",0!==r.indexOf("mce-")&&n.attr("type","mce-"+r)}),n.parser.addNodeFilter("#cdata",function(e){for(var t=e.length,n;t--;)n=e[t],n.type=8,n.name="#comment",n.value="[CDATA["+n.value+"]]"}),n.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(e){for(var t=e.length,r,i=n.schema.getNonEmptyElements();t--;)r=e[t],r.isEmpty(i)&&(r.append(new o("br",1)).shortEnded=!0)}),n.serializer=new a(r,n),n.selection=new l(n.dom,n.getWin(),n.serializer,n),n.formatter=new c(n),n.undoManager=new u(n),n.forceBlocks=new f(n),n.enterKey=new d(n),n._nodeChangeDispatcher=new i(n),n._selectionOverrides=new R(n),n.fire("PreInit"),r.browser_spellcheck||r.gecko_spellcheck||(h.body.spellcheck=!1,D.setAttrib(p,"spellcheck","false")),n.quirks=new x(n),n.fire("PostRender"),r.directionality&&(p.dir=r.directionality),r.nowrap&&(p.style.whiteSpace="nowrap"),r.protect&&n.on("BeforeSetContent",function(e){O(r.protect,function(t){e.content=e.content.replace(t,function(e){return""})})}),n.on("SetContent",function(){n.addVisual(n.getBody())}),r.padd_empty_editor&&n.on("PostProcess",function(e){e.content=e.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
    [\r\n]*)$/,"")}),n.load({initial:!0,format:"html"}),n.startContent=n.getContent({format:"raw"}),n.initialized=!0,n.bindPendingEventDelegates(),n.fire("init"),n.focus(!0),n.nodeChanged({initial:!0}),n.execCallback("init_instance_callback",n),n.on("compositionstart compositionend",function(e){n.composing="compositionstart"===e.type}),n.contentStyles.length>0&&(m="",O(n.contentStyles,function(e){m+=e+"\r\n"}),n.dom.addStyle(m)),O(n.contentCSS,function(e){n.loadedCSS[e]||(n.dom.loadCSS(e),n.loadedCSS[e]=!0)}),r.auto_focus&&E.setEditorTimeout(n,function(){var e;e=r.auto_focus===!0?n:n.editorManager.get(r.auto_focus),e.destroyed||e.focus()},100),s=h=p=null},focus:function(e){function t(e){return n.dom.getParent(e,function(e){return"true"===n.dom.getContentEditable(e)})}var n=this,r=n.selection,i=n.settings.content_editable,o,a,s=n.getDoc(),l=n.getBody(),c;if(!e){if(o=r.getRng(),o.item&&(a=o.item(0)),n.quirks.refreshContentEditable(),c=t(r.getNode()),n.$.contains(l,c))return c.focus(),r.normalize(),void n.editorManager.setActive(n);if(i||(w.opera||n.getBody().focus(),n.getWin().focus()),W||i){if(l.setActive)try{l.setActive()}catch(u){l.focus()}else l.focus();i&&r.normalize()}a&&a.ownerDocument==s&&(o=s.body.createControlRange(),o.addElement(a),o.select())}n.editorManager.setActive(n)},execCallback:function(e){var t=this,n=t.settings[e],r;if(n)return t.callbackLookup&&(r=t.callbackLookup[e])&&(n=r.func,r=r.scope),"string"==typeof n&&(r=n.replace(/\.\w+$/,""),r=r?z(r):0,n=z(n),t.callbackLookup=t.callbackLookup||{},t.callbackLookup[e]={func:n,scope:r}),n.apply(r||t,Array.prototype.slice.call(arguments,1))},translate:function(e){var t=this.settings.language||"en",n=this.editorManager.i18n;return e?(e=n.data[t+"."+e]||e.replace(/\{\#([^\}]+)\}/g,function(e,r){return n.data[t+"."+r]||"{#"+r+"}"}),this.editorManager.translate(e)):""},getLang:function(e,n){return this.editorManager.i18n.data[(this.settings.language||"en")+"."+e]||(n!==t?n:"{#"+e+"}")},getParam:function(e,t,n){var r=e in this.settings?this.settings[e]:t,i;return"hash"===n?(i={},"string"==typeof r?O(r.indexOf("=")>0?r.split(/[;,](?![^=;,]*(?:[;,]|$))/):r.split(","),function(e){e=e.split("="),e.length>1?i[F(e[0])]=F(e[1]):i[F(e[0])]=F(e)}):i=r,i):r},nodeChanged:function(e){this._nodeChangeDispatcher.nodeChanged(e)},addButton:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),t.text||t.icon||(t.icon=e),n.buttons=n.buttons||{},t.tooltip=t.tooltip||t.title,n.buttons[e]=t},addMenuItem:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),n.menuItems=n.menuItems||{},n.menuItems[e]=t},addContextToolbar:function(e,t){var n=this,r;n.contextToolbars=n.contextToolbars||[],"string"==typeof e&&(r=e,e=function(e){return n.dom.is(e,r)}),n.contextToolbars.push({id:A.uuid("mcet"),predicate:e,items:t})},addCommand:function(e,t,n){this.editorCommands.addCommand(e,t,n)},addQueryStateHandler:function(e,t,n){this.editorCommands.addQueryStateHandler(e,t,n)},addQueryValueHandler:function(e,t,n){this.editorCommands.addQueryValueHandler(e,t,n)},addShortcut:function(e,t,n,r){this.shortcuts.add(e,t,n,r)},execCommand:function(e,t,n,r){return this.editorCommands.execCommand(e,t,n,r)},queryCommandState:function(e){return this.editorCommands.queryCommandState(e)},queryCommandValue:function(e){return this.editorCommands.queryCommandValue(e)},queryCommandSupported:function(e){return this.editorCommands.queryCommandSupported(e)},show:function(){var e=this;e.hidden&&(e.hidden=!1,e.inline?e.getBody().contentEditable=!0:(D.show(e.getContainer()),D.hide(e.id)),e.load(),e.fire("show"))},hide:function(){var e=this,t=e.getDoc();e.hidden||(V&&t&&!e.inline&&t.execCommand("SelectAll"),e.save(),e.inline?(e.getBody().contentEditable=!1,e==e.editorManager.focusedEditor&&(e.editorManager.focusedEditor=null)):(D.hide(e.getContainer()),D.setStyle(e.id,"display",e.orgDisplay)),e.hidden=!0,e.fire("hide"))},isHidden:function(){return!!this.hidden},setProgressState:function(e,t){this.fire("ProgressState",{state:e,time:t})},load:function(e){var n=this,r=n.getElement(),i;return r?(e=e||{},e.load=!0,i=n.setContent(r.value!==t?r.value:r.innerHTML,e),e.element=r,e.no_events||n.fire("LoadContent",e),e.element=r=null,i):void 0},save:function(e){var t=this,n=t.getElement(),r,i;if(n&&t.initialized)return e=e||{},e.save=!0,e.element=n,r=e.content=t.getContent(e),e.no_events||t.fire("SaveContent",e),"raw"==e.format&&t.fire("RawSaveContent",e),r=e.content,/TEXTAREA|INPUT/i.test(n.nodeName)?n.value=r:(t.inline||(n.innerHTML=r),(i=D.getParent(t.id,"form"))&&O(i.elements,function(e){return e.name==t.id?(e.value=r,!1):void 0})),e.element=n=null,e.set_dirty!==!1&&t.setDirty(!1),r},setContent:function(e,t){var n=this,r=n.getBody(),i,o;return t=t||{},t.format=t.format||"html",t.set=!0,t.content=e,t.no_events||n.fire("BeforeSetContent",t),e=t.content,0===e.length||/^\s+$/.test(e)?(o=V&&11>V?"":'
    ',"TABLE"==r.nodeName?e="
    "+o+"
    ',e+="",d=0;b>d;d++){for(e+="",c=0;a>c;c++)e+="";e+=""}return e+="",e+="

    "},d=function(a){var b=a.dom.select("*[data-mce-id]");return b[0]},e=function(a,b,e){a.undoManager.transact(function(){var f,g;a.insertContent(c(b,e)),f=d(a),f.removeAttribute("data-mce-id"),g=a.dom.select("td,th",f),a.selection.setCursorLocation(g[0],0)})},f=function(a,b){a.execCommand("FormatBlock",!1,b)},g=function(b,c,d){var e,f;e=b.editorUpload.blobCache,f=e.create(a.uuid("mceu"),d,c),e.add(f),b.insertContent(b.dom.createHTML("img",{src:f.blobUri()}))},h=function(a){a.selection.collapse(!1)},i=function(a){a.focus(),b.unlinkSelection(a),h(a)},j=function(a,b,c){a.focus(),a.dom.setAttrib(b,"href",c),h(a)},k=function(a,b){a.execCommand("mceInsertLink",!1,{href:b}),h(a)},l=function(a,b){var c=a.dom.getParent(a.selection.getStart(),"a[href]");c?j(a,c,b):k(a,b)},m=function(a,b){0===b.trim().length?i(a):l(a,b)};return{insertTable:e,formatBlock:f,insertBlob:g,createLink:m,unlink:i}}),g("r",[],function(){var a=function(a){return/^www\.|\.(com|org|edu|gov|uk|net|ca|de|jp|fr|au|us|ru|ch|it|nl|se|no|es|mil)$/i.test(a.trim())},b=function(a){return/^https?:\/\//.test(a.trim())};return{isDomainLike:a,isAbsolute:b}}),g("g",["c","d","o","m","r"],function(a,b,c,d,e){var f=function(a){a.find("textbox").eq(0).each(function(a){a.focus()})},g=function(c,d){var e=b.create(a.extend({type:"form",layout:"flex",direction:"row",padding:5,name:c,spacing:3},d));return e.on("show",function(){f(e)}),e},h=function(a,b){return b?a.show():a.hide()},i=function(a,b){return new c(function(c){a.windowManager.confirm("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",function(a){var d=a===!0?"http://"+b:b;c(d)})})},j=function(a,b){return!e.isAbsolute(b)&&e.isDomainLike(b)?i(a,b):c.resolve(b)},k=function(a,b){var c=function(){a.focus(),d.unlink(a),b()};return g("quicklink",{items:[{type:"button",name:"unlink",icon:"unlink",onclick:c,tooltip:"Remove link"},{type:"textbox",name:"linkurl",placeholder:"Paste or type a link"},{type:"button",icon:"checkmark",subtype:"primary",tooltip:"Ok",onclick:"submit"}],onshow:function(){var b,c="";b=a.dom.getParent(a.selection.getStart(),"a[href]"),b&&(c=a.dom.getAttrib(b,"href")),this.fromJSON({linkurl:c}),h(this.find("#unlink"),b)},onsubmit:function(c){j(a,c.data.linkurl).then(function(c){d.createLink(a,c),b()})}})};return{createQuickLinkForm:k}}),h("s",tinymce.geom.Rect),g("t",[],function(){var a=function(a){return{x:a.left,y:a.top,w:a.width,h:a.height}},b=function(a){return{left:a.x,top:a.y,width:a.w,height:a.h,right:a.x+a.w,bottom:a.y+a.h}};return{fromClientRect:a,toClientRect:b}}),g("h",["e","s","t"],function(a,b,c){var d=function(b){var c=a.getViewPort();return{x:b.x+c.x,y:b.y+c.y,w:b.w,h:b.h}},e=function(a){var b=a.getBoundingClientRect();return d({x:b.left,y:b.top,w:Math.max(a.clientWidth,a.offsetWidth),h:Math.max(a.clientHeight,a.offsetHeight)})},f=function(a,b){return e(b)},g=function(a){return e(a.getElement().ownerDocument.body)},h=function(a){return e(a.getContentAreaContainer()||a.getBody())},i=function(a){var b=a.selection.getBoundingClientRect();return b?d(c.fromClientRect(b)):null};return{getElementRect:f,getPageAreaRect:g,getContentAreaRect:h,getSelectionRect:i}}),g("i",["s","t"],function(a,b){var c=function(a,b){return{rect:a,position:b}},d=function(a,b){return{x:b.x,y:b.y,w:a.w,h:a.h}},e=function(b,e,f,g,h){var i,j,k;return i=a.findBestRelativePosition(h,f,g,b),f=a.clamp(f,g),i?(j=a.relativePosition(h,f,i),k=d(h,j),c(k,i)):(f=a.intersect(g,f),f?(i=a.findBestRelativePosition(h,f,g,e))?(j=a.relativePosition(h,f,i),k=d(h,j),c(k,i)):(k=d(h,f),c(k,i)):null)},f=function(a,b,c){return e(["cr-cl","cl-cr"],["bc-tc","bl-tl","br-tr"],a,b,c)},g=function(a,b,c){return e(["tc-bc","bc-tc","tl-bl","bl-tl","tr-br","br-tr"],["bc-tc","bl-tl","br-tr"],a,b,c)},h=function(a,c,d,e){var f;return"function"==typeof a?(f=a({elementRect:b.toClientRect(c),contentAreaRect:b.toClientRect(d),panelRect:b.toClientRect(e)}),b.fromClientRect(f)):e},i=function(a){return a.panelRect};return{calcInsert:f,calc:g,userConstrain:h,defaultHandler:i}}),g("a",["j"],function(a){var b=function(a,b){if(b(a))return!0;throw new Error("Default value doesn't match requested type.")},c=function(a){return function(c,d,e){var f=c.settings;return b(e,a),d in f&&a(f[d])?f[d]:e}},d=function(a,b){return a.split(b).filter(function(a){return a.length>0})},e=function(b,c){var e=function(a){return"string"==typeof a?d(a,/[ ,]/):a},f=function(a,b){return a===!1?[]:b};return a.isArray(b)?b:a.isString(b)?e(b):a.isBoolean(b)?f(b,c):c},f=function(a){return function(c,d,f){var g=d in c.settings?c.settings[d]:f;return b(f,a),e(g,f)}};return{getStringOr:c(a.isString),getBoolOr:c(a.isBoolean),getNumberOr:c(a.isNumber),getHandlerOr:c(a.isFunction),getToolbarItemsOr:f(a.isArray)}}),g("3",["c","d","e","f","g","h","i","a"],function(a,b,c,d,e,f,g,h){return function(){var i,j,k=["bold","italic","|","quicklink","h2","h3","blockquote"],l=["quickimage","quicktable"],m=function(b,c){return a.map(c,function(a){return d.create(b,a.id,a.items)})},n=function(a){return h.getToolbarItemsOr(a,"selection_toolbar",k)},o=function(a){return h.getToolbarItemsOr(a,"insert_toolbar",l)},p=function(a){return a.items().length>0},q=function(c,f){var g=m(c,f).concat([d.create(c,"text",n(c)),d.create(c,"insert",o(c)),e.createQuickLinkForm(c,z)]);return b.create({type:"floatpanel",role:"dialog",classes:"tinymce tinymce-inline arrow",ariaLabel:"Inline toolbar",layout:"flex",direction:"column",align:"stretch",autohide:!1,autofix:!0,fixed:!0,border:1,items:a.grep(g,p),oncancel:function(){c.focus()}})},r=function(a){a&&a.show()},s=function(a,b){a.moveTo(b.x,b.y)},t=function(b,c){c=c?c.substr(0,2):"",a.each({t:"down",b:"up",c:"center"},function(a,d){b.classes.toggle("arrow-"+a,d===c.substr(0,1))}),"cr"===c?(b.classes.toggle("arrow-left",!0),b.classes.toggle("arrow-right",!1)):"cl"===c?(b.classes.toggle("arrow-left",!0),b.classes.toggle("arrow-right",!0)):a.each({l:"left",r:"right"},function(a,d){b.classes.toggle("arrow-"+a,d===c.substr(1,1))})},u=function(a,b){var c=a.items().filter("#"+b);return c.length>0?(c[0].show(),a.reflow(),!0):!1},v=function(a,b,d,e){var i,k,l,m;return r(a),a.items().hide(),u(a,b)?(m=h.getHandlerOr(d,"inline_toolbar_position_handler",g.defaultHandler),i=f.getContentAreaRect(d),k=c.getRect(a.getEl()),l="insert"===b?g.calcInsert(e,i,k):g.calc(e,i,k),void(l?(k=l.rect,j=e,s(a,g.userConstrain(m,e,i,k)),t(a,l.position)):z(a))):void z(a)},w=function(){return i.items().filter("form:visible").length>0},x=function(a,b){if(i){if(i.items().hide(),!u(i,b))return void z(i);var d,e,k,l;r(i),i.items().hide(),u(i,b),l=h.getHandlerOr(a,"inline_toolbar_position_handler",g.defaultHandler),d=f.getContentAreaRect(a),e=c.getRect(i.getEl()),k=g.calc(j,d,e),k&&(e=k.rect,s(i,g.userConstrain(l,j,d,e)),t(i,k.position))}},y=function(a,b,c,d){i||(i=q(a,d),i.renderTo(document.body).reflow().moveTo(c.x,c.y),a.nodeChanged()),v(i,b,a,c)},z=function(){i&&i.hide()},A=function(){i&&i.find("toolbar:visible").eq(0).each(function(a){a.focus(!0)})},B=function(){i&&(i.remove(),i=null)},C=function(){return i&&i.visible()&&w()};return{show:y,showForm:x,inForm:C,hide:z,focus:A,remove:B}}}),g("k",["o"],function(a){var b=function(b){return new a(function(a){var c=new FileReader;c.onloadend=function(){a(c.result.split(",")[1])},c.readAsDataURL(b)})};return{blobToBase64:b}}),g("l",["o"],function(a){var b=function(){return new a(function(a){var b;b=document.createElement("input"),b.type="file",b.style.position="fixed",b.style.left=0,b.style.top=0,b.style.opacity=.001,document.body.appendChild(b),b.onchange=function(b){a(Array.prototype.slice.call(b.target.files))},b.click(),b.parentNode.removeChild(b)})};return{pickFile:b}}),g("4",["3","k","l","m"],function(a,b,c,d){var e=function(a){for(var b=function(b){return function(){d.formatBlock(a,b)}},c=1;6>c;c++){var e="h"+c;a.addButton(e,{text:e.toUpperCase(),tooltip:"Heading "+c,stateSelector:e,onclick:b(e),onPostRender:function(){var a=this.getEl().firstChild.firstChild;a.style.fontWeight="bold"}})}},f=function(a,f){a.addButton("quicklink",{icon:"link",tooltip:"Insert/Edit link",stateSelector:"a[href]",onclick:function(){f.showForm(a,"quicklink")}}),a.addButton("quickimage",{icon:"image",tooltip:"Insert image",onclick:function(){c.pickFile().then(function(c){var e=c[0];b.blobToBase64(e).then(function(b){d.insertBlob(a,b,e)})})}}),a.addButton("quicktable",{icon:"table",tooltip:"Insert table",onclick:function(){f.hide(),d.insertTable(a,2,2)}}),e(a)};return{addToEditor:f}}),h("n",tinymce.EditorManager),g("5",["n","e"],function(a,b){var c=function(a,b){var c=function(){a.fire("SkinLoaded"),b()};a.initialized?c():a.on("init",c)},d=function(b){var c=a.baseURL+"/skins/";return b?c+b:c+"lightgray"},e=function(a,b){return a.documentBaseURI.toAbsolute(b)},f=function(a,f){var g=a.settings,h=g.skin_url?e(a,g.skin_url):d(g.skin),i=function(){c(a,f)};b.styleSheetLoader.load(h+"/skin.min.css",i),a.contentCSS.push(h+"/content.inline.min.css")};return{load:f}}),g("8",[],function(){var a=function(a,b){return{id:a,rect:b}},b=function(a,b){for(var c=0;c li",c("UL")),"numlist"==g&&d.selectorChanged("ol > li",c("OL")),b.settings.stateSelector&&d.selectorChanged(b.settings.stateSelector,function(a){b.active(a)},!0),b.settings.disabledStateSelector&&d.selectorChanged(b.settings.disabledStateSelector,function(a){b.disabled(a)})}var g;"|"==b?d=null:n.has(b)?(b={type:b,size:c},e.push(b),d=null):(d||(d={type:"buttongroup",items:[]},e.push(d)),a.buttons[b]&&(g=b,b=a.buttons[g],"function"==typeof b&&(b=b()),b.type=b.type||"button",b.size=c,b=n.create(b),d.items.push(b),a.initialized?f():a.on("init",f)))}),{type:"toolbar",layout:"flow",items:e}}function c(a){function c(c){return c?(d.push(b(c,a)),!0):void 0}var d=[];if(tinymce.isArray(m.toolbar)){if(0===m.toolbar.length)return;tinymce.each(m.toolbar,function(a,b){m["toolbar"+(b+1)]=a}),delete m.toolbar}for(var e=1;10>e&&c(m["toolbar"+e]);e++);return d.length||m.toolbar===!1||c(m.toolbar||t),d.length?{type:"panel",layout:"stack",classes:"toolbar-grp",ariaRoot:!0,ariaRemember:!0,items:d}:void 0}function d(){function b(b){var c;return"|"==b?{text:"|"}:c=a.menuItems[b]}function c(c){var d,e,f,g,h;if(h=tinymce.makeMap((m.removed_menuitems||"").split(/[ ,]/)),m.menu?(e=m.menu[c],g=!0):e=s[c],e){d={text:e.title},f=[],o((e.items||"").split(/[ ,]/),function(a){var c=b(a);c&&!h[a]&&f.push(b(a))}),g||o(a.menuItems,function(a){a.context==c&&("before"==a.separator&&f.push({text:"|"}),a.prependToContext?f.unshift(a):f.push(a),"after"==a.separator&&f.push({text:"|"}))});for(var i=0;i=0;d--)for(e=g.length-1;e>=0;e--)if(g[e].predicate(f[d]))return{toolbar:g[e],element:f[d]};return null}var t;a.on("click keyup setContent",function(b){("setcontent"!=b.type||b.selection)&&tinymce.util.Delay.setEditorTimeout(a,function(){var b;b=s(a.selection.getNode()),b?(r(),p(b)):r()})}),a.on("blur hide",r),a.on("ObjectResizeStart",function(){var b=s(a.selection.getNode());b&&b.toolbar.panel&&b.toolbar.panel.hide()}),a.on("nodeChange ResizeEditor ResizeWindow",k),a.on("remove",function(){tinymce.each(c(),function(a){a.panel&&a.panel.remove()}),a.contextToolbars={}}),a.shortcuts.add("ctrl+shift+e > ctrl+shift+p","",function(){var b=s(a.selection.getNode());b&&b.toolbar.panel&&b.toolbar.panel.items()[0].focus()})}function i(a){return function(){a.initialized?a.fire("SkinLoaded"):a.on("init",function(){a.fire("SkinLoaded")})}}function j(b){function f(){if(o&&o.moveRel&&o.visible()&&!o._fixed){var b=a.selection.getScrollContainer(),c=a.getBody(),d=0,e=0;if(b){var f=p.getPos(c),g=p.getPos(b);d=Math.max(0,g.x-f.x),e=Math.max(0,g.y-f.y)}o.fixed(!1).moveRel(c,a.rtl?["tr-br","br-tr"]:["tl-bl","bl-tl","tr-br"]).moveBy(d,e)}}function g(){o&&(o.show(),f(),p.addClass(a.getBody(),"mce-edit-focus"))}function j(){o&&(o.hide(),r.hideAll(),p.removeClass(a.getBody(),"mce-edit-focus"))}function k(){return o?void(o.visible()||g()):(o=l.panel=n.create({type:q?"panel":"floatpanel",role:"application",classes:"tinymce tinymce-inline",layout:"flex",direction:"column",align:"stretch",autohide:!1,autofix:!0,fixed:!!q,border:1,items:[m.menubar===!1?null:{type:"menubar",border:"0 0 1 0",items:d()},c(m.toolbar_items_size)]}),a.fire("BeforeRenderUI"),o.renderTo(q||document.body).reflow(),e(o),g(),h(),a.on("nodeChange",f),a.on("activate",g),a.on("deactivate",j),void a.nodeChanged())}var o,q;return m.fixed_toolbar_container&&(q=p.select(m.fixed_toolbar_container)[0]),m.content_editable=!0,a.on("focus",function(){b.skinUiCss?tinymce.DOM.styleSheetLoader.load(b.skinUiCss,k,k):k()}),a.on("blur hide",j),a.on("remove",function(){o&&(o.remove(),o=null)}),b.skinUiCss&&tinymce.DOM.styleSheetLoader.load(b.skinUiCss,i(a)),{}}function k(b){function g(){return function(a){"readonly"==a.mode?j.find("*").disabled(!0):j.find("*").disabled(!1)}}var j,k,o;return b.skinUiCss&&tinymce.DOM.styleSheetLoader.load(b.skinUiCss,i(a)),j=l.panel=n.create({type:"panel",role:"application",classes:"tinymce",style:"visibility: hidden",layout:"stack",border:1,items:[m.menubar===!1?null:{type:"menubar",border:"0 0 1 0",items:d()},c(m.toolbar_items_size),{type:"panel",name:"iframe",layout:"stack",classes:"edit-area",html:"",border:"1 0 0 0"}]}),m.resize!==!1&&(k={type:"resizehandle",direction:m.resize,onResizeStart:function(){var b=a.getContentAreaContainer().firstChild;o={width:b.clientWidth,height:b.clientHeight}},onResize:function(a){"both"==m.resize?f(o.width+a.deltaX,o.height+a.deltaY):f(null,o.height+a.deltaY)}}),m.statusbar!==!1&&j.add({type:"panel",name:"statusbar",classes:"statusbar",layout:"flow",border:"1 0 0 0",ariaRoot:!0,items:[{type:"elementpath",editor:a},k]}),a.fire("BeforeRenderUI"),a.on("SwitchMode",g()),j.renderBefore(b.targetNode).reflow(),m.readonly&&a.setMode("readonly"),m.width&&tinymce.DOM.setStyle(j.getEl(),"width",m.width),a.on("remove",function(){j.remove(),j=null}),e(j),h(),{iframeContainer:j.find("#iframe")[0].getEl(),editorContainer:j.getEl()}}var l=this,m=a.settings,n=tinymce.ui.Factory,o=tinymce.each,p=tinymce.DOM,q=tinymce.geom.Rect,r=tinymce.ui.FloatPanel,s={file:{title:"File",items:"newdocument"},edit:{title:"Edit",items:"undo redo | cut copy paste pastetext | selectall"},insert:{title:"Insert",items:"|"},view:{title:"View",items:"visualaid |"},format:{title:"Format",items:"bold italic underline strikethrough superscript subscript | formats | removeformat"},table:{title:"Table"},tools:{title:"Tools"}},t="undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image";l.renderUI=function(b){var c=m.skin!==!1?m.skin||"lightgray":!1;if(c){var d=m.skin_url;d=d?a.documentBaseURI.toAbsolute(d):tinymce.baseURL+"/skins/"+c,tinymce.Env.documentMode<=7?b.skinUiCss=d+"/skin.ie7.min.css":b.skinUiCss=d+"/skin.min.css",a.contentCSS.push(d+"/content"+(a.inline?".inline":"")+".min.css")}return a.on("ProgressState",function(a){l.throbber=l.throbber||new tinymce.ui.Throbber(l.panel.getEl("body")),a.state?l.throbber.show(a.time):l.throbber.hide()}),m.inline?j(b):k(b)},l.resizeTo=f,l.resizeBy=g}); +tinymce.PluginManager.add("advlist",function(a){function b(a,b){var c=[];return tinymce.each(b.split(/[ ,]/),function(a){c.push({text:a.replace(/\-/g," ").replace(/\b\w/g,function(a){return a.toUpperCase()}),data:"default"==a?"":a})}),c}function c(b,c){a.undoManager.transact(function(){var d,e=a.dom,f=a.selection;if(d=e.getParent(f.getNode(),"ol,ul"),!d||d.nodeName!=b||c===!1){var h={"list-style-type":c?c:""};a.execCommand("UL"==b?"InsertUnorderedList":"InsertOrderedList",!1,h)}c=c===!1?g[b]:c,g[b]=c,d=e.getParent(f.getNode(),"ol,ul"),d&&(e.setStyle(d,"listStyleType",c?c:null),d.removeAttribute("data-mce-style")),a.focus()})}function d(b){var c=a.dom.getStyle(a.dom.getParent(a.selection.getNode(),"ol,ul"),"listStyleType")||"";b.control.items().each(function(a){a.active(a.settings.data===c)})}var e,f,g={};e=b("OL",a.getParam("advlist_number_styles","default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman")),f=b("UL",a.getParam("advlist_bullet_styles","default,circle,disc,square")),a.addButton("numlist",{type:"splitbutton",tooltip:"Numbered list",menu:e,onshow:d,onselect:function(a){c("OL",a.control.settings.data)},onclick:function(){c("OL",!1)}}),a.addButton("bullist",{type:"splitbutton",tooltip:"Bullet list",menu:f,onshow:d,onselect:function(a){c("UL",a.control.settings.data)},onclick:function(){c("UL",!1)}})}); +tinymce.PluginManager.add("anchor",function(a){function b(){var b=a.selection.getNode(),c="",d="A"==b.tagName&&""===a.dom.getAttrib(b,"href");d&&(c=b.name||b.id||""),a.windowManager.open({title:"Anchor",body:{type:"textbox",name:"name",size:40,label:"Name",value:c},onsubmit:function(c){var e=c.data.name;d?b.id=e:(a.selection.collapse(!0),a.execCommand("mceInsertContent",!1,a.dom.createHTML("a",{id:e})))}})}a.addCommand("mceAnchor",b),a.addButton("anchor",{icon:"anchor",tooltip:"Anchor",onclick:b,stateSelector:"a:not([href])"}),a.addMenuItem("anchor",{icon:"anchor",text:"Anchor",context:"insert",onclick:b})}); +tinymce.PluginManager.add("autolink",function(a){function b(a){e(a,-1,"(",!0)}function c(a){e(a,0,"",!0)}function d(a){e(a,-1,"",!1)}function e(a,b,c){function d(a,b){if(0>b&&(b=0),3==a.nodeType){var c=a.data.length;b>c&&(b=c)}return b}function e(a,b){1!=a.nodeType||a.hasChildNodes()?h.setStart(a,d(a,b)):h.setStartBefore(a)}function f(a,b){1!=a.nodeType||a.hasChildNodes()?h.setEnd(a,d(a,b)):h.setEndAfter(a)}var h,i,j,k,l,m,n,o,p,q;if("A"!=a.selection.getNode().tagName){if(h=a.selection.getRng(!0).cloneRange(),h.startOffset<5){if(o=h.endContainer.previousSibling,!o){if(!h.endContainer.firstChild||!h.endContainer.firstChild.nextSibling)return;o=h.endContainer.firstChild.nextSibling}if(p=o.length,e(o,p),f(o,p),h.endOffset<5)return;i=h.endOffset,k=o}else{if(k=h.endContainer,3!=k.nodeType&&k.firstChild){for(;3!=k.nodeType&&k.firstChild;)k=k.firstChild;3==k.nodeType&&(e(k,0),f(k,k.nodeValue.length))}i=1==h.endOffset?2:h.endOffset-1-b}j=i;do e(k,i>=2?i-2:0),f(k,i>=1?i-1:0),i-=1,q=h.toString();while(" "!=q&&""!==q&&160!=q.charCodeAt(0)&&i-2>=0&&q!=c);h.toString()==c||160==h.toString().charCodeAt(0)?(e(k,i),f(k,j),i+=1):0===h.startOffset?(e(k,0),f(k,j)):(e(k,i),f(k,j)),m=h.toString(),"."==m.charAt(m.length-1)&&f(k,j-1),m=h.toString(),n=m.match(g),n&&("www."==n[1]?n[1]="http://www.":/@$/.test(n[1])&&!/^mailto:/.test(n[1])&&(n[1]="mailto:"+n[1]),l=a.selection.getBookmark(),a.selection.setRng(h),a.execCommand("createlink",!1,n[1]+n[2]),a.selection.moveToBookmark(l),a.nodeChanged())}}var f,g=/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i;return a.settings.autolink_pattern&&(g=a.settings.autolink_pattern),a.on("keydown",function(b){return 13==b.keyCode?d(a):void 0}),tinymce.Env.ie?void a.on("focus",function(){if(!f){f=!0;try{a.execCommand("AutoUrlDetect",!1,!0)}catch(b){}}}):(a.on("keypress",function(c){return 41==c.keyCode?b(a):void 0}),void a.on("keyup",function(b){return 32==b.keyCode?c(a):void 0}))}); +tinymce.PluginManager.add("autoresize",function(a){function b(){return a.plugins.fullscreen&&a.plugins.fullscreen.isFullscreen()}function c(d){var g,h,i,j,k,l,m,n,o,p,q,r,s=tinymce.DOM;if(h=a.getDoc()){if(i=h.body,j=h.documentElement,k=e.autoresize_min_height,!i||d&&"setcontent"===d.type&&d.initial||b())return void(i&&j&&(i.style.overflowY="auto",j.style.overflowY="auto"));m=a.dom.getStyle(i,"margin-top",!0),n=a.dom.getStyle(i,"margin-bottom",!0),o=a.dom.getStyle(i,"padding-top",!0),p=a.dom.getStyle(i,"padding-bottom",!0),q=a.dom.getStyle(i,"border-top-width",!0),r=a.dom.getStyle(i,"border-bottom-width",!0),l=i.offsetHeight+parseInt(m,10)+parseInt(n,10)+parseInt(o,10)+parseInt(p,10)+parseInt(q,10)+parseInt(r,10),(isNaN(l)||0>=l)&&(l=tinymce.Env.ie?i.scrollHeight:tinymce.Env.webkit&&0===i.clientHeight?0:i.offsetHeight),l>e.autoresize_min_height&&(k=l),e.autoresize_max_height&&l>e.autoresize_max_height?(k=e.autoresize_max_height,i.style.overflowY="auto",j.style.overflowY="auto"):(i.style.overflowY="hidden",j.style.overflowY="hidden",i.scrollTop=0),k!==f&&(g=k-f,s.setStyle(a.iframeElement,"height",k+"px"),f=k,tinymce.isWebKit&&0>g&&c(d))}}function d(b,e,f){tinymce.util.Delay.setEditorTimeout(a,function(){c({}),b--?d(b,e,f):f&&f()},e)}var e=a.settings,f=0;a.settings.inline||(e.autoresize_min_height=parseInt(a.getParam("autoresize_min_height",a.getElement().offsetHeight),10),e.autoresize_max_height=parseInt(a.getParam("autoresize_max_height",0),10),a.on("init",function(){var b,c;b=a.getParam("autoresize_overflow_padding",1),c=a.getParam("autoresize_bottom_margin",50),b!==!1&&a.dom.setStyles(a.getBody(),{paddingLeft:b,paddingRight:b}),c!==!1&&a.dom.setStyles(a.getBody(),{paddingBottom:c})}),a.on("nodechange setcontent keyup FullscreenStateChanged",c),a.getParam("autoresize_on_init",!0)&&a.on("init",function(){d(20,100,function(){d(5,1e3)})}),a.addCommand("mceAutoResize",c))}); +tinymce._beforeUnloadHandler=function(){var a;return tinymce.each(tinymce.editors,function(b){b.plugins.autosave&&b.plugins.autosave.storeDraft(),!a&&b.isDirty()&&b.getParam("autosave_ask_before_unload",!0)&&(a=b.translate("You have unsaved changes are you sure you want to navigate away?"))}),a},tinymce.PluginManager.add("autosave",function(a){function b(a,b){var c={s:1e3,m:6e4};return a=/^(\d+)([ms]?)$/.exec(""+(a||b)),(a[2]?c[a[2]]:1)*parseInt(a,10)}function c(){var a=parseInt(n.getItem(k+"time"),10)||0;return(new Date).getTime()-a>m.autosave_retention?(d(!1),!1):!0}function d(b){n.removeItem(k+"draft"),n.removeItem(k+"time"),b!==!1&&a.fire("RemoveDraft")}function e(){!j()&&a.isDirty()&&(n.setItem(k+"draft",a.getContent({format:"raw",no_events:!0})),n.setItem(k+"time",(new Date).getTime()),a.fire("StoreDraft"))}function f(){c()&&(a.setContent(n.getItem(k+"draft"),{format:"raw"}),a.fire("RestoreDraft"))}function g(){l||(setInterval(function(){a.removed||e()},m.autosave_interval),l=!0)}function h(){var b=this;b.disabled(!c()),a.on("StoreDraft RestoreDraft RemoveDraft",function(){b.disabled(!c())}),g()}function i(){a.undoManager.beforeChange(),f(),d(),a.undoManager.add()}function j(b){var c=a.settings.forced_root_block;return b=tinymce.trim("undefined"==typeof b?a.getBody().innerHTML:b),""===b||new RegExp("^<"+c+"[^>]*>((\xa0| |[ ]|]*>)+?|)|
    $","i").test(b)}var k,l,m=a.settings,n=tinymce.util.LocalStorage;k=m.autosave_prefix||"tinymce-autosave-{path}{query}-{id}-",k=k.replace(/\{path\}/g,document.location.pathname),k=k.replace(/\{query\}/g,document.location.search),k=k.replace(/\{id\}/g,a.id),m.autosave_interval=b(m.autosave_interval,"30s"),m.autosave_retention=b(m.autosave_retention,"20m"),a.addButton("restoredraft",{title:"Restore last draft",onclick:i,onPostRender:h}),a.addMenuItem("restoredraft",{text:"Restore last draft",onclick:i,onPostRender:h,context:"file"}),a.settings.autosave_restore_when_empty!==!1&&(a.on("init",function(){c()&&j()&&f()}),a.on("saveContent",function(){d()})),window.onbeforeunload=tinymce._beforeUnloadHandler,this.hasDraft=c,this.storeDraft=e,this.restoreDraft=f,this.removeDraft=d,this.isEmpty=j}); +!function(){tinymce.create("tinymce.plugins.BBCodePlugin",{init:function(a){var b=this,c=a.getParam("bbcode_dialect","punbb").toLowerCase();a.on("beforeSetContent",function(a){a.content=b["_"+c+"_bbcode2html"](a.content)}),a.on("postProcess",function(a){a.set&&(a.content=b["_"+c+"_bbcode2html"](a.content)),a.get&&(a.content=b["_"+c+"_html2bbcode"](a.content))})},getInfo:function(){return{longname:"BBCode Plugin",author:"Ephox Corp",authorurl:"http://www.tinymce.com",infourl:"http://www.tinymce.com/wiki.php/Plugin:bbcode"}},_punbb_html2bbcode:function(a){function b(b,c){a=a.replace(b,c)}return a=tinymce.trim(a),b(/(.*?)<\/a>/gi,"[url=$1]$2[/url]"),b(/(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),b(/(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),b(/(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),b(/(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),b(/(.*?)<\/span>/gi,"[color=$1]$2[/color]"),b(/(.*?)<\/font>/gi,"[color=$1]$2[/color]"),b(/(.*?)<\/span>/gi,"[size=$1]$2[/size]"),b(/(.*?)<\/font>/gi,"$1"),b(//gi,"[img]$1[/img]"),b(/(.*?)<\/span>/gi,"[code]$1[/code]"),b(/(.*?)<\/span>/gi,"[quote]$1[/quote]"),b(/(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]"),b(/(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]"),b(/(.*?)<\/em>/gi,"[code][i]$1[/i][/code]"),b(/(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]"),b(/(.*?)<\/u>/gi,"[code][u]$1[/u][/code]"),b(/(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]"),b(/<\/(strong|b)>/gi,"[/b]"),b(/<(strong|b)>/gi,"[b]"),b(/<\/(em|i)>/gi,"[/i]"),b(/<(em|i)>/gi,"[i]"),b(/<\/u>/gi,"[/u]"),b(/(.*?)<\/span>/gi,"[u]$1[/u]"),b(//gi,"[u]"),b(/]*>/gi,"[quote]"),b(/<\/blockquote>/gi,"[/quote]"),b(/
    /gi,"\n"),b(//gi,"\n"),b(/
    /gi,"\n"),b(/

    /gi,""),b(/<\/p>/gi,"\n"),b(/ |\u00a0/gi," "),b(/"/gi,'"'),b(/</gi,"<"),b(/>/gi,">"),b(/&/gi,"&"),a},_punbb_bbcode2html:function(a){function b(b,c){a=a.replace(b,c)}return a=tinymce.trim(a),b(/\n/gi,"
    "),b(/\[b\]/gi,""),b(/\[\/b\]/gi,""),b(/\[i\]/gi,""),b(/\[\/i\]/gi,""),b(/\[u\]/gi,""),b(/\[\/u\]/gi,""),b(/\[url=([^\]]+)\](.*?)\[\/url\]/gi,'$2'),b(/\[url\](.*?)\[\/url\]/gi,'$1'),b(/\[img\](.*?)\[\/img\]/gi,''),b(/\[color=(.*?)\](.*?)\[\/color\]/gi,'$2'),b(/\[code\](.*?)\[\/code\]/gi,'$1 '),b(/\[quote.*?\](.*?)\[\/quote\]/gi,'$1 '),a}}),tinymce.PluginManager.add("bbcode",tinymce.plugins.BBCodePlugin)}(); +tinymce.PluginManager.add("charmap",function(a){function b(){return[["160","no-break space"],["173","soft hyphen"],["34","quotation mark"],["162","cent sign"],["8364","euro sign"],["163","pound sign"],["165","yen sign"],["169","copyright sign"],["174","registered sign"],["8482","trade mark sign"],["8240","per mille sign"],["181","micro sign"],["183","middle dot"],["8226","bullet"],["8230","three dot leader"],["8242","minutes / feet"],["8243","seconds / inches"],["167","section sign"],["182","paragraph sign"],["223","sharp s / ess-zed"],["8249","single left-pointing angle quotation mark"],["8250","single right-pointing angle quotation mark"],["171","left pointing guillemet"],["187","right pointing guillemet"],["8216","left single quotation mark"],["8217","right single quotation mark"],["8220","left double quotation mark"],["8221","right double quotation mark"],["8218","single low-9 quotation mark"],["8222","double low-9 quotation mark"],["60","less-than sign"],["62","greater-than sign"],["8804","less-than or equal to"],["8805","greater-than or equal to"],["8211","en dash"],["8212","em dash"],["175","macron"],["8254","overline"],["164","currency sign"],["166","broken bar"],["168","diaeresis"],["161","inverted exclamation mark"],["191","turned question mark"],["710","circumflex accent"],["732","small tilde"],["176","degree sign"],["8722","minus sign"],["177","plus-minus sign"],["247","division sign"],["8260","fraction slash"],["215","multiplication sign"],["185","superscript one"],["178","superscript two"],["179","superscript three"],["188","fraction one quarter"],["189","fraction one half"],["190","fraction three quarters"],["402","function / florin"],["8747","integral"],["8721","n-ary sumation"],["8734","infinity"],["8730","square root"],["8764","similar to"],["8773","approximately equal to"],["8776","almost equal to"],["8800","not equal to"],["8801","identical to"],["8712","element of"],["8713","not an element of"],["8715","contains as member"],["8719","n-ary product"],["8743","logical and"],["8744","logical or"],["172","not sign"],["8745","intersection"],["8746","union"],["8706","partial differential"],["8704","for all"],["8707","there exists"],["8709","diameter"],["8711","backward difference"],["8727","asterisk operator"],["8733","proportional to"],["8736","angle"],["180","acute accent"],["184","cedilla"],["170","feminine ordinal indicator"],["186","masculine ordinal indicator"],["8224","dagger"],["8225","double dagger"],["192","A - grave"],["193","A - acute"],["194","A - circumflex"],["195","A - tilde"],["196","A - diaeresis"],["197","A - ring above"],["256","A - macron"],["198","ligature AE"],["199","C - cedilla"],["200","E - grave"],["201","E - acute"],["202","E - circumflex"],["203","E - diaeresis"],["274","E - macron"],["204","I - grave"],["205","I - acute"],["206","I - circumflex"],["207","I - diaeresis"],["298","I - macron"],["208","ETH"],["209","N - tilde"],["210","O - grave"],["211","O - acute"],["212","O - circumflex"],["213","O - tilde"],["214","O - diaeresis"],["216","O - slash"],["332","O - macron"],["338","ligature OE"],["352","S - caron"],["217","U - grave"],["218","U - acute"],["219","U - circumflex"],["220","U - diaeresis"],["362","U - macron"],["221","Y - acute"],["376","Y - diaeresis"],["562","Y - macron"],["222","THORN"],["224","a - grave"],["225","a - acute"],["226","a - circumflex"],["227","a - tilde"],["228","a - diaeresis"],["229","a - ring above"],["257","a - macron"],["230","ligature ae"],["231","c - cedilla"],["232","e - grave"],["233","e - acute"],["234","e - circumflex"],["235","e - diaeresis"],["275","e - macron"],["236","i - grave"],["237","i - acute"],["238","i - circumflex"],["239","i - diaeresis"],["299","i - macron"],["240","eth"],["241","n - tilde"],["242","o - grave"],["243","o - acute"],["244","o - circumflex"],["245","o - tilde"],["246","o - diaeresis"],["248","o slash"],["333","o macron"],["339","ligature oe"],["353","s - caron"],["249","u - grave"],["250","u - acute"],["251","u - circumflex"],["252","u - diaeresis"],["363","u - macron"],["253","y - acute"],["254","thorn"],["255","y - diaeresis"],["563","y - macron"],["913","Alpha"],["914","Beta"],["915","Gamma"],["916","Delta"],["917","Epsilon"],["918","Zeta"],["919","Eta"],["920","Theta"],["921","Iota"],["922","Kappa"],["923","Lambda"],["924","Mu"],["925","Nu"],["926","Xi"],["927","Omicron"],["928","Pi"],["929","Rho"],["931","Sigma"],["932","Tau"],["933","Upsilon"],["934","Phi"],["935","Chi"],["936","Psi"],["937","Omega"],["945","alpha"],["946","beta"],["947","gamma"],["948","delta"],["949","epsilon"],["950","zeta"],["951","eta"],["952","theta"],["953","iota"],["954","kappa"],["955","lambda"],["956","mu"],["957","nu"],["958","xi"],["959","omicron"],["960","pi"],["961","rho"],["962","final sigma"],["963","sigma"],["964","tau"],["965","upsilon"],["966","phi"],["967","chi"],["968","psi"],["969","omega"],["8501","alef symbol"],["982","pi symbol"],["8476","real part symbol"],["978","upsilon - hook symbol"],["8472","Weierstrass p"],["8465","imaginary part"],["8592","leftwards arrow"],["8593","upwards arrow"],["8594","rightwards arrow"],["8595","downwards arrow"],["8596","left right arrow"],["8629","carriage return"],["8656","leftwards double arrow"],["8657","upwards double arrow"],["8658","rightwards double arrow"],["8659","downwards double arrow"],["8660","left right double arrow"],["8756","therefore"],["8834","subset of"],["8835","superset of"],["8836","not a subset of"],["8838","subset of or equal to"],["8839","superset of or equal to"],["8853","circled plus"],["8855","circled times"],["8869","perpendicular"],["8901","dot operator"],["8968","left ceiling"],["8969","right ceiling"],["8970","left floor"],["8971","right floor"],["9001","left-pointing angle bracket"],["9002","right-pointing angle bracket"],["9674","lozenge"],["9824","black spade suit"],["9827","black club suit"],["9829","black heart suit"],["9830","black diamond suit"],["8194","en space"],["8195","em space"],["8201","thin space"],["8204","zero width non-joiner"],["8205","zero width joiner"],["8206","left-to-right mark"],["8207","right-to-left mark"]]}function c(a){return tinymce.util.Tools.grep(a,function(a){return i(a)&&2==a.length})}function d(a){return i(a)?[].concat(c(a)):"function"==typeof a?a():[]}function e(b){var c=a.settings;return c.charmap&&(b=d(c.charmap)),c.charmap_append?[].concat(b).concat(d(c.charmap_append)):b}function f(){return e(b())}function g(b){a.fire("insertCustomChar",{chr:b}).chr,a.execCommand("mceInsertContent",!1,b)}function h(){function b(a){for(;a;){if("TD"==a.nodeName)return a;a=a.parentNode}}var c,d,e,h;c='';var i=f(),j=Math.min(i.length,25),k=Math.ceil(i.length/j);for(e=0;k>e;e++){for(c+="",d=0;j>d;d++){var l=e*j+d;if(l
    '+(m?String.fromCharCode(parseInt(m[0],10)):" ")+"
    "}else c+="
    "}c+="";var n={type:"container",html:c,onclick:function(a){var c=a.target;/^(TD|DIV)$/.test(c.nodeName)&&b(c).firstChild&&(g(tinymce.trim(c.innerText||c.textContent)),a.ctrlKey||h.close())},onmouseover:function(a){var c=b(a.target);c&&c.firstChild?(h.find("#preview").text(c.firstChild.firstChild.data),h.find("#previewTitle").text(c.title)):(h.find("#preview").text(" "),h.find("#previewTitle").text(" "))}};h=a.windowManager.open({title:"Special character",spacing:10,padding:10,items:[n,{type:"container",layout:"flex",direction:"column",align:"center",spacing:5,minWidth:160,minHeight:160,items:[{type:"label",name:"preview",text:" ",style:"font-size: 40px; text-align: center",border:1,minWidth:140,minHeight:80},{type:"label",name:"previewTitle",text:" ",style:"text-align: center",border:1,minWidth:140,minHeight:80}]}],buttons:[{text:"Close",onclick:function(){h.close()}}]})}var i=tinymce.util.Tools.isArray;return a.addCommand("mceShowCharmap",h),a.addButton("charmap",{icon:"charmap",tooltip:"Special character",cmd:"mceShowCharmap"}),a.addMenuItem("charmap",{icon:"charmap",text:"Special character",cmd:"mceShowCharmap",context:"insert"}),{getCharMap:f,insertChar:g}}); +tinymce.PluginManager.add("code",function(a){function b(){var b=a.windowManager.open({title:"Source code",body:{type:"textbox",name:"code",multiline:!0,minWidth:a.getParam("code_dialog_width",600),minHeight:a.getParam("code_dialog_height",Math.min(tinymce.DOM.getViewPort().h-200,500)),spellcheck:!1,style:"direction: ltr; text-align: left"},onSubmit:function(b){a.focus(),a.undoManager.transact(function(){a.setContent(b.data.code)}),a.selection.setCursorLocation(),a.nodeChanged()}});b.find("#code").value(a.getContent({source_view:!0}))}a.addCommand("mceCodeEditor",b),a.addButton("code",{icon:"code",tooltip:"Source code",onclick:b}),a.addMenuItem("code",{icon:"code",text:"Source code",context:"tools",onclick:b})}); +!function(a,b){"use strict";function c(a,b){for(var c,d=[],f=0;fa.length)break a;if(!(q instanceof e)){k.lastIndex=0;var r=k.exec(q);if(r){m&&(n=r[1].length);var s=r.index-1+n,r=r[0].slice(n),t=r.length,u=s+t,v=q.slice(0,s+1),w=q.slice(u+1),x=[p,1];v&&x.push(v);var y=new e(h,l?c.tokenize(r,l):r,o);x.push(y),w&&x.push(w),Array.prototype.splice.apply(f,x)}}}}}return f},hooks:{all:{},add:function(a,b){var d=c.hooks.all;d[a]=d[a]||[],d[a].push(b)},run:function(a,b){var d=c.hooks.all[a];if(d&&d.length)for(var e,f=0;e=d[f++];)e(b)}}},d=c.Token=function(a,b,c){this.type=a,this.content=b,this.alias=c};return d.stringify=function(a,b,e){if("string"==typeof a)return a;if("Array"===c.util.type(a))return a.map(function(c){return d.stringify(c,b,a)}).join("");var f={type:a.type,content:d.stringify(a.content,b,e),tag:"span",classes:["token",a.type],attributes:{},language:b,parent:e};if("comment"==f.type&&(f.attributes.spellcheck="true"),a.alias){var g="Array"===c.util.type(a.alias)?a.alias:[a.alias];Array.prototype.push.apply(f.classes,g)}c.hooks.run("wrap",f);var h="";for(var i in f.attributes)h+=(h?" ":"")+i+'="'+(f.attributes[i]||"")+'"';return"<"+f.tag+' class="'+f.classes.join(" ")+'" '+h+">"+f.content+""},b.document?void 0:b.addEventListener?(b.addEventListener("message",function(a){var d=JSON.parse(a.data),e=d.language,f=d.code,g=d.immediateClose;b.postMessage(c.highlight(f,c.languages[e],e)),g&&b.close()},!1),b.Prism):b.Prism}();return"undefined"!=typeof module&&module.exports&&(module.exports=c),"undefined"!=typeof global&&(global.Prism=c),c.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[^\s>\/=.]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},c.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),c.languages.xml=c.languages.markup,c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,c.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},c.languages.css.atrule.inside.rest=c.util.clone(c.languages.css),c.languages.markup&&(c.languages.insertBefore("markup","tag",{style:{pattern:/[\w\W]*?<\/style>/i,inside:{tag:{pattern:/|<\/style>/i,inside:c.languages.markup.tag.inside},rest:c.languages.css},alias:"language-css"}}),c.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:c.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:c.languages.css}},alias:"language-css"}},c.languages.markup.tag)),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),c.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),c.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/i,inside:{tag:{pattern:/|<\/script>/i,inside:c.languages.markup.tag.inside},rest:c.languages.javascript},alias:"language-javascript"}}),c.languages.js=c.languages.javascript,c.languages.c=c.languages.extend("clike",{keyword:/\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,operator:/\-[>-]?|\+\+?|!=?|<>?=?|==?|&&?|\|?\||[~^%?*\/]/,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)[ful]*\b/i}),c.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+([^\r\n\\]|\\.|\\(?:\r\n?|\n))*/im,lookbehind:!0,alias:"property",inside:{string:{pattern:/(#\s*include\s*)(<.+?>|("|')(\\?.)+?\3)/,lookbehind:!0}}}}),delete c.languages.c["class-name"],delete c.languages.c["boolean"],c.languages.csharp=c.languages.extend("clike",{keyword:/\b(abstract|as|async|await|base|bool|break|byte|case|catch|char|checked|class|const|continue|decimal|default|delegate|do|double|else|enum|event|explicit|extern|false|finally|fixed|float|for|foreach|goto|if|implicit|in|int|interface|internal|is|lock|long|namespace|new|null|object|operator|out|override|params|private|protected|public|readonly|ref|return|sbyte|sealed|short|sizeof|stackalloc|static|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|unsafe|ushort|using|virtual|void|volatile|while|add|alias|ascending|async|await|descending|dynamic|from|get|global|group|into|join|let|orderby|partial|remove|select|set|value|var|where|yield)\b/,string:[/@("|')(\1\1|\\\1|\\?(?!\1)[\s\S])*\1/,/("|')(\\?.)*?\1/],number:/\b-?(0x[\da-f]+|\d*\.?\d+)\b/i}),c.languages.insertBefore("csharp","keyword",{preprocessor:{pattern:/(^\s*)#.*/m,lookbehind:!0}}),c.languages.cpp=c.languages.extend("c",{keyword:/\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,"boolean":/\b(true|false)\b/,operator:/[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|&{1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/}),c.languages.insertBefore("cpp","keyword",{"class-name":{pattern:/(class\s+)[a-z0-9_]+/i,lookbehind:!0}}),c.languages.java=c.languages.extend("clike",{keyword:/\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/,number:/\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+(?:e[+-]?\d+)?[df]?\b/i,operator:{pattern:/(^|[^.])(?:\+[+=]?|-[-=]?|!=?|<>?>?=?|==?|&[&=]?|\|[|=]?|\*=?|\/=?|%=?|\^=?|[?:~])/m,lookbehind:!0}}),c.languages.php=c.languages.extend("clike",{keyword:/\b(and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|private|protected|parent|throw|null|echo|print|trait|namespace|final|yield|goto|instanceof|finally|try|catch)\b/i,constant:/\b[A-Z0-9_]{2,}\b/,comment:{pattern:/(^|[^\\])(?:\/\*[\w\W]*?\*\/|\/\/.*)/,lookbehind:!0}}),c.languages.insertBefore("php","class-name",{"shell-comment":{pattern:/(^|[^\\])#.*/,lookbehind:!0,alias:"comment"}}),c.languages.insertBefore("php","keyword",{delimiter:/\?>|<\?(?:php)?/i,variable:/\$\w+\b/i,"package":{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/,lookbehind:!0,inside:{punctuation:/\\/}}}),c.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/,lookbehind:!0}}),c.languages.markup&&(c.hooks.add("before-highlight",function(a){"php"===a.language&&(a.tokenStack=[],a.backupCode=a.code,a.code=a.code.replace(/(?:<\?php|<\?)[\w\W]*?(?:\?>)/gi,function(b){return a.tokenStack.push(b),"{{{PHP"+a.tokenStack.length+"}}}"}))}),c.hooks.add("before-insert",function(a){"php"===a.language&&(a.code=a.backupCode,delete a.backupCode)}),c.hooks.add("after-highlight",function(a){if("php"===a.language){for(var b,d=0;b=a.tokenStack[d];d++)a.highlightedCode=a.highlightedCode.replace("{{{PHP"+(d+1)+"}}}",c.highlight(b,a.grammar,"php").replace(/\$/g,"$$$$"));a.element.innerHTML=a.highlightedCode}}),c.hooks.add("wrap",function(a){"php"===a.language&&"markup"===a.type&&(a.content=a.content.replace(/(\{\{\{PHP[0-9]+\}\}\})/g,'$1'))}),c.languages.insertBefore("php","comment",{markup:{pattern:/<[^?]\/?(.*?)>/,inside:c.languages.markup},php:/\{\{\{PHP[0-9]+\}\}\}/})),c.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},string:/"""[\s\S]+?"""|'''[\s\S]+?'''|("|')(?:\\?.)*?\1/,"function":{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_][a-zA-Z0-9_]*(?=\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)[a-z0-9_]+/i,lookbehind:!0},keyword:/\b(?:as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/,"boolean":/\b(?:True|False)\b/,number:/\b-?(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/,punctuation:/[{}[\];(),.:]/},function(a){a.languages.ruby=a.languages.extend("clike",{comment:/#(?!\{[^\r\n]*?\}).*/,keyword:/\b(alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|false|for|if|in|module|new|next|nil|not|or|raise|redo|require|rescue|retry|return|self|super|then|throw|true|undef|unless|until|when|while|yield)\b/});var b={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:a.util.clone(a.languages.ruby)}};a.languages.insertBefore("ruby","keyword",{regex:[{pattern:/%r([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/,inside:{interpolation:b}},{pattern:/%r<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/,inside:{interpolation:b}},{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}],variable:/[@$]+[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/,symbol:/:[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/}),a.languages.insertBefore("ruby","number",{builtin:/\b(Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|File|Fixnum|Fload|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z][a-zA-Z_0-9]*(?:[?!]|\b)/}),a.languages.ruby.string=[{pattern:/%[qQiIwWxs]?([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/,inside:{interpolation:b}},{pattern:/%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/,inside:{interpolation:b}},{pattern:/("|')(#\{[^}]+\}|\\(?:\r?\n|\r)|\\?.)*?\1/,inside:{interpolation:b}}]}(c),c}),d("tinymce/codesampleplugin/Utils",[],function(){function a(a){return a&&"PRE"==a.nodeName&&-1!==a.className.indexOf("language-")}function b(a){return function(b,c){return a(c)}}return{isCodeSample:a,trimArg:b}}),d("tinymce/codesampleplugin/Dialog",["tinymce/dom/DOMUtils","tinymce/codesampleplugin/Utils","tinymce/codesampleplugin/Prism"],function(a,b,c){function d(a){var b=[{text:"HTML/XML",value:"markup"},{text:"JavaScript",value:"javascript"},{text:"CSS",value:"css"},{text:"PHP",value:"php"},{text:"Ruby",value:"ruby"},{text:"Python",value:"python"},{text:"Java",value:"java"},{text:"C",value:"c"},{text:"C#",value:"csharp"},{text:"C++",value:"cpp"}],c=a.settings.codesample_languages;return c?c:b}function e(a,b,d){a.undoManager.transact(function(){var e=f(a);d=i.encode(d),e?(a.dom.setAttrib(e,"class","language-"+b),e.innerHTML=d,c.highlightElement(e),a.selection.select(e)):(a.insertContent('

    '+d+"
    "),a.selection.select(a.$("#__new").removeAttr("id")[0]))})}function f(a){var c=a.selection.getNode();return b.isCodeSample(c)?c:null}function g(a){var b=f(a);return b?b.textContent:""}function h(a){var b,c=f(a);return c?(b=c.className.match(/language-(\w+)/),b?b[1]:""):""}var i=a.DOM;return{open:function(a){a.windowManager.open({title:"Insert/Edit code sample",minWidth:Math.min(i.getViewPort().w,a.getParam("codesample_dialog_width",800)),minHeight:Math.min(i.getViewPort().h,a.getParam("codesample_dialog_height",650)),layout:"flex",direction:"column",align:"stretch",body:[{type:"listbox",name:"language",label:"Language",maxWidth:200,value:h(a),values:d(a)},{type:"textbox",name:"code",multiline:!0,spellcheck:!1,ariaLabel:"Code view",flex:1,style:"direction: ltr; text-align: left",classes:"monospace",value:g(a),autofocus:!0}],onSubmit:function(b){e(a,b.data.language,b.data.code)}})}}}),d("tinymce/codesampleplugin/Plugin",["tinymce/Env","tinymce/PluginManager","tinymce/codesampleplugin/Prism","tinymce/codesampleplugin/Dialog","tinymce/codesampleplugin/Utils"],function(a,b,c,d,e){var f,g=e.trimArg;b.add("codesample",function(b,h){function i(){var a;b.inline&&f||!b.inline&&j||(b.inline?f=!0:j=!0,a=b.dom.create("link",{rel:"stylesheet",href:h+"/css/prism.css"}),b.getDoc().getElementsByTagName("head")[0].appendChild(a))}var j,k=b.$;a.ceFalse&&(b.on("PreProcess",function(a){k("pre[contenteditable=false]",a.node).filter(g(e.isCodeSample)).each(function(a,b){var c=k(b),d=b.textContent;c.attr("class",k.trim(c.attr("class"))),c.removeAttr("contentEditable"),c.empty().append(k("").each(function(){this.textContent=d}))})}),b.on("SetContent",function(){var a=k("pre").filter(g(e.isCodeSample)).filter(function(a,b){return"false"!==b.contentEditable});a.length&&b.undoManager.transact(function(){a.each(function(a,d){k(d).find("br").each(function(a,c){c.parentNode.replaceChild(b.getDoc().createTextNode("\n"),c)}),d.contentEditable=!1,d.innerHTML=b.dom.encode(d.textContent),c.highlightElement(d),d.className=k.trim(d.className)})})}),b.addCommand("codesample",function(){d.open(b)}),b.addButton("codesample",{cmd:"codesample",title:"Insert/Edit code sample"}),b.on("init",i))})}),f(["tinymce/codesampleplugin/Prism","tinymce/codesampleplugin/Utils","tinymce/codesampleplugin/Dialog","tinymce/codesampleplugin/Plugin"])}(this); +tinymce.PluginManager.add("colorpicker",function(a){function b(b,c){function d(a){var b=new tinymce.util.Color(a),c=b.toRgb();f.fromJSON({r:c.r,g:c.g,b:c.b,hex:b.toHex().substr(1)}),e(b.toHex())}function e(a){f.find("#preview")[0].getEl().style.background=a}var f=a.windowManager.open({title:"Color",items:{type:"container",layout:"flex",direction:"row",align:"stretch",padding:5,spacing:10,items:[{type:"colorpicker",value:c,onchange:function(){var a=this.rgb();f&&(f.find("#r").value(a.r),f.find("#g").value(a.g),f.find("#b").value(a.b),f.find("#hex").value(this.value().substr(1)),e(this.value()))}},{type:"form",padding:0,labelGap:5,defaults:{type:"textbox",size:7,value:"0",flex:1,spellcheck:!1,onchange:function(){var a,b,c=f.find("colorpicker")[0];return a=this.name(),b=this.value(),"hex"==a?(b="#"+b,d(b),void c.value(b)):(b={r:f.find("#r").value(),g:f.find("#g").value(),b:f.find("#b").value()},c.value(b),void d(b))}},items:[{name:"r",label:"R",autofocus:1},{name:"g",label:"G"},{name:"b",label:"B"},{name:"hex",label:"#",value:"000000"},{name:"preview",type:"container",border:1}]}]},onSubmit:function(){b("#"+this.toJSON().hex)}});d(c)}a.settings.color_picker_callback||(a.settings.color_picker_callback=b)}); +tinymce.PluginManager.add("contextmenu",function(a){var b,c=a.settings.contextmenu_never_use_native,d=function(a){return a.ctrlKey&&!c},e=function(){return tinymce.Env.mac&&tinymce.Env.webkit};a.on("mousedown",function(b){e()&&2===b.button&&!d(b)&&a.selection.isCollapsed()&&a.once("contextmenu",function(b){a.selection.placeCaretAt(b.clientX,b.clientY)})}),a.on("contextmenu",function(c){var e;if(!d(c)){if(c.preventDefault(),e=a.settings.contextmenu||"link image inserttable | cell row column deletetable",b)b.show();else{var f=[];tinymce.each(e.split(/[ ,]/),function(b){var c=a.menuItems[b];"|"==b&&(c={text:b}),c&&(c.shortcut="",f.push(c))});for(var g=0;g
    "}var d=[["cool","cry","embarassed","foot-in-mouth"],["frown","innocent","kiss","laughing"],["money-mouth","sealed","smile","surprised"],["tongue-out","undecided","wink","yell"]];a.addButton("emoticons",{type:"panelbutton",panel:{role:"application",autohide:!0,html:c,onclick:function(b){var c=a.dom.getParent(b.target,"a");c&&(a.insertContent(''+c.getAttribute('),this.hide())}},tooltip:"Emoticons"})}); +tinymce.PluginManager.add("fullpage",function(a){function b(){var b=c();a.windowManager.open({title:"Document properties",data:b,defaults:{type:"textbox",size:40},body:[{name:"title",label:"Title"},{name:"keywords",label:"Keywords"},{name:"description",label:"Description"},{name:"robots",label:"Robots"},{name:"author",label:"Author"},{name:"docencoding",label:"Encoding"}],onSubmit:function(a){d(tinymce.extend(b,a.data))}})}function c(){function b(a,b){var c=a.attr(b);return c||""}var c,d,f=e(),g={};return g.fontface=a.getParam("fullpage_default_fontface",""),g.fontsize=a.getParam("fullpage_default_fontsize",""),c=f.firstChild,7==c.type&&(g.xml_pi=!0,d=/encoding="([^"]+)"/.exec(c.value),d&&(g.docencoding=d[1])),c=f.getAll("#doctype")[0],c&&(g.doctype=""),c=f.getAll("title")[0],c&&c.firstChild&&(g.title=c.firstChild.value),k(f.getAll("meta"),function(a){var b,c=a.attr("name"),d=a.attr("http-equiv");c?g[c.toLowerCase()]=a.attr("content"):"Content-Type"==d&&(b=/charset\s*=\s*(.*)\s*/gi.exec(a.attr("content")),b&&(g.docencoding=b[1]))}),c=f.getAll("html")[0],c&&(g.langcode=b(c,"lang")||b(c,"xml:lang")),g.stylesheets=[],tinymce.each(f.getAll("link"),function(a){"stylesheet"==a.attr("rel")&&g.stylesheets.push(a.attr("href"))}),c=f.getAll("body")[0],c&&(g.langdir=b(c,"dir"),g.style=b(c,"style"),g.visited_color=b(c,"vlink"),g.link_color=b(c,"link"),g.active_color=b(c,"alink")),g}function d(b){function c(a,b,c){a.attr(b,c?c:void 0)}function d(a){g.firstChild?g.insert(a,g.firstChild):g.append(a)}var f,g,h,j,m,n=a.dom;f=e(),g=f.getAll("head")[0],g||(j=f.getAll("html")[0],g=new l("head",1),j.firstChild?j.insert(g,j.firstChild,!0):j.append(g)),j=f.firstChild,b.xml_pi?(m='version="1.0"',b.docencoding&&(m+=' encoding="'+b.docencoding+'"'),7!=j.type&&(j=new l("xml",7),f.insert(j,f.firstChild,!0)),j.value=m):j&&7==j.type&&j.remove(),j=f.getAll("#doctype")[0],b.doctype?(j||(j=new l("#doctype",10),b.xml_pi?f.insert(j,f.firstChild):d(j)),j.value=b.doctype.substring(9,b.doctype.length-1)):j&&j.remove(),j=null,k(f.getAll("meta"),function(a){"Content-Type"==a.attr("http-equiv")&&(j=a)}),b.docencoding?(j||(j=new l("meta",1),j.attr("http-equiv","Content-Type"),j.shortEnded=!0,d(j)),j.attr("content","text/html; charset="+b.docencoding)):j&&j.remove(),j=f.getAll("title")[0],b.title?(j?j.empty():(j=new l("title",1),d(j)),j.append(new l("#text",3)).value=b.title):j&&j.remove(),k("keywords,description,author,copyright,robots".split(","),function(a){var c,e,g=f.getAll("meta"),h=b[a];for(c=0;c"))}function e(){return new tinymce.html.DomParser({validate:!1,root_name:"#document"}).parse(i)}function f(b){function c(a){return a.replace(/<\/?[A-Z]+/g,function(a){return a.toLowerCase()})}var d,f,h,l,m=b.content,n="",o=a.dom;if(!b.selection&&!("raw"==b.format&&i||b.source_view&&a.getParam("fullpage_hide_in_source_view"))){0!==m.length||b.source_view||(m=tinymce.trim(i)+"\n"+tinymce.trim(m)+"\n"+tinymce.trim(j)),m=m.replace(/<(\/?)BODY/gi,"<$1body"),d=m.indexOf("",d),i=c(m.substring(0,d+1)),f=m.indexOf("\n"),h=e(),k(h.getAll("style"),function(a){a.firstChild&&(n+=a.firstChild.value)}),l=h.getAll("body")[0],l&&o.setAttribs(a.getBody(),{style:l.attr("style")||"",dir:l.attr("dir")||"",vLink:l.attr("vlink")||"",link:l.attr("link")||"",aLink:l.attr("alink")||""}),o.remove("fullpage_styles");var p=a.getDoc().getElementsByTagName("head")[0];n&&(o.add(p,"style",{id:"fullpage_styles"},n),l=o.get("fullpage_styles"),l.styleSheet&&(l.styleSheet.cssText=n));var q={};tinymce.each(p.getElementsByTagName("link"),function(a){"stylesheet"==a.rel&&a.getAttribute("data-mce-fullpage")&&(q[a.href]=a)}),tinymce.each(h.getAll("link"),function(a){var b=a.attr("href");q[b]||"stylesheet"!=a.attr("rel")||o.add(p,"link",{rel:"stylesheet",text:"text/css",href:b,"data-mce-fullpage":"1"}),delete q[b]}),tinymce.each(q,function(a){a.parentNode.removeChild(a)})}}function g(){var b,c="",d="";return a.getParam("fullpage_default_xml_pi")&&(c+='\n'),c+=a.getParam("fullpage_default_doctype",""),c+="\n\n\n",(b=a.getParam("fullpage_default_title"))&&(c+=""+b+"\n"),(b=a.getParam("fullpage_default_encoding"))&&(c+='\n'),(b=a.getParam("fullpage_default_font_family"))&&(d+="font-family: "+b+";"),(b=a.getParam("fullpage_default_font_size"))&&(d+="font-size: "+b+";"),(b=a.getParam("fullpage_default_text_color"))&&(d+="color: "+b+";"),c+="\n\n"}function h(b){b.selection||b.source_view&&a.getParam("fullpage_hide_in_source_view")||(b.content=tinymce.trim(i)+"\n"+tinymce.trim(b.content)+"\n"+tinymce.trim(j))}var i,j,k=tinymce.each,l=tinymce.html.Node;a.addCommand("mceFullPageProperties",b),a.addButton("fullpage",{title:"Document properties",cmd:"mceFullPageProperties"}),a.addMenuItem("fullpage",{text:"Document properties",cmd:"mceFullPageProperties",context:"file"}),a.on("BeforeSetContent",f),a.on("GetContent",h)}); +tinymce.PluginManager.add("fullscreen",function(a){function b(){var a,b,c=window,d=document,e=d.body;return e.offsetWidth&&(a=e.offsetWidth,b=e.offsetHeight),c.innerWidth&&c.innerHeight&&(a=c.innerWidth,b=c.innerHeight),{w:a,h:b}}function c(){var a=tinymce.DOM.getViewPort();return{x:a.x,y:a.y}}function d(a){scrollTo(a.x,a.y)}function e(){function e(){m.setStyle(p,"height",b().h-(o.clientHeight-p.clientHeight))}var n,o,p,q,r=document.body,s=document.documentElement;l=!l,o=a.getContainer(),n=o.style,p=a.getContentAreaContainer().firstChild,q=p.style,l?(k=c(),f=q.width,g=q.height,q.width=q.height="100%",i=n.width,j=n.height,n.width=n.height="",m.addClass(r,"mce-fullscreen"),m.addClass(s,"mce-fullscreen"),m.addClass(o,"mce-fullscreen"),m.bind(window,"resize",e),e(),h=e):(q.width=f,q.height=g,i&&(n.width=i),j&&(n.height=j),m.removeClass(r,"mce-fullscreen"),m.removeClass(s,"mce-fullscreen"),m.removeClass(o,"mce-fullscreen"),m.unbind(window,"resize",h),d(k)),a.fire("FullscreenStateChanged",{state:l})}var f,g,h,i,j,k,l=!1,m=tinymce.DOM;return a.settings.inline?void 0:(a.on("init",function(){a.addShortcut("Ctrl+Shift+F","",e)}),a.on("remove",function(){h&&m.unbind(window,"resize",h)}),a.addCommand("mceFullScreen",e),a.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Ctrl+Shift+F",selectable:!0,onClick:function(){e(),a.focus()},onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})},context:"view"}),a.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Ctrl+Shift+F",onClick:e,onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})}}),{isFullscreen:function(){return l}})}); +tinymce.PluginManager.add("hr",function(a){a.addCommand("InsertHorizontalRule",function(){a.execCommand("mceInsertContent",!1,"
    ")}),a.addButton("hr",{icon:"hr",tooltip:"Horizontal line",cmd:"InsertHorizontalRule"}),a.addMenuItem("hr",{icon:"hr",text:"Horizontal line",cmd:"InsertHorizontalRule",context:"insert"})}); +tinymce.PluginManager.add("image",function(a){function b(a,b){function c(a,c){d.parentNode&&d.parentNode.removeChild(d),b({width:a,height:c})}var d=document.createElement("img");d.onload=function(){c(Math.max(d.width,d.clientWidth),Math.max(d.height,d.clientHeight))},d.onerror=function(){c()};var e=d.style;e.visibility="hidden",e.position="fixed",e.bottom=e.left=0,e.width=e.height="auto",document.body.appendChild(d),d.src=a}function c(a,b,c){function d(a,c){return c=c||[],tinymce.each(a,function(a){var e={text:a.text||a.title};a.menu?e.menu=d(a.menu):(e.value=a.value,b(e)),c.push(e)}),c}return d(a,c||[])}function d(b){return function(){var c=a.settings.image_list;"string"==typeof c?tinymce.util.XHR.send({url:c,success:function(a){b(tinymce.util.JSON.parse(a))}}):"function"==typeof c?c(b):b(c)}}function e(d){function e(){var a,b,c,d;a=l.find("#width")[0],b=l.find("#height")[0],a&&b&&(c=a.value(),d=b.value(),l.find("#constrain")[0].checked()&&o&&p&&c&&d&&(o!=c?(d=Math.round(c/o*d),isNaN(d)||b.value(d)):(c=Math.round(d/p*c),isNaN(c)||a.value(c))),o=c,p=d)}function f(){function b(b){function c(){b.onload=b.onerror=null,a.selection&&(a.selection.select(b),a.nodeChanged())}b.onload=function(){s.width||s.height||!u||t.setAttribs(b,{width:b.clientWidth,height:b.clientHeight}),c()},b.onerror=c}var c,d;j(),e(),s=tinymce.extend(s,l.toJSON()),s.alt||(s.alt=""),s.title||(s.title=""),""===s.width&&(s.width=null),""===s.height&&(s.height=null),s.style||(s.style=null),s={src:s.src,alt:s.alt,title:s.title,width:s.width,height:s.height,style:s.style,caption:s.caption,"class":s["class"]},a.undoManager.transact(function(){function e(b){return a.schema.getTextBlockElements()[b.nodeName]}if(!s.src)return void(m&&(t.remove(m),a.focus(),a.nodeChanged()));if(""===s.title&&(s.title=null),m?t.setAttribs(m,s):(s.id="__mcenew",a.focus(),a.selection.setContent(t.createHTML("img",s)),m=t.get("__mcenew"),t.setAttrib(m,"id",null)),a.editorUpload.uploadImagesAuto(),s.caption===!1&&t.is(m.parentNode,"figure.image")&&(c=m.parentNode,t.insertAfter(m,c),t.remove(c)),s.caption!==!0)b(m);else if(!t.is(m.parentNode,"figure.image")){d=m,m=m.cloneNode(!0),c=t.create("figure",{"class":"image"}),c.appendChild(m),c.appendChild(t.create("figcaption",{contentEditable:!0},"Caption")),c.contentEditable=!1;var f=t.getParent(d,e);f?t.split(f,d,c):t.replace(c,d),a.selection.select(c)}})}function g(a){return a&&(a=a.replace(/px$/,"")),a}function h(c){var d,e,f,g=c.meta||{};q&&q.value(a.convertURL(this.value(),"src")),tinymce.each(g,function(a,b){l.find("#"+b).value(a)}),g.width||g.height||(d=a.convertURL(this.value(),"src"),e=a.settings.image_prepend_url,f=new RegExp("^(?:[a-z]+:)?//","i"),e&&!f.test(d)&&d.substring(0,e.length)!==e&&(d=e+d),this.value(d),b(a.documentBaseURI.toAbsolute(this.value()),function(a){a.width&&a.height&&u&&(o=a.width,p=a.height,l.find("#width").value(o),l.find("#height").value(p))}))}function i(a){if(a.margin){var b=a.margin.split(" ");switch(b.length){case 1:a["margin-top"]=a["margin-top"]||b[0],a["margin-right"]=a["margin-right"]||b[0],a["margin-bottom"]=a["margin-bottom"]||b[0],a["margin-left"]=a["margin-left"]||b[0];break;case 2:a["margin-top"]=a["margin-top"]||b[0],a["margin-right"]=a["margin-right"]||b[1],a["margin-bottom"]=a["margin-bottom"]||b[0],a["margin-left"]=a["margin-left"]||b[1];break;case 3:a["margin-top"]=a["margin-top"]||b[0],a["margin-right"]=a["margin-right"]||b[1],a["margin-bottom"]=a["margin-bottom"]||b[2],a["margin-left"]=a["margin-left"]||b[1];break;case 4:a["margin-top"]=a["margin-top"]||b[0],a["margin-right"]=a["margin-right"]||b[1],a["margin-bottom"]=a["margin-bottom"]||b[2],a["margin-left"]=a["margin-left"]||b[3]}delete a.margin}return a}function j(){function b(a){return a.length>0&&/^[0-9]+$/.test(a)&&(a+="px"),a}if(a.settings.image_advtab){var c=l.toJSON(),d=t.parseStyle(c.style);d=i(d),c.vspace&&(d["margin-top"]=d["margin-bottom"]=b(c.vspace)),c.hspace&&(d["margin-left"]=d["margin-right"]=b(c.hspace)),c.border&&(d["border-width"]=b(c.border)),l.find("#style").value(t.serializeStyle(t.parseStyle(t.serializeStyle(d))))}}function k(){if(a.settings.image_advtab){var b=l.toJSON(),c=t.parseStyle(b.style);l.find("#vspace").value(""),l.find("#hspace").value(""),c=i(c),(c["margin-top"]&&c["margin-bottom"]||c["margin-right"]&&c["margin-left"])&&(c["margin-top"]===c["margin-bottom"]?l.find("#vspace").value(g(c["margin-top"])):l.find("#vspace").value(""),c["margin-right"]===c["margin-left"]?l.find("#hspace").value(g(c["margin-right"])):l.find("#hspace").value("")),c["border-width"]&&l.find("#border").value(g(c["border-width"])),l.find("#style").value(t.serializeStyle(t.parseStyle(t.serializeStyle(c))))}}var l,m,n,o,p,q,r,s={},t=a.dom,u=a.settings.image_dimensions!==!1;m=a.selection.getNode(),n=t.getParent(m,"figure.image"),n&&(m=t.select("img",n)[0]),m&&("IMG"!=m.nodeName||m.getAttribute("data-mce-object")||m.getAttribute("data-mce-placeholder"))&&(m=null),m&&(o=t.getAttrib(m,"width"),p=t.getAttrib(m,"height"),s={src:t.getAttrib(m,"src"),alt:t.getAttrib(m,"alt"),title:t.getAttrib(m,"title"),"class":t.getAttrib(m,"class"),width:o,height:p,caption:!!n}),d&&(q={type:"listbox",label:"Image list",values:c(d,function(b){b.value=a.convertURL(b.value||b.url,"src")},[{text:"None",value:""}]),value:s.src&&a.convertURL(s.src,"src"),onselect:function(a){var b=l.find("#alt");(!b.value()||a.lastControl&&b.value()==a.lastControl.text())&&b.value(a.control.text()),l.find("#src").value(a.control.value()).fire("change")},onPostRender:function(){q=this}}),a.settings.image_class_list&&(r={name:"class",type:"listbox",label:"Class",values:c(a.settings.image_class_list,function(b){b.value&&(b.textStyle=function(){return a.formatter.getCssText({inline:"img",classes:[b.value]})})})});var v=[{name:"src",type:"filepicker",filetype:"image",label:"Source",autofocus:!0,onchange:h},q];a.settings.image_description!==!1&&v.push({name:"alt",type:"textbox",label:"Image description"}),a.settings.image_title&&v.push({name:"title",type:"textbox",label:"Image Title"}),u&&v.push({type:"container",label:"Dimensions",layout:"flex",direction:"row",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:5,size:3,onchange:e,ariaLabel:"Width"},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:5,size:3,onchange:e,ariaLabel:"Height"},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}),v.push(r),a.settings.image_caption&&tinymce.Env.ceFalse&&v.push({name:"caption",type:"checkbox",label:"Caption"}),a.settings.image_advtab?(m&&(m.style.marginLeft&&m.style.marginRight&&m.style.marginLeft===m.style.marginRight&&(s.hspace=g(m.style.marginLeft)),m.style.marginTop&&m.style.marginBottom&&m.style.marginTop===m.style.marginBottom&&(s.vspace=g(m.style.marginTop)),m.style.borderWidth&&(s.border=g(m.style.borderWidth)),s.style=a.dom.serializeStyle(a.dom.parseStyle(a.dom.getAttrib(m,"style")))),l=a.windowManager.open({title:"Insert/edit image",data:s,bodyType:"tabpanel",body:[{title:"General",type:"form",items:v},{title:"Advanced",type:"form",pack:"start",items:[{label:"Style",name:"style",type:"textbox",onchange:k},{type:"form",layout:"grid",packV:"start",columns:2,padding:0,alignH:["left","right"],defaults:{type:"textbox",maxWidth:50,onchange:j},items:[{label:"Vertical space",name:"vspace"},{label:"Horizontal space",name:"hspace"},{label:"Border",name:"border"}]}]}],onSubmit:f})):l=a.windowManager.open({title:"Insert/edit image",data:s,body:v,onSubmit:f})}a.on("preInit",function(){function b(a){var b=a.attr("class");return b&&/\bimage\b/.test(b)}function c(a){return function(c){function d(b){b.attr("contenteditable",a?"true":null)}for(var e,f=c.length;f--;)e=c[f],b(e)&&(e.attr("contenteditable",a?"false":null),tinymce.each(e.getAll("figcaption"),d))}}a.parser.addNodeFilter("figure",c(!0)),a.serializer.addNodeFilter("figure",c(!1))}),a.addButton("image",{icon:"image",tooltip:"Insert/edit image",onclick:d(e),stateSelector:"img:not([data-mce-object],[data-mce-placeholder]),figure.image"}),a.addMenuItem("image",{icon:"image",text:"Insert/edit image",onclick:d(e),context:"insert",prependToContext:!0}),a.addCommand("mceImage",d(e))}); +!function(){var a={},b=function(b){for(var c=a[b],e=c.deps,f=c.defn,g=e.length,h=new Array(g),i=0;g>i;++i)h[i]=d(e[i]);var j=f.apply(null,h);if(void 0===j)throw"module ["+b+"] returned undefined";c.instance=j},c=function(b,c,d){if("string"!=typeof b)throw"module id must be a string";if(void 0===c)throw"no dependencies for "+b;if(void 0===d)throw"no definition function for "+b;a[b]={deps:c,defn:d,instance:void 0}},d=function(c){var d=a[c];if(void 0===d)throw"module ["+c+"] was undefined";return void 0===d.instance&&b(c),d.instance},e=function(a,b){for(var c=a.length,e=new Array(c),f=0;c>f;++f)e.push(d(a[f]));b.apply(null,b)},f={};f.bolt={module:{api:{define:c,require:e,demand:d}}};var g=c,h=function(a,b){g(a,[],function(){return b})};h("1",tinymce.PluginManager),h("2",tinymce.Env),h("3",tinymce.util.Promise),h("4",tinymce.util.URI),h("5",tinymce.util.Tools),h("6",tinymce.util.Delay),g("m",[],function(){function a(a,b){return d(document.createElement("canvas"),a,b)}function b(a){return a.getContext("2d")}function c(a){var b=null;try{b=a.getContext("webgl")||a.getContext("experimental-webgl")}catch(c){}return b||(b=null),b}function d(a,b,c){return a.width=b,a.height=c,a}return{create:a,resize:d,get2dContext:b,get3dContext:c}}),g("n",[],function(){function a(a){return a.naturalWidth||a.width}function b(a){return a.naturalHeight||a.height}return{getWidth:a,getHeight:b}}),g("o",[],function(){function a(a,b){return function(){a.apply(b,arguments)}}function b(b){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof b)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],h(b,a(d,this),a(e,this))}function c(a){var b=this;return null===this._state?void this._deferreds.push(a):void i(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function d(b){try{if(b===this)throw new TypeError("A promise cannot be resolved with itself.");if(b&&("object"==typeof b||"function"==typeof b)){var c=b.then;if("function"==typeof c)return void h(a(c,b),a(d,this),a(e,this))}this._state=!0,this._value=b,f.call(this)}catch(g){e.call(this,g)}}function e(a){this._state=!1,this._value=a,f.call(this)}function f(){for(var a=0,b=this._deferreds.length;b>a;a++)c.call(this,this._deferreds[a]);this._deferreds=null}function g(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function h(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}if(window.Promise)return window.Promise;var i=b.immediateFn||"function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},j=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};return b.prototype["catch"]=function(a){return this.then(null,a)},b.prototype.then=function(a,d){var e=this;return new b(function(b,f){c.call(e,new g(a,d,b,f))})},b.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&j(arguments[0])?arguments[0]:arguments);return new b(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;fd;d++)a[d].then(b,c)})},b}),g("p",[],function(){function a(a){var b=document.createElement("a");return b.href=a,b.pathname}function b(b){var c=a(b).split("."),d=c[c.length-1],e={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png"};return d&&(d=d.toLowerCase()),e[d]}return{guessMimeType:b}}),g("e",["o","m","p","n"],function(a,b,c,d){function e(b){return new a(function(a){function c(){b.removeEventListener("load",c),a(b)}b.complete?a(b):b.addEventListener("load",c)})}function f(a){return e(a).then(function(a){var c,e;return e=b.create(d.getWidth(a),d.getHeight(a)),c=b.get2dContext(e),c.drawImage(a,0,0),e})}function g(a){return e(a).then(function(a){var b=a.src;return 0===b.indexOf("blob:")?i(b):0===b.indexOf("data:")?j(b):f(a).then(function(a){return j(a.toDataURL(c.guessMimeType(b)))})})}function h(b){return new a(function(a){function c(){d.removeEventListener("load",c),a(d)}var d=new Image;d.addEventListener("load",c),d.src=URL.createObjectURL(b),d.complete&&c()})}function i(b){return new a(function(a){var c=new XMLHttpRequest;c.open("GET",b,!0),c.responseType="blob",c.onload=function(){200==this.status&&a(this.response)},c.send()})}function j(b){return new a(function(a){var c,d,e,f,g,h;if(b=b.split(","),f=/data:([^;]+)/.exec(b[0]),f&&(g=f[1]),c=atob(b[1]),window.WebKitBlobBuilder){for(h=new WebKitBlobBuilder,d=new ArrayBuffer(c.length),e=0;ec?a=c:b>a&&(a=b),a}function b(){return[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1]}function c(a,b){var c,d,e,f,g=[],h=new Array(10);for(c=0;5>c;c++){for(d=0;5>d;d++)g[d]=b[d+5*c];for(d=0;5>d;d++){for(f=0,e=0;5>e;e++)f+=a[d+5*e]*g[e];h[d+5*c]=f}}return h}function d(b,c){return c=a(c,0,1),b.map(function(b,d){return d%6===0?b=1-(1-b)*c:b*=c,a(b,0,1)})}function e(b,d){var e;return d=a(d,-1,1),d*=100,0>d?e=127+d/100*127:(e=d%1,e=0===e?l[d]:l[Math.floor(d)]*(1-e)+l[Math.floor(d)+1]*e,e=127*e+127),c(b,[e/127,0,0,0,.5*(127-e),0,e/127,0,0,.5*(127-e),0,0,e/127,0,.5*(127-e),0,0,0,1,0,0,0,0,0,1])}function f(b,d){var e,f,g,h;return d=a(d,-1,1),e=1+(d>0?3*d:d),f=.3086,g=.6094,h=.082,c(b,[f*(1-e)+e,g*(1-e),h*(1-e),0,0,f*(1-e),g*(1-e)+e,h*(1-e),0,0,f*(1-e),g*(1-e),h*(1-e)+e,0,0,0,0,0,1,0,0,0,0,0,1])}function g(b,d){var e,f,g,h,i;return d=a(d,-180,180)/180*Math.PI,e=Math.cos(d),f=Math.sin(d),g=.213,h=.715,i=.072,c(b,[g+e*(1-g)+f*-g,h+e*-h+f*-h,i+e*-i+f*(1-i),0,0,g+e*-g+.143*f,h+e*(1-h)+.14*f,i+e*-i+f*-.283,0,0,g+e*-g+f*-(1-g),h+e*-h+f*h,i+e*(1-i)+f*i,0,0,0,0,0,1,0,0,0,0,0,1])}function h(b,d){return d=a(255*d,-255,255),c(b,[1,0,0,0,d,0,1,0,0,d,0,0,1,0,d,0,0,0,1,0,0,0,0,0,1])}function i(b,d,e,f){return d=a(d,0,2),e=a(e,0,2),f=a(f,0,2),c(b,[d,0,0,0,0,0,e,0,0,0,0,0,f,0,0,0,0,0,1,0,0,0,0,0,1])}function j(b,e){return e=a(e,0,1),c(b,d([.393,.769,.189,0,0,.349,.686,.168,0,0,.272,.534,.131,0,0,0,0,0,1,0,0,0,0,0,1],e))}function k(b,e){return e=a(e,0,1),c(b,d([.33,.34,.33,0,0,.33,.34,.33,0,0,.33,.34,.33,0,0,0,0,0,1,0,0,0,0,0,1],e))}var l=[0,.01,.02,.04,.05,.06,.07,.08,.1,.11,.12,.14,.15,.16,.17,.18,.2,.21,.22,.24,.25,.27,.28,.3,.32,.34,.36,.38,.4,.42,.44,.46,.48,.5,.53,.56,.59,.62,.65,.68,.71,.74,.77,.8,.83,.86,.89,.92,.95,.98,1,1.06,1.12,1.18,1.24,1.3,1.36,1.42,1.48,1.54,1.6,1.66,1.72,1.78,1.84,1.9,1.96,2,2.12,2.25,2.37,2.5,2.62,2.75,2.87,3,3.2,3.4,3.6,3.8,4,4.3,4.7,4.9,5,5.5,6,6.5,6.8,7,7.3,7.5,7.8,8,8.4,8.7,9,9.4,9.6,9.8,10];return{identity:b,adjust:d,multiply:c,adjustContrast:e,adjustBrightness:h,adjustSaturation:f,adjustHue:g,adjustColors:i,adjustSepia:j,adjustGrayscale:k}}),g("c",["m","n","e","q"],function(a,b,c,d){function e(d,e){return c.blobToImage(d).then(function(d){function f(a,b){var c,d,e,f,g,h=a.data,i=b[0],j=b[1],k=b[2],l=b[3],m=b[4],n=b[5],o=b[6],p=b[7],q=b[8],r=b[9],s=b[10],t=b[11],u=b[12],v=b[13],w=b[14],x=b[15],y=b[16],z=b[17],A=b[18],B=b[19];for(g=0;gc?a=c:b>a&&(a=b),a}var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;for(g=Math.round(Math.sqrt(c.length)),h=Math.floor(g/2),e=a.data,f=b.data,t=a.width,u=a.height,j=0;u>j;j++)for(i=0;t>i;i++){for(k=l=m=0,o=0;g>o;o++)for(n=0;g>n;n++)p=d(i+n-h,0,t-1),q=d(j+o-h,0,u-1),r=4*(q*t+p),s=c[o*g+n],k+=e[r]*s,l+=e[r+1]*s,m+=e[r+2]*s;r=4*(j*t+i),f[r]=d(k,0,255),f[r+1]=d(l,0,255),f[r+2]=d(m,0,255)}return b}var g,h,i=a.create(b.getWidth(d),b.getHeight(d)),j=a.get2dContext(i);return j.drawImage(d,0,0),k(d),g=j.getImageData(0,0,i.width,i.height),h=j.getImageData(0,0,i.width,i.height),h=f(g,h,e),j.putImageData(h,0,0),c.canvasToBlob(i)})}function g(d){return function(e,f){return c.blobToImage(e).then(function(e){function g(a,b){var c,d=a.data;for(c=0;ci||i>2)&&(i=.5>i?.5:2,k=!0),(.5>j||j>2)&&(j=.5>j?.5:2,k=!0);var l=f(a,i,j);return k?l.then(function(a){return e(a,b,c)}):l}function f(b,e,f){return new a(function(a){var g=d.getWidth(b),h=d.getHeight(b),i=Math.floor(g*e),j=Math.floor(h*f),k=c.create(i,j),l=c.get2dContext(k);l.drawImage(b,0,0,g,h,0,0,i,j),a(k)})}return{scale:e}}),g("d",["e","m","n","r"],function(a,b,c,d){function e(d,e){return a.blobToImage(d).then(function(f){var g=b.create(c.getWidth(f),c.getHeight(f)),h=b.get2dContext(g),j=0,k=0;return e=0>e?360+e:e,90!=e&&270!=e||b.resize(g,g.height,g.width),90!=e&&180!=e||(j=g.width),270!=e&&180!=e||(k=g.height),h.translate(j,k),h.rotate(e*Math.PI/180),h.drawImage(f,0,0),i(f),a.canvasToBlob(g,d.type)})}function f(d,e){return a.blobToImage(d).then(function(d){var f=b.create(c.getWidth(d),c.getHeight(d)),g=b.get2dContext(f);return"v"==e?(g.scale(1,-1),g.drawImage(d,0,-f.height)):(g.scale(-1,1),g.drawImage(d,-f.width,0)),i(d),a.canvasToBlob(f)})}function g(c,d,e,f,g){return a.blobToImage(c).then(function(c){var h=b.create(f,g),j=b.get2dContext(h);return j.drawImage(c,-d,-e),i(c),a.canvasToBlob(h)})}function h(b,c,e){return a.blobToImage(b).then(function(f){var g;return g=d.scale(f,c,e).then(function(c){return a.canvasToBlob(c,b.type)}).then(j(f))["catch"](j(f))})}var i=a.revokeImageUrl,j=function(a){return function(b){return i(a),b}};return{rotate:e,flip:f,crop:g,resize:h}}),g("7",["c","d"],function(a,b){var c=function(b){return a.invert(b)},d=function(b){return a.sharpen(b)},e=function(b){return a.emboss(b)},f=function(b,c){return a.gamma(b,c)},g=function(b,c){return a.exposure(b,c)},h=function(b,c,d,e){return a.colorize(b,c,d,e)},i=function(b,c){return a.brightness(b,c)},j=function(b,c){return a.hue(b,c)},k=function(b,c){return a.saturate(b,c)},l=function(b,c){return a.contrast(b,c)},m=function(b,c){return a.grayscale(b,c)},n=function(b,c){return a.sepia(b,c)},o=function(a,c){return b.flip(a,c)},p=function(a,c,d,e,f){return b.crop(a,c,d,e,f)},q=function(a,c,d){return b.resize(a,c,d)},r=function(a,c){return b.rotate(a,c)};return{invert:c,sharpen:d,emboss:e,brightness:i,hue:j,saturate:k,contrast:l,grayscale:m,sepia:n,colorize:h,gamma:f,exposure:g,flip:o,crop:p,resize:q,rotate:r}}),g("8",["e"],function(a){var b=function(b){return a.blobToImage(b)},c=function(b){return a.imageToBlob(b)},d=function(b){return a.blobToDataUri(b)},e=function(b){return a.blobToBase64(b)};return{blobToImage:b,imageToBlob:c,blobToDataUri:d,blobToBase64:e}}),h("f",tinymce.dom.DOMUtils),h("g",tinymce.ui.Factory),h("h",tinymce.ui.Form),h("i",tinymce.ui.Container),h("s",tinymce.ui.Control),h("t",tinymce.ui.DragHelper),h("u",tinymce.geom.Rect),h("w",tinymce.dom.DomQuery),h("x",tinymce.util.Observable),h("y",tinymce.util.VK),g("v",["w","t","u","5","x","y"],function(a,b,c,d,e,f){var g=0;return function(h,i,j,k,l){function m(a,b){return{x:b.x+a.x,y:b.y+a.y,w:b.w,h:b.h}}function n(a,b){return{x:b.x-a.x,y:b.y-a.y,w:b.w,h:b.h}}function o(){return n(j,h)}function p(a,b,d,e){var f,g,i,k,l;f=b.x,g=b.y,i=b.w,k=b.h,f+=d*a.deltaX,g+=e*a.deltaY,i+=d*a.deltaW,k+=e*a.deltaH,20>i&&(i=20),20>k&&(k=20),l=h=c.clamp({x:f,y:g,w:i,h:k},j,"move"==a.name),l=n(j,l),y.fire("updateRect",{rect:l}),v(l)}function q(){function c(a){var c;return new b(D,{document:k.ownerDocument,handle:D+"-"+a.name,start:function(){c=h},drag:function(b){p(a,c,b.deltaX,b.deltaY)}})}a('
    ').appendTo(k),d.each(B,function(b){a("#"+D,k).append('