From 4dbcd4fc8f43e4f3c07b4b944caeb0632660cb64 Mon Sep 17 00:00:00 2001 From: HyungKyu Song Date: Fri, 15 Feb 2013 15:11:57 +0900 Subject: [PATCH] Tizen 2.0 Release --- AUTHORS | 6 + LICENSE.Flora | 206 ++++++++++++++ NOTICE | 4 + config.xml | 14 + css/style.css | 174 ++++++++++++ icon.png | Bin 0 -> 17581 bytes images/00_winset_Back.png | Bin 0 -> 3225 bytes images/etc.png | Bin 0 -> 1292 bytes images/folder.png | Bin 0 -> 743 bytes images/img.png | Bin 0 -> 1454 bytes images/music.png | Bin 0 -> 1621 bytes images/pdf.png | Bin 0 -> 4794 bytes images/ppt.png | Bin 0 -> 5417 bytes images/text.png | Bin 0 -> 4023 bytes images/video.png | Bin 0 -> 2040 bytes index.html | 17 ++ js/app.clipboard.js | 113 ++++++++ js/app.config.js | 28 ++ js/app.helpers.js | 123 ++++++++ js/app.js | 239 ++++++++++++++++ js/app.model.js | 255 +++++++++++++++++ js/app.systemIO.js | 263 +++++++++++++++++ js/app.ui.js | 659 +++++++++++++++++++++++++++++++++++++++++++ js/app.ui.templateManager.js | 108 +++++++ js/main.js | 78 +++++ templates/emptyFolder.tpl | 1 + templates/fileRow.tpl | 5 + templates/folderRow.tpl | 6 + templates/levelUpRow.tpl | 3 + templates/main.tpl | 64 +++++ 30 files changed, 2366 insertions(+) create mode 100644 AUTHORS create mode 100644 LICENSE.Flora create mode 100644 NOTICE create mode 100644 config.xml create mode 100644 css/style.css create mode 100755 icon.png create mode 100644 images/00_winset_Back.png create mode 100755 images/etc.png create mode 100755 images/folder.png create mode 100755 images/img.png create mode 100755 images/music.png create mode 100755 images/pdf.png create mode 100755 images/ppt.png create mode 100755 images/text.png create mode 100755 images/video.png create mode 100644 index.html create mode 100644 js/app.clipboard.js create mode 100644 js/app.config.js create mode 100644 js/app.helpers.js create mode 100644 js/app.js create mode 100644 js/app.model.js create mode 100644 js/app.systemIO.js create mode 100644 js/app.ui.js create mode 100644 js/app.ui.templateManager.js create mode 100644 js/main.js create mode 100644 templates/emptyFolder.tpl create mode 100644 templates/fileRow.tpl create mode 100644 templates/folderRow.tpl create mode 100644 templates/levelUpRow.tpl create mode 100644 templates/main.tpl diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..a447a9f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Pawel Sierszen +Piotr Wronski +Dariusz Paziewski +Tomasz Lukawski +Tomasz Paciorek +Aniela Rudy-Gawecka diff --git a/LICENSE.Flora b/LICENSE.Flora new file mode 100644 index 0000000..9c95663 --- /dev/null +++ b/LICENSE.Flora @@ -0,0 +1,206 @@ +Flora License + +Version 1.0, May, 2012 + +http://floralicense.org/license/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and +all other entities that control, are controlled by, or are +under common control with that entity. For the purposes of +this definition, "control" means (i) the power, direct or indirect, +to cause the direction or management of such entity, +whether by contract or otherwise, or (ii) ownership of fifty percent (50%) +or more of the outstanding shares, or (iii) beneficial ownership of +such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, +and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice +that is included in or attached to the work (an example is provided +in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship. For the purposes of this License, +Derivative Works shall not include works that remain separable from, +or merely link (or bind by name) to the interfaces of, the Work and +Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original +version of the Work and any modifications or additions to that Work or +Derivative Works thereof, that is intentionally submitted to Licensor +for inclusion in the Work by the copyright owner or by an individual or +Legal Entity authorized to submit on behalf of the copyright owner. +For the purposes of this definition, "submitted" means any form of +electronic, verbal, or written communication sent to the Licensor or +its representatives, including but not limited to communication on +electronic mailing lists, source code control systems, and issue +tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding +communication that is conspicuously marked or otherwise designated +in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +"Tizen Certified Platform" shall mean a software platform that complies +with the standards set forth in the Compatibility Definition Document +and passes the Compatibility Test Suite as defined from time to time +by the Tizen Technical Steering Group and certified by the Tizen +Association or its designated agent. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work +solely as incorporated into a Tizen Certified Platform, where such +license applies only to those patent claims licensable by such +Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work solely +as incorporated into a Tizen Certified Platform to which such +Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim +in a lawsuit) alleging that the Work or a Contribution incorporated +within the Work constitutes direct or contributory patent infringement, +then any patent licenses granted to You under this License for that +Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof pursuant to the copyright license +above, in any medium, with or without modifications, and in Source or +Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works + a copy of this License; and + 2. You must cause any modified files to carry prominent notices stating + that You changed the files; and + 3. You must retain, in the Source form of any Derivative Works that + You distribute, all copyright, patent, trademark, and attribution + notices from the Source form of the Work, excluding those notices + that do not pertain to any part of the Derivative Works; and + 4. If the Work includes a "NOTICE" text file as part of its distribution, + then any Derivative Works that You distribute must include a readable + copy of the attribution notices contained within such NOTICE file, + excluding those notices that do not pertain to any part of + the Derivative Works, in at least one of the following places: + within a NOTICE text file distributed as part of the Derivative Works; + within the Source form or documentation, if provided along with the + Derivative Works; or, within a display generated by the Derivative Works, + if and wherever such third-party notices normally appear. + The contents of the NOTICE file are for informational purposes only + and do not modify the License. + +You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text +from the Work, provided that such additional attribution notices +cannot be construed as modifying the License. You may add Your own +copyright statement to Your modifications and may provide additional or +different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works +as a whole, provided Your use, reproduction, and distribution of +the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Flora License to your work + +To apply the Flora License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Flora License, Version 1.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://floralicense.org/license/ + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..85044e4 --- /dev/null +++ b/NOTICE @@ -0,0 +1,4 @@ +Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Flora License, Version 1. +Please, see the LICENSE.Flora file for Flora License terms and conditions. + diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..87d62ba --- /dev/null +++ b/config.xml @@ -0,0 +1,14 @@ + + + + + FileManager + + + + + + + + + diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..50aca9e --- /dev/null +++ b/css/style.css @@ -0,0 +1,174 @@ +* { + margin: 0px; + padding: 0px; +} + +body { + overflow: hidden; +} + +#fileList { + margin: 0; +} + +#mainTitle { + width: 260px; +} + +#fileList > li { + padding-top: 0.3rem; + padding-bottom: 0.3rem; + border-top: solid 1px #ddd; +} + +#fileList > li > span.nodename { + display: inline-block; + position: absolute; + line-height: 32px; + white-space: nowrap; + text-overflow: ellipsis; + width: 75%; + overflow: hidden; + margin-top: 5px; +} + +#fileList > li.gradientBackground > span.nodename { + color: #fff !important; +} + +#fileList > li.file img { + width: 32px; + height: 32px; +} + +#fileList > li.folder img { + margin-top: 0.1rem; +} + +#fileList > li.levelUp { + padding-left: 47px !important; + height: 32px; +} + +.selectAll { + padding-left: 10px; + display: inline-block; +} + +.selectAll span.ui-icon { + top: 40% !important; +} + +.selectAll span.ui-btn-text { + padding-left: 1.5rem !important; +} + +#navbar { + height: 16px; + padding: 2px 10px; + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + background-color: #EEE; + border-top: solid 1px #DDD; + direction: rtl; + text-align: left; +} + +.ui-pathDiv { + position: absolute; + top: 49px; + left: 0px; + right: 0px; + bottom: 0px; +} + +#pathDiv { + padding: 2px 0px 0px 5px; + border: 0px; +} + +#pathDiv .ui-li-text-main { + font-size: 18px; +} + +#morePopup td.text { + padding: 5px; +} + +.ui-header.ui-bar-s .ui-btn.standard { + width: 100%; + height: 100%; +} + +.ui-li-1line-bigicon1.ui-li.ui-li-static.ui-body-s.ui-li-has-thumb { + padding-left: 0.7rem; + padding-right: 0rem; +} + +.my-ui-checkbox { + display: inline-block; + margin-top: 0rem; + margin-right: 0rem; + position: relative !important; + top: -0.7rem; + left: -1.5rem; +} + +ul.ui-listview > li.ui-li-1line-bigicon1 img.ui-li-bigicon { + display: inline-block; + margin-top: 0rem; + margin-right: 0.7rem; + position: relative; + left: 5px; +} + +.ui-checkbox .ui-btn.ui-btn-icon-left .ui-btn-inner { + line-height: 1.1rem; + padding: 0 0 0 0rem; + width: 30px; +} + +.ui-checkbox .ui-btn.ui-btn-icon-left .ui-btn-inner.ui-btn-hastxt { + width: 100%; +} + +.ui-btn-corner-all { + -webkit-border-radius: 0px; + bordert-radius: 0px; +} + +.ui-content.ui-scrollview-clip > div.ui-scrollview-view { + padding: 0px; +} + +input.ui-input-text.new_folder { + width: 100%; + height: 50px; + padding: 0 0 0 .4em; +} + +.gradientBackground { + background: -webkit-linear-gradient(top, #5A99BA 0%, #205473 100%) !important; /* from tizen-white */ +} + +.hidden { + display: none !important; +} + +.vhidden { + visibility: hidden !important; +} + +.ui-tabbar a { + color: #999 !important; +} + +#addFolderPopup span.ui-btn-inner { + width: 100px; +} + +#newFolderName { + margin:0.4em; +} \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100755 index 0000000000000000000000000000000000000000..983c883493a0ae561b9592d54ea8204042b4b6fb GIT binary patch literal 17581 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 LNkvXXu0mjfzQ*#c literal 0 HcmV?d00001 diff --git a/images/etc.png b/images/etc.png new file mode 100755 index 0000000000000000000000000000000000000000..26748d83cfa4fee28a125d4dcdb01a77f901102d GIT binary patch literal 1292 zcmXX_4NMbf82%72CqH@+lifgL83s7xUn4kb2m%%b4_HAR0;?NfOL0M?fWN^EDrmWZ zWy)=3p!|qJnXozNCQ%1o4yIBDC@|4AgDu@C!HGx=0^Z&axP15A^Zq^W`@Ly(ikMxl zP99DOA=k(Vc^rKG@k`hPzPdj46rm+iim1Keq9}p~9@z^~YFXr{RHO$)$wQGcEqZ`2 zM8N}`sKO!Ah!50)iOYvD(k11x5lrAf2%K~`Rs$rJha|eo*lEGZfHK>Bv=3)W?u*nt zTm^_vE9N-Iin$atZ8(j*g*||9u-Pa|O0EGy4mRgUf+|UeNYr8v{m=>~Ob8qvBI?W{ zslZba6^njk5NQ}=9tnd8f`G6G@Zee?rMNH3w=O6HqL55z0b?%+;`v*#DFxXoyctk{ zTT?JMz}}`zTLu3pb>7NWTBm;N}w{d$UhOcP_W1W$zXLO zqNG-k7zMPCCH!czN+fg^M3As9VxKk?7FZ;p^}7*4P^AKyFGxEqWHKuoctI#w>L^PV z33Q`CDp@j}C3S+NLZE8}B7voEK`)lb70@X&sbXo9nM~JG1_)`1QjyFRNR?LFYDU>+ zszg8vt<+?ujRI_dOxB`=aw1vVU4~>Z=aH1EF`5q$TK__edCE*w+NGh29$nazH~aews|Vu zxibu}UIr>4$unIMlfGO%u-nZ{Y!SEWtQCIWOHiadbnnRn@~SZ zzbWIHsVMV+#~Qb4pmUw4?^#r5wr;s?%kufF?mbX z_B@|X&$@$!Jubt4cI-<2X=LICpRY+AZD>kUTQ8I;KR&+cYa;1Ed(g0YP4P>g{{Fe5 zrOOZJHoshPC_ZV%wR$4*PVJ*xeuf}NMQ^3terhZ-qDej?4avE$=(v%Byl#d~uMHVH zDErPme&hKQuL8f~k9|?_)T9hQmth)r(&=xSTw{+Y2i=~;4_-a$zjttr-IJloAcvNz z*MqCFU*GwVyxqup4d&>>8=ZUYxESw$NhWRkhLq9I(Q4I~UK)qgO|ot6l-zrDXK0V6 zbGogU>YR4z*}t34v&NmiRHM6WajbJK-nQqhwr2U|0|kC56jeyQ`Oz;W$U$Rt6MC|| z-?BCD?_60u&Z)dKoJti~mYOhVXEPet<+aw?l8UVjwwaw(zU~QaE3VjQ_4r3SY}gw8 zZuHj^kGs~_U))9)=2adpJeK&uqu^==GIaV` z*J;mo^`-0S#87qmr-AOx(axKjcQ_jbOFe)!9*clvqyM!=M3Q?<0t&rNN_|Ch+{7?P)00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2i*Y| z4FVjzHBMOo000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0006)Nkl)yf6okK#$FQXemh5sL&XS|#I#jWS6*nMNELczk$H9v1NHg=A#f<+TB$knX zrpi?s$un+s_vqBmivu8m}@DuHOc(Dg`tA zu=W-oQ{Fs9L(=wdxivvs^M8=|=5bhu?Sa*rT4jFi~U->YAbMrnHNAy0={u z-sWGkcyWSKbFk<5Ubqar)n78O(dhwgNvR^G&IeaEzok(A#5f;dX5bffZXs25UckUsq literal 0 HcmV?d00001 diff --git a/images/img.png b/images/img.png new file mode 100755 index 0000000000000000000000000000000000000000..4dd3be3b1bbeef29bb56a19a9de7184919ceec6e GIT binary patch literal 1454 zcmV;f1yTBmP)<>OB zFU*^7e)%%*4YO+*WB7k!45LO-s3=qvDhd^aib6#pA1*BNBRoJg5OzhO=mL&*l+sQb z#wC|<1%Tu_i|Xd$t(aOzO8O-Nj&XHN{$T-A4 z-cR}bCgfV2-qlMIKtZhf1IkzP>Gv5@KEy#oPwQNy z^9Rc3G=T=-I>s!c00=x|(l7R{YE3m9UP8F){VF9ok7%Ps<%B0LKps9N2;nQe9 ztGnGB0{#t5<~9?7ja(a8UUMNfGdgSHk9b9=wJ3n`K*Ezy?Iq>_dx5byGHaV~WH9D) zE>W3b0=pfVbO@H-Pa=L=Ra8%rD?Jyy3%L;-vI^BiX%zSbC;?DF&l`)0e{K;@!Tml-M}?|O)DtuQC6<+ZT9FASm-`y!O{k`e2H>FbC1t>o`EnP) z5{%i0g9ZW6Dm{4^A2^!A-BC4l6Zmr19H8Axg~T;$g&-M(9GQnnRYo+RA@=v>Zkau# zqmz-2-}!w;dHw6unO}San3^p*e0Vzjeo$8u~ZR zgM{1w_Ia(4dkm!o$s#n36tR7qiGQ#GkJvyvunrf!vr10}7p`v(&R)KUP`PD0o#O-a zuWdvwcH9CMV$2P%LbfZR_%c)|bdnN*kkdt?=K=nKG3yus!9|-disq`)JU|Ko2&g}R z28`*zD?0r;(;CwF6^s>kXE@6U2pwaUz&2p4xF@4Z32r@cA3fbG`@yr2Xwpy_g^;vs zz|LFd2up#t$wbA(7lUi}SH!n9Jv;h41_Q!al*Q%ODq%K^fUkf+DCPcjR+6%~03YIe}qJp-f+uV_JzZ3lQT z>-zC|eGh?+#sN`5@aug`|Z+wUb_;3P4R?ja)bp zA|H%71w6+nqITj3R*^#DF%SW`FyHg<%{{?s_`yt7DGoo15BRt)5oQG!og-{Lm1pQIyItZOZ0iD1L7;`6rne)IQ^@4HYdOP-S3`z_MX`T z!uUlVZVWdBK|Ep?$B5wn9LEoJf$`0$=N$;*Tp?T{7L1IHKm`sQvy6esfDs1FgkvTG zGeCyXgyCc)mk5VQkbbB@Ag3nGFv`&Hfd3gEcfrii2BQJf6X3%sq)6%{D?v5{l1U($ z^uTBtTZKR}X~Th%bdo2I)(_wA!vpJt%%y~WR6YU}2~0m$8@v;EA$k~Lwy{XSEBepW z!SI2i7C6#EhTG+6M($`0IMBc)C2-`VBXqbLhdLTV4(K04cpHw}gJ#$isKdyia9|`A zEkKq0qtZ}HD3=mt09pgkh$vAaN}LpxD1p)d z^a@8Ri5_SaK%)l)0km46$v~wNG(&;rY8)&_5kwEP9E24|iJ~JcC4f4#X<^qSs8j>= z5{gI<401F#1I_gf9tnppP_|VRzYMvyBPGSGQkl&vvkeBD%8b)naGEVlMa9ke>Y+L@ zFR!oj;*g7qzl~WtuTBftYP@un8xtjXwHS^ES%`&dtFhqOAzSQ0(XlAyo`00;MiM@G~ zP7^f19abKEd$6uMh-N9&m1}}?=fkh6F*YVjykm8r#gh5ySH4X)w(x}aJ<-XaHh1E| zhjILF)AFLWgc(i7gu-vEC{-hVp`bq%y%>HbfP3F)VQoDR^fwvyU)zwX~c^?N^s*YGC7UVU9iSLHQ@1%LldbWMlVika)9)BDG* zjJ{vAC*9gh;T&%- z%qLiSRi~CUrUw|!^{+eJW+9Tu)Dh+B+q{i~fC}CfE)sLr_w0yBY|m=lSy_LC9atgk z(cw8+N`C(KtjN;wlV46}xkXa6tt}B)@0I}ZbH(9_f#QBw@N5txom#V z#&s2szc8QiXGX{QpNUR_}rkcNY@4q(qs#pL3 literal 0 HcmV?d00001 diff --git a/images/pdf.png b/images/pdf.png new file mode 100755 index 0000000000000000000000000000000000000000..2480d818341a660d16797b59ef59121484cf9aa0 GIT binary patch literal 4794 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 Wcsr)1CMZz=0000h7 literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 0000000..9bd228a --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + file manager + + + + + + + + +
+ + \ No newline at end of file diff --git a/js/app.clipboard.js b/js/app.clipboard.js new file mode 100644 index 0000000..8b150f7 --- /dev/null +++ b/js/app.clipboard.js @@ -0,0 +1,113 @@ +/*jslint devel: true*/ +/*global $*/ + +/** + * @class Config + */ +function Clipboard() { + 'use strict'; + this.mode = this.INACTIVE_MODE; +} + +(function () { // strict mode wrapper + 'use strict'; + Clipboard.prototype = { + /** + * Clipboard mode for copying + */ + COPY_MODE_ID: 0, + + /** + * Clipboard mode for moving + */ + MOVE_MODE_ID: 1, + + /** + * Clipbboard inactive mode + */ + INACTIVE_MODE: -1, + + /** + * Clipboard data + */ + data: [], + + /** + * Clipboard mode: [copy | move | inactive] + */ + mode: undefined, + + /** + * Returns all paths in clipboard + * @returns {array} + */ + get: function Clipboard_get() { + return this.data; + }, + + /** + * Add new path to clipboard + * @param {array} paths aray of full paths + * @returns {number} current length of clipboard objects + */ + add: function Clipboard_add(paths) { + var len = paths.length, + i; + + // clear clipboard + this.clear(); + for (i = 0; i < len; i += 1) { + if (this.has(paths[i]) === false) { + this.data.push(paths[i]); + } + } + + return this.data.length; + }, + + /** + * Checks if specified path is already in clipboard + * @param {string} path full path + * @returns {boolean} + */ + has: function Clipboard_has(path) { + return $.inArray(path, this.data) === -1 ? false : true; + }, + + /** + * Clears all clipboard data and resets clipboard mode + */ + clear: function Clipboard_clear() { + this.data = []; + this.mode = this.INACTIVE_MODE; + }, + + /** + * Sets clipboard mode + * @param {number} mode + * @returns {boolean} + */ + setMode: function Clipboard_setMode(mode) { + if ($.inArray(mode, [this.MOVE_MODE_ID, this.COPY_MODE_ID]) === false) { + console.error('Incorrect clipboard mode'); + return false; + } + this.mode = mode; + return true; + }, + + /** + * @returns {number} mode Clipboard mode + */ + getMode: function Clipboard_getMode() { + return this.mode; + }, + + /** + * @returns {boolean} + */ + isEmpty: function Clipboard_isEmpty() { + return this.data.length === 0; + } + }; +}()); diff --git a/js/app.config.js b/js/app.config.js new file mode 100644 index 0000000..e0332c3 --- /dev/null +++ b/js/app.config.js @@ -0,0 +1,28 @@ +/** + * @class Config + */ +function Config() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + Config.prototype = { + + properties: { + 'templateDir': 'templates', + 'templateExtension': '.tpl' + }, + + /** + * Returns config value + */ + get: function (value, defaultValue) { + + if (this.properties.hasOwnProperty(value)) { + return this.properties[value]; + } + return defaultValue; + } + }; +}()); diff --git a/js/app.helpers.js b/js/app.helpers.js new file mode 100644 index 0000000..6998571 --- /dev/null +++ b/js/app.helpers.js @@ -0,0 +1,123 @@ +/*jslint devel: true*/ +/*global $ */ + +/** + * @class Helpers + */ +function Helpers() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + Helpers.prototype = { + + /** + * Capitalise the first letter + * + * @param {string} text + * @returns {string} + */ + UCFirst: function Helpers_UCFirst(text) { + return text.charAt(0).toUpperCase() + text.slice(1); + }, + + /** + * @param {string} fileName + * @returns {string} extension for specified file name + */ + getFileExtension: function Helpers_getFileExtension(fileName) { + var splittedFileName = fileName.split('.'), + ext = ''; + + if (splittedFileName.length > 1) { + ext = '.' + splittedFileName.pop(); + } + return ext; + }, + + /** + * Return icon filename for the given extension. + * For example, for '.mp3' returns 'music.png' + * + * @param {string} ext + * @return {string} + */ + resolveFileIcon: function Helpers_resolveFileIcon(ext) { + + ext = ext.toLowerCase(); + + switch (ext) { + case '.jpg': + return 'img.png'; + case '.png': + return 'img.png'; + case '.gif': + return 'img.png'; + case '.pdf': + return 'pdf.png'; + case '.mp3': + return 'music.png'; + case '.avi': + return 'video.png'; + case '.mp4': + return 'video.png'; + case '.ppt': + return 'ppt.png'; + case '.txt': + return 'text.png'; + case '.doc': + return 'text.png'; + case '.xls': + return 'text.png'; + case '.directory': + return 'folder.png'; + default: + return 'etc.png'; + } + }, + + /** + * Resolve file extension to MIME type + * + * @param {string} ext File extension + * @returns {string} + */ + resolveMimeType: function Helpers_resolveMimeType(ext) { + var mime = ''; + + ext = ext.toLowerCase(); + + if (ext === '.jpg' || ext === '.png' || ext === '.gif') { + mime = 'image/*'; + } else if (ext === '.mp4' || ext === '.ogv' || ext === '.avi') { + mime = 'video/*'; + } else if (ext === '.mp3') { + mime = 'audio/mp3'; + } else if (ext === '.txt' || ext === '.doc' || ext === '.html' || ext === '.ppt' || ext === '.xls' || ext === '.pdf') { + mime = 'text/*'; + } + + return mime; + }, + + /** + * Returns thumbnail URI for specified file + * @param {string} fileName + * @param {File} node + * @returns {string} + */ + getThumbnailURI: function Helpers_getThumbnailURI(fileName, node) { + var ext = this.getFileExtension(fileName), + thumbnailURI = ''; + + if (!node.thumbnailURIs) { + thumbnailURI = 'images/' + this.resolveFileIcon(ext); + } else if (node.thumbnailURIs[0] && $.inArray(ext, ['.mp4', '.jpg', '.png', '.gif'])) { + thumbnailURI = node.thumbnailURIs[0]; + } + + return thumbnailURI; + } + }; +}()); diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..690489c --- /dev/null +++ b/js/app.js @@ -0,0 +1,239 @@ +/*jslint devel: true*/ +/*global tizen, $, app, Ui, Model, Helpers, Config, Clipboard*/ + +var App = null; + +(function () { // strict mode wrapper + 'use strict'; + + /** + * Creates a new application object + * + * @class Application + * @constructor + */ + App = function App() { + }; + + App.prototype = { + /** + * @type Array + */ + requires: ['js/app.config.js', 'js/app.model.js', 'js/app.ui.js', 'js/app.ui.templateManager.js', 'js/app.systemIO.js', 'js/app.helpers.js', 'js/app.clipboard.js'], + + /** + * @type Model + */ + model: null, + + /** + * @type Ui + */ + ui: null, + + /** + * @type Config + */ + config: null, + + /** + * @type SystemIO + */ + systemIO: null, + + /** + * @type Helpers + */ + helpers: null, + + /** + * @type {string} + */ + currentPath: '', + + /** + * + */ + currentDirHandle: null, + + /** + * @type {Clipboard} + */ + clipboard: null, + + /** + * Initialization + */ + init: function App_init() { + this.config = new Config(); + this.model = new Model(); + this.ui = new Ui(); + this.helpers = new Helpers(); + this.clipboard = new Clipboard(); + + this.model.loadInternalStorages(this.initUi.bind(this)); + this.addEvents(); + }, + + /** + * UI initialization + */ + initUi: function App_initUi() { + this.ui.init(this.model.getInternalStorages()); + }, + + /** + * Add pages events + */ + addEvents: function App_addEvents() { + var self = this; + document.addEventListener('webkitvisibilitychange', function () { self.refreshCurrentPage(); }); + }, + + /** + * Displays media storages + */ + displayStorages: function App_displayStorages() { + this.ui.scrollContentTo(0); + this.ui.displayStorages(this.model.getInternalStorages()); + }, + + /** + * Displays specified folder + * @param {string} path + */ + displayFolder: function App_displayFolder(path, refresh) { + var self = this; + + // get folder data and push into rendering method + this.model.getFolderData(path, function (dir, nodes) { + // on success + + // update current path + self.currentPath = path; + + // update current dir handle + self.currentDirHandle = dir; + + // display folder UI + if (refresh === undefined) { + self.ui.scrollContentTo(0); + } + self.ui.displayFolder(path, nodes); + }); + }, + + /** + * Opens specified file + * @params {string} uri File URI + */ + openFile: function App_openFile(uri, fullUri) { + var ext = this.helpers.getFileExtension(uri), + mime = this.helpers.resolveMimeType(ext); + + if (mime !== '') { + this.model.openFile(fullUri, mime); + } else { + console.error('Unsupported mime type for extension ' + ext); + } + }, + + /** + * Displays parent location + */ + goLevelUp: function App_goLevelUp() { + // split current path and get proper path for parent location + var newPath = this.currentPath.split('/').slice(0, -1).join('/'); + + if (newPath !== '') { + this.displayFolder(newPath); + } else { + this.displayStorages(); + } + }, + + /** + * creates new dir in currently viewed dir + * @param {string} dirName + */ + createDir: function App_createDir(dirName) { + + if (this.currentDirPath !== '') { + try { + this.currentDirHandle.createDirectory(dirName); + } catch (e) { + alert(e.message); + } + this.refreshCurrentPage(); + } else { + alert("You can't create new nodes in the main view"); + } + }, + + /** + * Triggers refresh current page + */ + refreshCurrentPage: function App_refreshCurrentPage() { + if (this.currentPath !== '') { + this.displayFolder(this.currentPath, true); + } else { + this.displayStorages(); + } + }, + + /** + * Deletes nodes with specified paths + * @param {string[]} nodes nodePaths + */ + deleteNodes: function App_deleteNodes(nodes) { + this.model.deleteNodes(nodes, this.currentDirHandle, this.ui.removeNodeFromList.bind(this.ui)); + }, + + /** + * @param {string[]} paths filepaths + * @param {number} mode clipboard mode + */ + saveToClipboard: function App_saveToClipboard(paths, mode) { + this.clipboard.add(paths); + this.clipboard.setMode(mode); + this.ui.refreshPasteActionBtn(this.clipboard.isEmpty()); + }, + + /** + * Paste nodes from clipboard to current dir + */ + pasteClipboard: function App_pasteClipboard() { + var clipboardData = this.clipboard.get(); + + if (clipboardData.length === 0) { + alert('Clipboard is empty'); + return false; + } + + if (this.clipboard.getMode() === this.clipboard.COPY_MODE_ID) { + this.model.copyNodes(this.currentDirHandle, clipboardData, this.currentPath, this.onPasteClipboardSuccess.bind(this)); + } else { + this.model.moveNodes(this.currentDirHandle, clipboardData, this.currentPath, this.onPasteClipboardSuccess.bind(this)); + } + + this.ui.refreshPasteActionBtn(this.clipboard.isEmpty()); + + return true; + }, + + /** + * Handler for paste clipboard success + */ + onPasteClipboardSuccess: function App_onPasteClipboardSuccess() { + this.clipboard.clear(); + this.refreshCurrentPage(); + }, + + /** + * App exit + */ + exit: function App_exit() { + tizen.application.getCurrentApplication().exit(); + } + }; +}()); diff --git a/js/app.model.js b/js/app.model.js new file mode 100644 index 0000000..16d6c13 --- /dev/null +++ b/js/app.model.js @@ -0,0 +1,255 @@ +/*jslint devel: true*/ +/*global tizen, SystemIO, $ */ + +/** + * @class Model + */ +function Model() { + 'use strict'; + this.init(); +} + +(function () { // strict mode wrapper + 'use strict'; + Model.prototype = { + + /** + * @type SystemIO + */ + systemIO: null, + + /** + * @type Array + */ + storages: [], + + /** + * API module initialisation + */ + init: function Model_init() { + this.systemIO = new SystemIO(); + }, + + /** + * @returns {FileSystemStorage[]} storages + */ + getInternalStorages: function Model_getInternalStorages() { + return this.storages; + }, + + /** + * Saves storages + * @param {function} onSuccess callback + */ + loadInternalStorages: function Model_loadInternalStorages(onSuccess) { + var self = this; + + this.systemIO.getStorages('INTERNAL', function (storages) { + self.storages = storages; + if (typeof onSuccess === 'function') { + onSuccess(); + } + }, 'internal0'); + }, + + /** + * Returns folder data + * @param {string} path Node path + * @param {function} onSuccess Success callback + * @param {function} onError Error callback + */ + getFolderData: function Model_getFolderData(path, onSuccess, onError) { + + var onOpenSuccess = function (dir) { + dir.listFiles( + function (files) { + onSuccess(dir, files); + }, + function (e) { + console.error('Model_getFolderData listFiles error', e); + } + ); + }, + onOpenError = function (e) { + console.error('Model_getFolderData openDir error', e); + }; + + this.systemIO.openDir(path, onOpenSuccess, onOpenError); + }, + + /** + * Launch a service associated with 'ext' to launch the 'uri' + * @param {string} ext + * @param {string} uri + * @returns {ApplicationSevice} + */ + openFile: function Model_openFile(fullUri, mime) { + var serviceReplyCB = { + onsuccess: function (reply) { + var num = 0; + for (num = 0; num < reply.data.length; num += 1) { + } + }, + onfailure: function () { + console.error('Launch service failed'); + } + }; + + try { + tizen.application.launchAppControl(new tizen.ApplicationControl( + 'http://tizen.org/appcontrol/operation/view', + fullUri, + mime + ), + null, + function () { }, + function (e) { + alert('launch sevice failed. reason :' + e.message); + }, + serviceReplyCB + ); + } catch (e) { + console.error('openFile error:', e); + } + }, + + /** + * @param {File[]} nodes Collection of node objects + * @param {File} dir Directory handle + * @param {function} onSuccess + * @param {function} onError + */ + deleteNodes: function Model_deleteNodes(nodes, dir, onSuccess, onError) { + var len = nodes.length, + onDeleteNodeSuccess = function (nodeId, isDir) { + if (typeof onSuccess === 'function') { + onSuccess(nodeId); + } + }, + onDeleteNodeError = function (e) { + console.error('Folder delete error', e); + if (typeof onError === 'function') { + onError(); + } + }, + i; + + for (i = 0; i < len; i = i + 1) { + if (nodes[i].folder) { + dir.deleteDirectory( + nodes[i].uri, + true, + onDeleteNodeSuccess.bind(this, nodes[i].id, true), + onDeleteNodeError + ); + } else { + dir.deleteFile( + nodes[i].uri, + onDeleteNodeSuccess.bind(this, nodes[i].id, false), + onDeleteNodeError + ); + } + } + }, + + /** + * Copy specified files to destination path + * Overwrites existing files + * + * @param {File} dir Directory handle + * @param {string[]} paths Array with absolute virtual file paths + * @param {string} destinationPath + * @param {function} onSuccess callback + */ + copyNodes: function Model_copyNodes(dir, paths, destinationPath, onSuccess) { + var len = paths.length, + copied = 0, + onCopyNodeSuccess = function () { + copied += 1; + if (copied === len) { + onSuccess(); + } + }, + onCopyNodeFailure = function () { + alert('Copying error'); + }, + i, + sourceName, + decision; + + this.systemIO.getFilesList(dir, function (filesList) { + for (i = 0; i < len; i = i + 1) { + if (destinationPath.indexOf(paths[i]) !== -1) { + alert('Copying error'); + return; + } + } + + for (i = 0; i < len; i = i + 1) { + decision = true; + sourceName = paths[i].split('/').pop(); + + if ($.inArray(sourceName, filesList) !== -1) { + decision = confirm('A file with (' + sourceName + ') name already exists.\nDo you want to overwrite it?'); + } + + if (decision) { + try { + dir.copyTo(paths[i], destinationPath + '/' + sourceName, true, onCopyNodeSuccess, onCopyNodeFailure); + } catch (e) { + console.error(e); + } + } + } + }); + }, + + /** + * Move specified files to destination path + * Overwrites existing files + * + * @param {File} dir Directory handle + * @param {string[]} paths Array with absolute virtual file paths + * @param {string} destinationPath + * @param {function} onSuccess callback + */ + moveNodes: function Model_moveNodes(dir, paths, destinationPath, onSuccess) { + var len = paths.length, + moved = 0, + onMoveNodeSuccess = function () { + moved += 1; + if (moved === len) { + onSuccess(); + } + }, + onMoveNodeFailure = function () { + alert('Moving error'); + }, + i, + sourceName, + decision; + + this.systemIO.getFilesList(dir, function (filesList) { + for (i = 0; i < len; i = i + 1) { + if (destinationPath.indexOf(paths[i]) !== -1) { + alert('Moving error'); + return; + } + } + + for (i = 0; i < len; i = i + 1) { + decision = true; + sourceName = paths[i].split('/').pop(); + + if ($.inArray(sourceName, filesList) !== -1) { + decision = confirm('A file with (' + sourceName + ') name already exists.\nDo you want to overwrite it?'); + } + + if (decision) { + dir.moveTo(paths[i], destinationPath + '/' + sourceName, true, onMoveNodeSuccess, onMoveNodeFailure); + } + } + }); + } + }; +}()); diff --git a/js/app.systemIO.js b/js/app.systemIO.js new file mode 100644 index 0000000..a3df78b --- /dev/null +++ b/js/app.systemIO.js @@ -0,0 +1,263 @@ +/*jslint devel: true*/ +/*global tizen, localStorage */ + +/** + * @class SystemIO + */ +function SystemIO() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + SystemIO.prototype = { + /** + * Creates new empty file in specified location + * + * @param {File} directoryHandle + * @param {string} fileName + */ + createFile: function SystemIO_createFile(directoryHandle, fileName) { + + try { + return directoryHandle.createFile(fileName); + } catch (e) { + console.error('SystemIO_createFile error:' + e.message); + return false; + } + }, + + /** + * Writes content to file stream + * + * @param {File} fileHandle file handler + * @param {string} fileContent file content + * @param {function} onSuccess on success callback + * @param {function} onError on error callback + * @param {string} content encoding + */ + writeFile: function SystemIO_writeFile(fileHandle, fileContent, onSuccess, onError, contentEncoding) { + onError = onError || function () {}; + + fileHandle.openStream('w', function (fileStream) { + if (contentEncoding === 'base64') { + fileStream.writeBase64(fileContent); + } else { + fileStream.write(fileContent); + } + + fileStream.close(); + + // launch onSuccess callback + if (typeof onSuccess === 'function') { + onSuccess(); + } + }, onError, 'UTF-8'); + }, + + /** + * Opens specified location + * + * @param {string} directory path + * @param {function} on success callback + * @param {function} on error callback + * @param {string} mode + */ + openDir: function SystemIO_openDir(directoryPath, onSuccess, onError, openMode) { + openMode = openMode || 'rw'; + onSuccess = onSuccess || function () {}; + + try { + tizen.filesystem.resolve(directoryPath, onSuccess, onError, openMode); + } catch (e) { + } + }, + + /** + * Parse specified filepath and returns data parts + * + * @param {string} filePath + * @returns {array} + */ + getPathData: function SystemIO_getPathData(filePath) { + var path = { + originalPath: filePath, + fileName: '', + dirName: '' + }, + splittedPath = filePath.split('/'); + + path.fileName = splittedPath.pop(); + path.dirName = splittedPath.join('/') || '/'; + + return path; + }, + + /** + * Save specified content to file + * + * @param {string} file path + * @param {string} file content + * @param {string} file encoding + */ + saveFileContent: function SystemIO_saveFileContent(filePath, fileContent, onSaveSuccess, fileEncoding) { + var pathData = this.getPathData(filePath), + self = this, + fileHandle; + + function onOpenDirSuccess(dir) { + // create new file + fileHandle = self.createFile(dir, pathData.fileName); + if (fileHandle !== false) { + // save data into this file + self.writeFile(fileHandle, fileContent, onSaveSuccess, false, fileEncoding); + } + } + + // open directory + this.openDir(pathData.dirName, onOpenDirSuccess); + }, + + /** + * Deletes node with specified path + * + * @param {string} node path + * @param {function} success callback + */ + deleteNode: function SystemIO_deleteNode(nodePath, onSuccess) { + var pathData = this.getPathData(nodePath), + self = this; + + function onDeleteSuccess() { + onSuccess(); + } + + function onDeleteError(e) { + console.error('SystemIO_deleteNode:_onDeleteError', e); + } + + function onOpenDirSuccess(dir) { + var onListFiles = function (files) { + if (files.length > 0) { + // file exists; + if (files[0].isDirectory) { + self.deleteDir(dir, files[0].fullPath, onDeleteSuccess, onDeleteError); + } else { + self.deleteFile(dir, files[0].fullPath, onDeleteSuccess, onDeleteError); + } + } else { + onDeleteSuccess(); + } + }; + + // check file exists; + dir.listFiles(onListFiles, function (e) { + console.error(e); + }, { + name: pathData.fileName + }); + } + + this.openDir(pathData.dirName, onOpenDirSuccess, function (e) { + console.error('openDir error:' + e.message); + }); + }, + + /** + * Deletes specified file + * + * @param {File} dir + * @param {string} file path + * @param {function} delete success callback + * @param {function} delete error callback + */ + deleteFile: function SystemIO_deleteFile(dir, filePath, onDeleteSuccess, onDeleteError) { + try { + dir.deleteFile(filePath, onDeleteSuccess, onDeleteError); + } catch (e) { + console.error('SystemIO_deleteFile error: ' + e.message); + return false; + } + }, + + /** + * Deletes specified directory + * + * @param {File} dir + * @param {string} dir path + * @param {function} delete success callback + * @param {function} delete error callback + * @returns {boolean} + */ + deleteDir: function SystemIO_deleteDir(dir, dirPath, onDeleteSuccess, onDeleteError) { + try { + dir.deleteDirectory(dirPath, false, onDeleteSuccess, onDeleteError); + } catch (e) { + console.error('SystemIO_deleteDir error:' + e.message); + return false; + } + + return true; + }, + + /** + * @param {string} type storage type + * @param {function} onSuccess on success callback + * @param {string} excluded Excluded storage + */ + getStorages: function SystemIO_getStorages(type, onSuccess, excluded) { + try { + tizen.filesystem.listStorages(function (storages) { + var tmp = [], + len = storages.length, + i; + + if (type !== undefined) { + for (i = 0; i < len; i += 1) { + if (storages[i].label === excluded) { + continue; + } + + if (storages[i].type === 0 || storages[i].type === type) { + tmp.push(storages[i]); + } + } + } else { + tmp = storages; + } + + if (typeof onSuccess === 'function') { + onSuccess(tmp); + } + }); + } catch (e) { + console.error('SystemIO_getStorages error:' + e.message); + } + }, + + getFilesList: function SystemIO_getFilesList(dir, onSuccess) { + try { + dir.listFiles( + function (files) { + var tmp = [], + len = files.length, + i; + + for (i = 0; i < len; i += 1) { + tmp.push(files[i].name); + } + + if (typeof onSuccess === 'function') { + onSuccess(tmp); + } + }, + function (e) { + console.error('SystemIO_getFilesList dir.listFiles() error:', e); + } + ); + } catch (e) { + console.error('SystemIO_getFilesList error:', e.message); + } + } + }; +}()); \ No newline at end of file diff --git a/js/app.ui.js b/js/app.ui.js new file mode 100644 index 0000000..5f8f73e --- /dev/null +++ b/js/app.ui.js @@ -0,0 +1,659 @@ +/*jslint devel: true */ +/*global $, app, TemplateManager, Helpers */ + +/** + * @class Ui + */ +function Ui() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + Ui.prototype = { + /** + * UI edit mode + * @type {boolean} + */ + editMode: false, + + /** + * @type {bool} block taps until the page change is completed + */ + nodeTapBlock: false, + + /** + * @type {TemplateManager} + */ + templateManager: null, + + /** + * @type Helpers + */ + helpers: null, + + /** + * @const {number} + */ + PATH_DIV_HEIGHT: 20, + + /** + * @const {number} + */ + SELECT_ALL_HEIGHT: 32, + + /** + * @const {number} header height, set on domReady + */ + HEADER_HEIGHT: 53, + + /** + * name of row gradient class + */ + CSS_GRADIENT_CLASS: 'gradientBackground', + + /** + * Standard tabbar actions + * @type {number} + */ + STD_TABBAR_EDIT_ACTION: 0, + STD_TABBAR_MORE_ACTION: 1, + STD_TABBAR_EXIT_ACTION: 2, + + /** + * Edit tabbar actions + * @type {number} + */ + EDIT_TABBAR_DELETE_ACTION: 0, + EDIT_TABBAR_MOVE_ACTION: 1, + EDIT_TABBAR_COPY_ACTION: 2, + EDIT_TABBAR_CANCEL_ACTION: 3, + + currentHeaderHeight: null, + currentScrollPosition: null, + + /** + * UI Initialization + */ + init: function Ui_init(storages) { + this.templateManager = new TemplateManager(); + this.helpers = new Helpers(); + // Disable text selection + $.mobile.tizen.disableSelection(document); + $(document).ready(this.initDom.bind(this, storages)); + }, + + initDom: function Ui_initDom(storages) { + var self = this; + + this.templateManager.loadToCache(['main', 'fileRow', 'folderRow', 'levelUpRow', 'emptyFolder'], function () { + $('#main').append($(self.templateManager.get('main')).children()).trigger('pagecreate'); + self.addEvents(); + self.displayStorages(storages); + }); + }, + + /** + * Add UI events + */ + addEvents: function Ui_addEvents() { + var self = this; + // touch events for all nodes + $('ul#fileList') + .on('tap', 'li.levelUp', function () { + if (self.editMode === true) { + self.handleCancelEditAction(); + } + app.goLevelUp(); + }) + .on('tap', 'li.node', function (e) { + self.handleNodeClick($(this), true); + }) + .on('change', 'input[type=checkbox]', function (e) { + self.handleNodeClick($(this).closest('li.node'), false); + }) + .on('touchstart', 'li', function (event) { + $(this).addClass(self.CSS_GRADIENT_CLASS); + }) + .on('touchend touchmove taphold', 'li', function (event) { + $(this).removeClass(self.CSS_GRADIENT_CLASS); + }); + + $('.selectAll input').on('change', this.handleSelectAllChange.bind(this)); + + // navbar + $('#navbar').on('tap', 'span', function () { + var uri = $(this).attr('uri'); + if (uri === 'home') { + app.displayStorages(); + } else { + app.displayFolder(uri); + } + }); + + // level up + $('#levelUpBtn').on('tap', function () { + if (self.editMode === true) { + self.handleCancelEditAction(); + } + app.goLevelUp(); + }); + + $('#homeBtn').on('tap', app.displayStorages.bind(app)); + + // edit action + $('#editActionBtn').on('tap', this.handleEditAction.bind(this)); + + // delete action + $('#deleteActionBtn').on('tap', this.handleDeleteAction.bind(this)); + + // cancel edit + $('#cancelActionBtn').on('tap', this.handleCancelEditAction.bind(this)); + + // copy action + $('#copyActionBtn').on('tap', this.handleCopyAction.bind(this)); + + // move action + $('#moveActionBtn').on('tap', this.handleMoveAction.bind(this)); + + // paste action + $('a#pasteActionBtn').on('tap', app.pasteClipboard.bind(app)); + + // exit + $('.ui-myExit').on('tap', app.exit); + + // add folder popup actions + $('#addFolderPopup').on("popupafterclose", function () { + // clear input value + $('#newFolderName').val('New folder'); + }); + + $('#newFolderName').on('tap', function () { + if ($(this).attr('value') === 'New folder') { + $(this).attr('value', ''); + } + }); + + $('#cancelNewFolder').on('tap', function () { + $('#addFolderPopup').popup('close'); + $('#morePopup').popupwindow('close'); + }); + + $('#saveNewFolder').on('tap taphold', function () { + var folderName = $('#newFolderName').val().trim(); + $('#addFolderPopup').popup('close'); + $('#morePopup').popupwindow('close'); + + if (folderName === '') { + alert("Empty folder name"); + } else if (folderName.match(/[\*\.\/\\\?\"\'\:<>|]/)) { + alert("The following special characters are not allowed: *./\\?:<>|'\""); + } else { + app.createDir(folderName); + } + $(this).parent().removeClass('ui-btn-down-s'); + }); + + $('#newFolderActionBtn, #pasteActionBtn').on('tap', function (e) { + setTimeout(function () { + $('#morePopup').popupwindow('close'); + }, 700); + }); + + /* workaround for UIFW & webkit scroll*/ + $('.ui-page').css('min-height', 0); + }, + + /** + * Handler for node click + * @param {File} node + * @param {boolean} toggleCheckbox + */ + handleNodeClick: function Ui_handleNodeClick(node, toggleCheckbox) { + if (this.editMode === true) { + //if edit mode is on toggle checkbox state + if (toggleCheckbox === true) { + this.toggleCheckBoxState(node); // select the checkbox + } + + this.refreshSelectAllStatus(); + + if ($('ul#fileList input:checkbox:checked').length > 0) { + this.enableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + } else { + this.disableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + } + } else if (node.hasClass('folder')) { + // otherwise display folder + app.displayFolder(node.attr('uri')); + } else { + // file + app.openFile(node.attr('uri'), node.attr('fullUri')); + } + }, + + /** + * Handler for edit action + */ + handleEditAction: function Ui_handleEditAction() { + this.editMode = true; + $('.standardTabbar').hide(); + $('div.editTabbar').show(); + this.disableControlBarButtons($('div.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + this.showEditCheckBoxes(); + }, + + /** + * Handler for cancel edit action + */ + handleCancelEditAction: function Ui_handleCancelEditAction() { + this.editMode = false; + $('div.editTabbar').hide(); + $('.standardTabbar').show(); + this.hideEditCheckBoxes(); + }, + + /** + * Handler for delete action + */ + handleDeleteAction: function Ui_handleDeleteAction() { + var nodesToDelete = [], + $rowElement; + + $('ul#fileList input:checkbox:checked').each(function (index) { + $rowElement = $(this).closest('li'); + nodesToDelete.push({ + id: $rowElement.attr('id'), + uri: $rowElement.attr('uri'), + name: $rowElement.attr('label'), + folder: $rowElement.hasClass('folder') + }); + }); + + if (nodesToDelete.length > 0 && confirm('Selected nodes will be deleted. Are you sure?')) { + app.deleteNodes(nodesToDelete); + this.scrollContentTo(0); + } + }, + + /** + * Handler for copy action + */ + handleCopyAction: function Ui_handleCopyAction() { + var paths = []; + if (this.editMode === true) { + $('ul#fileList input:checkbox:checked').each(function (index) { + paths.push($(this).closest('li').attr('uri')); + }); + app.saveToClipboard(paths, app.clipboard.COPY_MODE_ID); + } + }, + + /** + * Handler for move action + */ + handleMoveAction: function Ui_handleMoveAction() { + var paths = []; + if (this.editMode === true) { + $('ul#fileList input:checkbox:checked').each(function (index) { + paths.push($(this).closest('li').attr('uri')); + }); + app.saveToClipboard(paths, app.clipboard.MOVE_MODE_ID); + } + }, + + /** + * Handler for paste action + */ + handlePasteAction: function Ui_handlePasteAction() { + }, + + /** + * Scrolls content to the specified position + */ + scrollContentTo: function scrollContentTo(value) { + $('#main [data-role="content"]').scrollview('scrollTo', 0, value); + }, + + /** + * @param {FileSystemStorage[]} nodes Storage elements + */ + displayStorages: function Ui_displayStorages(nodes) { + var len = nodes.length, + listElements = [], + nodeName, + listTemplate = '', + i; + + this.updateNavbar(''); + + for (i = 0; i < len; i = i + 1) { + nodeName = nodes[i].label.trim(); + if (nodeName !== '' && (nodes[i].type === 0 || nodes[i].type === 'INTERNAL') && nodeName.indexOf('wgt-') === -1) { + listElements.push(this.templateManager.get('folderRow', { + id: i, + name: nodeName, + uri: nodeName, + fullUri: nodeName + })); + } + } + + $('#levelUpBtn').addClass('vhidden'); + $('#homeBtn').addClass('vhidden'); + + $('#editActionBtn').addClass('vhidden'); + $('#moreActionBtn').addClass('vhidden'); + $('h1#mainTitle').html('Media'); + + // update file list + $('#fileList').empty(); + listTemplate = listElements.join(''); + $(listTemplate).appendTo('#fileList'); + // reset scrollview position + //$('#main .ui-scrollview-view').css('-webkit-transform', 'none'); + $('#fileList') + .trigger("refresh"); + + this.resetDefaultCheckBoxLabelEvents(); + this.hideSelectAllArea(); + this.handleCancelEditAction(); + }, + + /** + * renders node list for folder + * @param {string} folderName + * @param {File[]} nodes + */ + displayFolder: function Ui_displayFolder(folderName, nodes) { + var len = nodes.length, + listElements = [this.templateManager.get('levelUpRow')], + nodeName, + i; + + // update title + this.updateTitle(folderName); + // update navbar + this.updateNavbar(folderName); + + // render nodes + for (i = 0; i < len; i = i + 1) { + nodeName = nodes[i].name.trim(); + if (nodeName !== '') { + if (nodes[i].isDirectory) { + // folder + listElements.push(this.templateManager.get('folderRow', { + id: i, + name: nodeName, + uri: nodes[i].fullPath, + fullUri: nodes[i].toURI() + })); + } else { + // file + listElements.push(this.templateManager.get('fileRow', { + id: i, + name: nodeName, + uri: nodes[i].fullPath, + fullUri: nodes[i].toURI(), + thumbnailURI: this.helpers.getThumbnailURI(nodeName, nodes[i]) + })); + } + } + } + + if (listElements.length === 1) { + // set content for empty folder + listElements.push(this.templateManager.get('emptyFolder')); + } + + $('#levelUpBtn').removeClass('vhidden'); + $('#homeBtn').removeClass('vhidden'); + $('#editActionBtn').removeClass('vhidden'); + $('#moreActionBtn').removeClass('vhidden'); + + // update file list + $('#fileList').html(listElements.join('')) + .trigger('refresh') + .trigger('create'); + + if (this.editMode === true) { + $('.selectAll').show(); + $('ul#fileList > li').css('paddingLeft', '2rem'); + $('.my-ui-checkbox').removeClass('hidden'); + } else { + $('.selectAll').hide(); + $('ul#fileList > li').css('paddingLeft', '0'); + $('.my-ui-checkbox').addClass('hidden'); + } + }, + + /** + * Toggle a checkbox associated with a given list element + * @param {jQuery} listElement + */ + toggleCheckBoxState: function Ui_toggleCheckBoxState(listElement) { + + var checkboxInput = null; + + checkboxInput = listElement.find('form > div.ui-checkbox input'); + checkboxInput + .attr('checked', !checkboxInput.attr('checked')) + .data('checkboxradio').refresh(); + }, + + /** + * Shows item checkboxes and topbar with select all option + */ + showEditCheckBoxes: function Ui_showEditCheckBoxes() { + var self = this; + + this.showSelectAllArea(); + + $('ul#fileList > li').animate({paddingLeft: '2rem'}, 500, 'swing', function () { + self.editMode = true; + $('.my-ui-checkbox').removeClass('hidden'); + }); + }, + + /** + * Hides item checkboxes and topbar with select all option + * All checkboxes are auto uncheked + */ + hideEditCheckBoxes: function Ui_hideEditCheckBoxes() { + var self = this; + + this.hideSelectAllArea(); // hide select all option topbar + + $('ul#fileList > li').animate({paddingLeft: '0'}, 200, 'swing', function () { + $('.my-ui-checkbox').addClass('hidden'); + $.mobile.activePage.page('refresh'); + }); + + // uncheck all checkboxes + $('ul#fileList input[type=checkbox]').each(function (index) { + var checkboxradio = $(this).data('checkboxradio'); + + $(this).attr('checked', false); + + if (checkboxradio) { + checkboxradio.refresh(); + } + }); + + //uncheck select all input + $('.ui-header .selectAll .ui-checkbox input') + .attr('checked', false) + .data('checkboxradio') + .refresh(); + }, + + /** + * Save current header and content height + */ + saveHeights: function Ui_saveHeights() { + this.currentHeaderHeight = $('#main div[data-role="header"]').height(); + this.currentScrollPosition = $('#main div[data-role="content"]').scrollview('getScrollPosition').y; + }, + + /** + * Changes content scroll position after showing/hiding selectAllArea + */ + changeContentScrollPosition: function Ui_changeContentScrollPosition() { + var diff; + if (this.currentScrollPosition !== 0) { + diff = $('#main div[data-role="header"]').height() - this.currentHeaderHeight; + $('#main div[data-role="content"]').scrollview('scrollTo', 0, -(this.currentScrollPosition + diff)); + } + }, + + /** + * Shows topbar with select all option + */ + showSelectAllArea: function Ui_showSelectAllArea() { + this.saveHeights(); + $('.selectAll').show(); + $.mobile.activePage.page('refresh'); + this.changeContentScrollPosition(); + }, + + /** + * Hides topbar with select all option + */ + hideSelectAllArea: function Ui_hideSelectAllArea() { + this.saveHeights(); + $('.selectAll').hide(); + $.mobile.activePage.page('refresh'); + this.changeContentScrollPosition(); + }, + + /** + * Enable specified options for tabbar + * @param {object} tabbar + * @param {array} enableOptions options to enable + */ + enableControlBarButtons: function Ui_enableControlBarButtons(tabbar, enableOptions) { + var i = 0, + len = enableOptions.length; + + for (i = 0; i < len; i += 1) { + tabbar.tabbar('enable', enableOptions[i]); + } + }, + + /** + * Disable specified options for tabbar + * @param {object} tabbar controlbar + * @param {array} disableOptions options to enable + */ + disableControlBarButtons: function Ui_disableControlBarButtons(tabbar, disableOptions) { + var i = 0, + len = disableOptions.length; + + for (i = 0; i < len; i += 1) { + tabbar.tabbar('disable', disableOptions[i]); + } + }, + + /** + * @param {string} path + */ + updateTitle: function Ui_updateTitle(path) { + var regexp = new RegExp('([^\/])+$', 'g'), + match = path.match(regexp), + lastDir = match[0] || '(dir)'; + $('h1#mainTitle').html(lastDir); + }, + + /** + * @param {string} path + */ + updateNavbar: function Ui_updateNavbar(path) { + var html = ['Media'], + splitted, + len, + i; + + if (typeof path === 'string' && path !== '') { + splitted = path.split('/'); + len = splitted.length; + + for (i = 0; i < len; i = i + 1) { + html.push('' + splitted[i] + ''); + } + } + $('#navbar').html(html.join(' > ')); + }, + + handleSelectAllChange: function Ui_handleSelectAllChange() { + var $selectAllInput = $('.ui-header .selectAll .ui-checkbox input'); + $selectAllInput.data('checkboxradio').refresh(); + + if ($selectAllInput.is(':checked')) { + // check all checkboxes + $('ul#fileList input[type=checkbox]').each(function (index) { + $(this).attr('checked', true); + $(this).data('checkboxradio').refresh(); + }); + + this.enableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + } else { + $('ul#fileList input[type=checkbox]').each(function (index) { + $(this).attr('checked', false); + $(this).data('checkboxradio').refresh(); + }); + + this.disableControlBarButtons($('.editTabbar'), [this.EDIT_TABBAR_DELETE_ACTION, this.EDIT_TABBAR_COPY_ACTION, this.EDIT_TABBAR_MOVE_ACTION]); + } + }, + + /** + * + */ + refreshSelectAllStatus: function Ui_refreshSelectAllStatus() { + var $selectAllInput = $('.ui-header .selectAll .ui-checkbox input'); + // update status of select all checkbox + if ($('ul#fileList input:checkbox:not(:checked)').length === 0) { + // all nodes checked + $selectAllInput.attr('checked', true).data('checkboxradio').refresh(); + } else { + // some node is not checked + $selectAllInput.attr('checked', false).data('checkboxradio').refresh(); + } + }, + + /** + * Unbinds default events for checkbox labels + */ + resetDefaultCheckBoxLabelEvents: function Ui_resetDefaultCheckBoxLabelEvents() { + $('div.ui-checkbox > label') + .unbind('vmousedown') + .unbind('vmouseup') + .unbind('vmouseover') + .unbind('vclick'); + }, + + /** + * Remove html node element from list + * @param {string} nodeId node id + */ + removeNodeFromList: function Ui_removeNodeFromList(nodeId) { + $('ul#fileList > li#' + nodeId).remove(); + + // hide select All checkbox if removed all elements; + if ($('ul#fileList > li.node').length === 0) { + this.hideSelectAllArea(); + } + }, + + /** + * Enable/Disable + */ + refreshPasteActionBtn: function Ui_refreshPasteActionBtn(clipboardEmpty) { + if (clipboardEmpty === true) { + $('#pasteActionBtnRow').addClass('hidden'); + } else { + $('#pasteActionBtnRow').removeClass('hidden'); + } + } + }; +}()); diff --git a/js/app.ui.templateManager.js b/js/app.ui.templateManager.js new file mode 100644 index 0000000..8d3de5d --- /dev/null +++ b/js/app.ui.templateManager.js @@ -0,0 +1,108 @@ +/*jslint devel: true*/ +/*global $, app */ +/** + * @class TemplateManager + */ +function TemplateManager() { + 'use strict'; + this.init(); +} + +(function () { // strict mode wrapper + 'use strict'; + TemplateManager.prototype = { + + /** + * Template cache + */ + cache: {}, + + /** + * UI module initialisation + */ + init: function init() { + + }, + + /** + * Returns template html (from cache) + */ + get: function TemplateManager_get(tplName, tplParams) { + + if (this.cache[tplName] !== undefined) { + return this.getCompleted(this.cache[tplName], tplParams); + } + return ''; + }, + + /** + * Load templates to cache + */ + loadToCache: function TemplateManager_loadToCache(tplNames, onSuccess) { + var self = this, + cachedTemplates = 0, + tplName, + tplPath; + + if ($.isArray(tplNames)) { + + // for each template + $.each(tplNames, function (index, fileName) { + + // cache template html + if (self.cache[fileName] === undefined) { + tplName = [fileName, app.config.get('templateExtension')].join(''); + tplPath = [app.config.get('templateDir'), tplName].join('/'); + + $.ajax({ + url: tplPath, + cache: true, + dataType: 'html', + async: true, + success: function (data) { + // increase counter + cachedTemplates += 1; + + // save to cache + self.cache[fileName] = data; + + // if all templates are cached launch callback + if (cachedTemplates >= tplNames.length && typeof onSuccess === 'function') { + onSuccess(); + } + }, + error: function (jqXHR, textStatus, errorThrown) { + alert(errorThrown); + } + }); + } else { + // template is already cached + cachedTemplates += 1; + // if all templates are cached launch callback + if (cachedTemplates >= tplNames.length && typeof onSuccess === 'function') { + onSuccess(); + } + } + }); + + } + }, + + /** + * Returns template completed by specified params + */ + getCompleted: function TemplateManager_getCompleted(tplHtml, tplParams) { + var tplParam, replaceRegExp; + + for (tplParam in tplParams) { + if (tplParams.hasOwnProperty(tplParam)) { + replaceRegExp = new RegExp(['%', tplParam, '%'].join(''), 'g'); + tplHtml = tplHtml.replace(replaceRegExp, tplParams[tplParam]); + } + } + + return tplHtml; + } + }; + +}()); \ No newline at end of file diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..45d59e6 --- /dev/null +++ b/js/main.js @@ -0,0 +1,78 @@ +/* + * Copyright 2012 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*jslint devel: true*/ +/*global $, tizen, App */ + +/** + * This file acts as a loader for the application and its dependencies + * + * First, the 'app.js' script is loaded . + * Then, scripts defined in 'app.requires' are loaded. + * Finally, the app is initialised - the app is instantiated ('app = new App()') + * and 'app.init()' is called. + */ + + +var app = null; + +(function () { // strict mode wrapper + 'use strict'; + + ({ + /** + * Loader init - load the App constructor + */ + init: function init() { + var self = this; + $.getScript('js/app.js') + .done(function () { + // once the app is loaded, create the app object + // and load the libraries + app = new App(); + self.loadLibs(); + }) + .fail(this.onGetScriptError); + }, + + /** + * Load dependencies + */ + loadLibs: function loadLibs() { + var loadedLibs = 0; + if ($.isArray(app.requires)) { + $.each(app.requires, function (index, filename) { + $.getScript(filename) + .done(function () { + loadedLibs += 1; + if (loadedLibs >= app.requires.length) { + // All dependencies are loaded - initialise the app + app.init(); + } + }) + .fail(this.onGetScriptError); + }); + } + }, + + /** + * Handle ajax errors + */ + onGetScriptError: function onGetScriptError(e, jqxhr, setting, exception) { + alert('An error occurred: ' + e.message); + } + }).init(); // run the loader +}()); \ No newline at end of file diff --git a/templates/emptyFolder.tpl b/templates/emptyFolder.tpl new file mode 100644 index 0000000..da09356 --- /dev/null +++ b/templates/emptyFolder.tpl @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/templates/fileRow.tpl b/templates/fileRow.tpl new file mode 100644 index 0000000..47e30f2 --- /dev/null +++ b/templates/fileRow.tpl @@ -0,0 +1,5 @@ +
  • + + + %name% +
  • \ No newline at end of file diff --git a/templates/folderRow.tpl b/templates/folderRow.tpl new file mode 100644 index 0000000..c4e91e3 --- /dev/null +++ b/templates/folderRow.tpl @@ -0,0 +1,6 @@ +
  • + + + %name% + +
  • \ No newline at end of file diff --git a/templates/levelUpRow.tpl b/templates/levelUpRow.tpl new file mode 100644 index 0000000..ddbc7cf --- /dev/null +++ b/templates/levelUpRow.tpl @@ -0,0 +1,3 @@ +
  • + .. +
  • \ No newline at end of file diff --git a/templates/main.tpl b/templates/main.tpl new file mode 100644 index 0000000..68dde67 --- /dev/null +++ b/templates/main.tpl @@ -0,0 +1,64 @@ +
    +
    +

    + Home + Up + + +
    + +
    +
      +
      + +
      +
      +
      + +
      + +
      + + + + + + + +
      + New folder +
      + Paste to folder +
      +
      +
      +

      Add new folder

      +

      + +

      +

      + + +

      +
      +
      +
      +
      \ No newline at end of file -- 2.7.4