Index: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.LanguageUpWiz.js |
— | — | @@ -0,0 +1,492 @@ |
| 2 | +mw.addMessages({ |
| 3 | + "mwe-code-unknown": "Unknown language" |
| 4 | +}); |
| 5 | + |
| 6 | +/** |
| 7 | + * Utility class which knows about languages, and how to construct HTML to select them |
| 8 | + * TODO: make this a more common library, used by this and TimedText |
| 9 | + */ |
| 10 | +mw.LanguageUpWiz = { |
| 11 | + |
| 12 | + defaultCode: 'en', // when we absolutely have no idea what language to preselect |
| 13 | + |
| 14 | + initialized: false, |
| 15 | + |
| 16 | + UNKNOWN: 'unknown', |
| 17 | + |
| 18 | + /** |
| 19 | + * List of all languages mediaWiki supports ( Avoid an api call to get this same info ) |
| 20 | + * http://commons.wikimedia.org/w/api.php?action=query&meta=siteinfo&siprop=languages&format=jsonfm |
| 21 | + * |
| 22 | + * Languages sorted by name, using tools in $SVNROOT/mediawiki/trunk/tools/langcodes |
| 23 | + * This is somewhat better than sorting by code (which produces totally bizarre results) but is not |
| 24 | + * a true lexicographic sort |
| 25 | + */ |
| 26 | + languages: [ |
| 27 | + { code: "ace", text: "Ac\u00e8h" }, |
| 28 | + { code: "af", text: "Afrikaans" }, |
| 29 | + { code: "ak", text: "Akan" }, |
| 30 | + { code: "als", text: "Alemannisch" }, // } XXX someone fix this please |
| 31 | + { code: "gsw", text: "Alemannisch" }, // } |
| 32 | + { code: "ang", text: "Anglo-Saxon" }, |
| 33 | + { code: "an", text: "Aragon\u00e9s" }, |
| 34 | + { code: "roa-rup", text: "Arm\u00e3neashce" }, |
| 35 | + { code: "frp", text: "Arpetan" }, |
| 36 | + { code: "ast", text: "Asturianu" }, |
| 37 | + { code: "gn", text: "Ava\u00f1e'\u1ebd" }, |
| 38 | + { code: "ay", text: "Aymar aru" }, |
| 39 | + { code: "az", text: "Az\u0259rbaycan" }, |
| 40 | + { code: "id", text: "Bahasa Indonesia" }, |
| 41 | + { code: "ms", text: "Bahasa Melayu" }, |
| 42 | + { code: "bm", text: "Bamanankan" }, |
| 43 | + { code: "map-bms", text: "Basa Banyumasan" }, |
| 44 | + { code: "jv", text: "Basa Jawa" }, |
| 45 | + { code: "su", text: "Basa Sunda" }, |
| 46 | + { code: "bcl", text: "Bikol Central" }, |
| 47 | + { code: "bi", text: "Bislama" }, |
| 48 | + { code: "bar", text: "Boarisch" }, |
| 49 | + { code: "bs", text: "Bosanski" }, |
| 50 | + { code: "br", text: "Brezhoneg" }, |
| 51 | + { code: "en-gb", text: "British English" }, |
| 52 | + { code: "nan", text: "B\u00e2n-l\u00e2m-g\u00fa" }, |
| 53 | + { code: "zh-min-nan", text: "B\u00e2n-l\u00e2m-g\u00fa" }, |
| 54 | + { code: "ca", text: "Catal\u00e0" }, |
| 55 | + { code: "ceb", text: "Cebuano" }, |
| 56 | + { code: "ch", text: "Chamoru" }, |
| 57 | + { code: "cbk-zam", text: "Chavacano de Zamboanga" }, |
| 58 | + { code: "ny", text: "Chi-Chewa" }, |
| 59 | + { code: "cho", text: "Choctaw" }, |
| 60 | + { code: "sei", text: "Cmique Itom" }, |
| 61 | + { code: "co", text: "Corsu" }, |
| 62 | + { code: "cy", text: "Cymraeg" }, |
| 63 | + { code: "da", text: "Dansk" }, |
| 64 | + { code: "dk", text: "Dansk (deprecated:da)" }, // XXX deprecated? |
| 65 | + { code: "pdc", text: "Deitsch" }, |
| 66 | + { code: "de", text: "Deutsch" }, |
| 67 | + { code: "de-formal", text: "Deutsch (Sie-Form)" }, |
| 68 | + { code: "nv", text: "Din\u00e9 bizaad" }, |
| 69 | + { code: "dsb", text: "Dolnoserbski" }, |
| 70 | + { code: "na", text: "Dorerin Naoero" }, |
| 71 | + { code: "mh", text: "Ebon" }, |
| 72 | + { code: "et", text: "Eesti" }, |
| 73 | + { code: "eml", text: "Emili\u00e0n e rumagn\u00f2l" }, |
| 74 | + { code: "en", text: "English" }, |
| 75 | + { code: "es", text: "Espa\u00f1ol" }, |
| 76 | + { code: "eo", text: "Esperanto" }, |
| 77 | + { code: "ext", text: "Estreme\u00f1u" }, |
| 78 | + { code: "eu", text: "Euskara" }, |
| 79 | + { code: "ee", text: "E\u028begbe" }, |
| 80 | + { code: "hif", text: "Fiji Hindi" }, // } XXX fix this |
| 81 | + { code: "hif-latn", text: "Fiji Hindi" }, // } |
| 82 | + { code: "fr", text: "Fran\u00e7ais" }, |
| 83 | + { code: "frc", text: "Fran\u00e7ais canadien" }, |
| 84 | + { code: "fy", text: "Frysk" }, |
| 85 | + { code: "ff", text: "Fulfulde" }, |
| 86 | + { code: "fur", text: "Furlan" }, |
| 87 | + { code: "fo", text: "F\u00f8royskt" }, |
| 88 | + { code: "ga", text: "Gaeilge" }, |
| 89 | + { code: "gv", text: "Gaelg" }, |
| 90 | + { code: "sm", text: "Gagana Samoa" }, |
| 91 | + { code: "gag", text: "Gagauz" }, |
| 92 | + { code: "gl", text: "Galego" }, |
| 93 | + { code: "aln", text: "Geg\u00eb" }, |
| 94 | + { code: "gd", text: "G\u00e0idhlig" }, |
| 95 | + { code: "ki", text: "G\u0129k\u0169y\u0169" }, |
| 96 | + { code: "hak", text: "Hak-k\u00e2-fa" }, |
| 97 | + { code: "haw", text: "Hawai`i" }, |
| 98 | + { code: "ho", text: "Hiri Motu" }, |
| 99 | + { code: "hsb", text: "Hornjoserbsce" }, |
| 100 | + { code: "hr", text: "Hrvatski" }, |
| 101 | + { code: "io", text: "Ido" }, |
| 102 | + { code: "ig", text: "Igbo" }, |
| 103 | + { code: "ilo", text: "Ilokano" }, |
| 104 | + { code: "hil", text: "Ilonggo" }, |
| 105 | + { code: "ia", text: "Interlingua" }, |
| 106 | + { code: "ie", text: "Interlingue" }, |
| 107 | + { code: "it", text: "Italiano" }, |
| 108 | + { code: "ik", text: "I\u00f1upiak" }, |
| 109 | + { code: "jut", text: "Jysk" }, |
| 110 | + { code: "kl", text: "Kalaallisut" }, |
| 111 | + { code: "kr", text: "Kanuri" }, |
| 112 | + { code: "pam", text: "Kapampangan" }, |
| 113 | + { code: "csb", text: "Kasz\u00ebbsczi" }, |
| 114 | + { code: "kw", text: "Kernowek" }, |
| 115 | + { code: "krj", text: "Kinaray-a" }, |
| 116 | + { code: "rw", text: "Kinyarwanda" }, |
| 117 | + { code: "rn", text: "Kirundi" }, |
| 118 | + { code: "sw", text: "Kiswahili" }, |
| 119 | + { code: "kg", text: "Kongo" }, |
| 120 | + { code: "avk", text: "Kotava" }, |
| 121 | + { code: "ht", text: "Krey\u00f2l ayisyen" }, |
| 122 | + { code: "kri", text: "Krio" }, |
| 123 | + { code: "ku", text: "Kurd\u00ee \/ \u0643\u0648\u0631\u062f\u06cc" }, |
| 124 | + { code: "kiu", text: "Kurmanc\u00ee" }, |
| 125 | + { code: "kj", text: "Kwanyama" }, |
| 126 | + { code: "lad", text: "Ladino" }, |
| 127 | + { code: "la", text: "Latina" }, |
| 128 | + { code: "lv", text: "Latvie\u0161u" }, |
| 129 | + { code: "lt", text: "Lietuvi\u0173" }, |
| 130 | + { code: "li", text: "Limburgs" }, |
| 131 | + { code: "lfn", text: "Lingua Franca Nova" }, |
| 132 | + { code: "ln", text: "Ling\u00e1la" }, |
| 133 | + { code: "jbo", text: "Lojban" }, |
| 134 | + { code: "lg", text: "Luganda" }, |
| 135 | + { code: "lmo", text: "Lumbaart" }, |
| 136 | + { code: "lb", text: "L\u00ebtzebuergesch" }, |
| 137 | + { code: "lij", text: "L\u00edguru" }, |
| 138 | + { code: "hu", text: "Magyar" }, |
| 139 | + { code: "mg", text: "Malagasy" }, |
| 140 | + { code: "mt", text: "Malti" }, |
| 141 | + { code: "arn", text: "Mapudungun" }, |
| 142 | + { code: "mwl", text: "Mirand\u00e9s" }, |
| 143 | + { code: "mus", text: "Mvskoke" }, |
| 144 | + { code: "cdo", text: "M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304" }, |
| 145 | + { code: "mi", text: "M\u0101ori" }, |
| 146 | + { code: "fj", text: "Na Vosa Vakaviti" }, |
| 147 | + { code: "nl", text: "Nederlands" }, |
| 148 | + { code: "nds-nl", text: "Nedersaksisch" }, |
| 149 | + { code: "niu", text: "Niu\u0113" }, |
| 150 | + { code: "nap", text: "Nnapulitano" }, |
| 151 | + { code: "pih", text: "Norfuk \/ Pitkern" }, |
| 152 | + { code: "nb", text: "Norsk (bokm\u00e5l)" }, |
| 153 | + { code: "no", text: "Norsk (bokm\u00e5l)" }, |
| 154 | + { code: "nn", text: "Norsk (nynorsk)" }, |
| 155 | + { code: "nrm", text: "Nouormand" }, |
| 156 | + { code: "nov", text: "Novial" }, |
| 157 | + { code: "nah", text: "N\u0101huatl" }, |
| 158 | + { code: "cr", text: "N\u0113hiyaw\u0113win \/ \u14c0\u1426\u1403\u152d\u140d\u140f\u1423" }, |
| 159 | + { code: "uz", text: "O'zbek" }, |
| 160 | + { code: "oc", text: "Occitan" }, |
| 161 | + { code: "om", text: "Oromoo" }, |
| 162 | + { code: "ng", text: "Oshiwambo" }, |
| 163 | + { code: "hz", text: "Otsiherero" }, |
| 164 | + { code: "pag", text: "Pangasinan" }, |
| 165 | + { code: "pap", text: "Papiamentu" }, |
| 166 | + { code: "pfl", text: "Pf\u00e4lzisch" }, |
| 167 | + { code: "pcd", text: "Picard" }, |
| 168 | + { code: "pms", text: "Piemont\u00e8is" }, |
| 169 | + { code: "nds", text: "Plattd\u00fc\u00fctsch" }, |
| 170 | + { code: "pdt", text: "Plautdietsch" }, |
| 171 | + { code: "pl", text: "Polski" }, |
| 172 | + { code: "pt", text: "Portugu\u00eas" }, |
| 173 | + { code: "pt-br", text: "Portugu\u00eas do Brasil" }, |
| 174 | + { code: "aa", text: "Qaf\u00e1r af" }, |
| 175 | + { code: "kaa", text: "Qaraqalpaqsha" }, |
| 176 | + { code: "crh", text: "Q\u0131r\u0131mtatarca" }, |
| 177 | + { code: "ty", text: "Reo M\u0101`ohi" }, |
| 178 | + { code: "ksh", text: "Ripoarisch" }, |
| 179 | + { code: "rmy", text: "Romani" }, |
| 180 | + { code: "ro", text: "Rom\u00e2n\u0103" }, |
| 181 | + { code: "rm", text: "Rumantsch" }, |
| 182 | + { code: "qu", text: "Runa Simi" }, |
| 183 | + { code: "sc", text: "Sardu" }, |
| 184 | + { code: "sdc", text: "Sassaresu" }, |
| 185 | + { code: "sli", text: "Schl\u00e4sch" }, |
| 186 | + { code: "de-ch", text: "Schweizer Hochdeutsch" }, |
| 187 | + { code: "sco", text: "Scots" }, |
| 188 | + { code: "stq", text: "Seeltersk" }, |
| 189 | + { code: "st", text: "Sesotho" }, |
| 190 | + { code: "nso", text: "Sesotho sa Leboa" }, |
| 191 | + { code: "tn", text: "Setswana" }, |
| 192 | + { code: "sq", text: "Shqip" }, |
| 193 | + { code: "ss", text: "SiSwati" }, |
| 194 | + { code: "scn", text: "Sicilianu" }, |
| 195 | + { code: "loz", text: "Silozi" }, |
| 196 | + { code: "simple", text: "Simple English" }, |
| 197 | + { code: "sk", text: "Sloven\u010dina" }, |
| 198 | + { code: "sl", text: "Sloven\u0161\u010dina" }, |
| 199 | + { code: "so", text: "Soomaaliga" }, |
| 200 | + { code: "ckb", text: "Soran\u00ee \/ \u06a9\u0648\u0631\u062f\u06cc" }, |
| 201 | + { code: "srn", text: "Sranantongo" }, |
| 202 | + { code: "sr-el", text: "Srpski (latinica)" }, |
| 203 | + { code: "sh", text: "Srpskohrvatski \/ \u0421\u0440\u043f\u0441\u043a\u043e\u0445\u0440\u0432\u0430\u0442\u0441\u043a\u0438" }, |
| 204 | + { code: "fi", text: "Suomi" }, |
| 205 | + { code: "sv", text: "Svenska" }, |
| 206 | + { code: "se", text: "S\u00e1megiella" }, |
| 207 | + { code: "sg", text: "S\u00e4ng\u00f6" }, |
| 208 | + { code: "tl", text: "Tagalog" }, |
| 209 | + { code: "kab", text: "Taqbaylit" }, |
| 210 | + { code: "roa-tara", text: "Tarand\u00edne" }, |
| 211 | + { code: "rif", text: "Tarifit" }, |
| 212 | + { code: "tt-latn", text: "Tatar\u00e7a" }, |
| 213 | + { code: "shi", text: "Ta\u0161l\u1e25iyt" }, |
| 214 | + { code: "tet", text: "Tetun" }, |
| 215 | + { code: "vi", text: "Ti\u1ebfng Vi\u1ec7t" }, |
| 216 | + { code: "tpi", text: "Tok Pisin" }, |
| 217 | + { code: "tokipona", text: "Toki Pona" }, |
| 218 | + { code: "tp", text: "Toki Pona (deprecated:tokipona)" }, // XXX deprecated? |
| 219 | + { code: "chy", text: "Tsets\u00eahest\u00e2hese" }, |
| 220 | + { code: "ve", text: "Tshivenda" }, |
| 221 | + { code: "tw", text: "Twi" }, |
| 222 | + { code: "tk", text: "T\u00fcrkmen\u00e7e" }, |
| 223 | + { code: "tr", text: "T\u00fcrk\u00e7e" }, |
| 224 | + { code: "ug-latn", text: "Uyghurche\u200e" }, |
| 225 | + { code: "ug", text: "Uyghurche\u200e \/ \u0626\u06c7\u064a\u063a\u06c7\u0631\u0686\u06d5" }, |
| 226 | + { code: "za", text: "Vahcuengh" }, |
| 227 | + { code: "vep", text: "Vepsan kel'" }, |
| 228 | + { code: "ruq", text: "Vl\u0103he\u015fte" }, |
| 229 | + { code: "ruq-latn", text: "Vl\u0103he\u015fte" }, |
| 230 | + { code: "vo", text: "Volap\u00fck" }, |
| 231 | + { code: "vec", text: "V\u00e8neto" }, |
| 232 | + { code: "fiu-vro", text: "V\u00f5ro" }, |
| 233 | + { code: "vro", text: "V\u00f5ro" }, |
| 234 | + { code: "wa", text: "Walon" }, |
| 235 | + { code: "vls", text: "West-Vlams" }, |
| 236 | + { code: "war", text: "Winaray" }, |
| 237 | + { code: "wo", text: "Wolof" }, |
| 238 | + { code: "ts", text: "Xitsonga" }, |
| 239 | + { code: "yo", text: "Yor\u00f9b\u00e1" }, |
| 240 | + { code: "diq", text: "Zazaki" }, |
| 241 | + { code: "zea", text: "Ze\u00eauws" }, |
| 242 | + { code: "sn", text: "chiShona" }, |
| 243 | + { code: "tum", text: "chiTumbuka" }, |
| 244 | + { code: "ike-latn", text: "inuktitut" }, |
| 245 | + { code: "xh", text: "isiXhosa" }, |
| 246 | + { code: "zu", text: "isiZulu" }, |
| 247 | + { code: "to", text: "lea faka-Tonga" }, |
| 248 | + { code: "tg-latn", text: "tojik\u012b" }, |
| 249 | + { code: "is", text: "\u00cdslenska" }, |
| 250 | + { code: "de-at", text: "\u00d6sterreichisches Deutsch" }, |
| 251 | + { code: "szl", text: "\u015al\u016fnski" }, |
| 252 | + { code: "el", text: "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac" }, |
| 253 | + { code: "pnt", text: "\u03a0\u03bf\u03bd\u03c4\u03b9\u03b1\u03ba\u03ac" }, |
| 254 | + { code: "av", text: "\u0410\u0432\u0430\u0440" }, |
| 255 | + { code: "ab", text: "\u0410\u04a7\u0441\u0443\u0430" }, |
| 256 | + { code: "ba", text: "\u0411\u0430\u0448\u04a1\u043e\u0440\u0442" }, |
| 257 | + { code: "be", text: "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f" }, |
| 258 | + { code: "be-tarask", text: "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430)" }, |
| 259 | + { code: "be-x-old", text: "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430)" }, |
| 260 | + { code: "bxr", text: "\u0411\u0443\u0440\u044f\u0430\u0434" }, |
| 261 | + { code: "bg", text: "\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438" }, |
| 262 | + { code: "ruq-cyrl", text: "\u0412\u043b\u0430\u0445\u0435\u0441\u0442\u0435" }, |
| 263 | + { code: "inh", text: "\u0413\u0406\u0430\u043b\u0433\u0406\u0430\u0439 \u011eal\u011faj" }, |
| 264 | + { code: "os", text: "\u0418\u0440\u043e\u043d\u0430\u0443" }, |
| 265 | + { code: "kv", text: "\u041a\u043e\u043c\u0438" }, |
| 266 | + { code: "ky", text: "\u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430" }, |
| 267 | + { code: "lbe", text: "\u041b\u0430\u043a\u043a\u0443" }, |
| 268 | + { code: "lez", text: "\u041b\u0435\u0437\u0433\u0438" }, |
| 269 | + { code: "mk", text: "\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438" }, |
| 270 | + { code: "mdf", text: "\u041c\u043e\u043a\u0448\u0435\u043d\u044c" }, |
| 271 | + { code: "mo", text: "\u041c\u043e\u043b\u0434\u043e\u0432\u0435\u043d\u044f\u0441\u043a\u044d" }, |
| 272 | + { code: "mn", text: "\u041c\u043e\u043d\u0433\u043e\u043b" }, |
| 273 | + { code: "ce", text: "\u041d\u043e\u0445\u0447\u0438\u0439\u043d" }, |
| 274 | + { code: "mhr", text: "\u041e\u043b\u044b\u043a \u041c\u0430\u0440\u0438\u0439" }, |
| 275 | + { code: "ru", text: "\u0420\u0443\u0441\u0441\u043a\u0438\u0439" }, |
| 276 | + { code: "sah", text: "\u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430" }, |
| 277 | + { code: "cu", text: "\u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f" }, |
| 278 | + { code: "sr-ec", text: "\u0421\u0440\u043f\u0441\u043a\u0438 (\u045b\u0438\u0440\u0438\u043b\u0438\u0446\u0430)" }, |
| 279 | + { code: "sr", text: "\u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski" }, |
| 280 | + { code: "tt-cyrl", text: "\u0422\u0430\u0442\u0430\u0440\u0447\u0430" }, |
| 281 | + { code: "tt", text: "\u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a" }, |
| 282 | + { code: "tg", text: "\u0422\u043e\u04b7\u0438\u043a\u04e3" }, |
| 283 | + { code: "tg-cyrl", text: "\u0422\u043e\u04b7\u0438\u043a\u04e3" }, |
| 284 | + { code: "tyv", text: "\u0422\u044b\u0432\u0430 \u0434\u044b\u043b" }, |
| 285 | + { code: "udm", text: "\u0423\u0434\u043c\u0443\u0440\u0442" }, |
| 286 | + { code: "uk", text: "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430" }, |
| 287 | + { code: "xal", text: "\u0425\u0430\u043b\u044c\u043c\u0433" }, |
| 288 | + { code: "cv", text: "\u0427\u04d1\u0432\u0430\u0448\u043b\u0430" }, |
| 289 | + { code: "myv", text: "\u042d\u0440\u0437\u044f\u043d\u044c" }, |
| 290 | + { code: "kk", text: "\u049a\u0430\u0437\u0430\u049b\u0448\u0430" }, |
| 291 | + { code: "hy", text: "\u0540\u0561\u0575\u0565\u0580\u0565\u0576" }, |
| 292 | + { code: "yi", text: "\u05d9\u05d9\u05b4\u05d3\u05d9\u05e9" }, |
| 293 | + { code: "he", text: "\u05e2\u05d1\u05e8\u05d9\u05ea" }, |
| 294 | + { code: "ug-arab", text: "\u0626\u06c7\u064a\u063a\u06c7\u0631\u0686\u06d5" }, |
| 295 | + { code: "ur", text: "\u0627\u0631\u062f\u0648" }, |
| 296 | + { code: "ar", text: "\u0627\u0644\u0639\u0631\u0628\u064a\u0629" }, |
| 297 | + { code: "bqi", text: "\u0628\u062e\u062a\u064a\u0627\u0631\u064a" }, |
| 298 | + { code: "bcc", text: "\u0628\u0644\u0648\u0686\u06cc \u0645\u06a9\u0631\u0627\u0646\u06cc" }, |
| 299 | + { code: "sd", text: "\u0633\u0646\u068c\u064a" }, |
| 300 | + { code: "fa", text: "\u0641\u0627\u0631\u0633\u06cc" }, |
| 301 | + { code: "arz", text: "\u0645\u0635\u0631\u0649" }, |
| 302 | + { code: "mzn", text: "\u0645\u064e\u0632\u0650\u0631\u0648\u0646\u064a" }, |
| 303 | + { code: "ha", text: "\u0647\u064e\u0648\u064f\u0633\u064e" }, |
| 304 | + { code: "pnb", text: "\u067e\u0646\u062c\u0627\u0628\u06cc" }, |
| 305 | + { code: "ps", text: "\u067e\u069a\u062a\u0648" }, |
| 306 | + { code: "glk", text: "\u06af\u06cc\u0644\u06a9\u06cc" }, |
| 307 | + { code: "arc", text: "\u0710\u072a\u0721\u071d\u0710" }, |
| 308 | + { code: "dv", text: "\u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0" }, |
| 309 | + { code: "ks", text: "\u0915\u0936\u094d\u092e\u0940\u0930\u0940 - (\u0643\u0634\u0645\u064a\u0631\u064a)" }, |
| 310 | + { code: "new", text: "\u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e" }, |
| 311 | + { code: "ne", text: "\u0928\u0947\u092a\u093e\u0932\u0940" }, |
| 312 | + { code: "pi", text: "\u092a\u093e\u093f\u0934" }, |
| 313 | + { code: "hif-deva", text: "\u092b\u093c\u0940\u091c\u0940 \u0939\u093f\u0928\u094d\u0926\u0940" }, |
| 314 | + { code: "bh", text: "\u092d\u094b\u091c\u092a\u0941\u0930\u0940" }, |
| 315 | + { code: "mr", text: "\u092e\u0930\u093e\u0920\u0940" }, |
| 316 | + { code: "mai", text: "\u092e\u0948\u0925\u093f\u0932\u0940" }, |
| 317 | + { code: "sa", text: "\u0938\u0902\u0938\u094d\u0915\u0943\u0924" }, |
| 318 | + { code: "hi", text: "\u0939\u093f\u0928\u094d\u0926\u0940" }, |
| 319 | + { code: "as", text: "\u0985\u09b8\u09ae\u09c0\u09af\u09bc\u09be" }, |
| 320 | + { code: "bpy", text: "\u0987\u09ae\u09be\u09b0 \u09a0\u09be\u09b0\/\u09ac\u09bf\u09b7\u09cd\u09a3\u09c1\u09aa\u09cd\u09b0\u09bf\u09af\u09bc\u09be \u09ae\u09a3\u09bf\u09aa\u09c1\u09b0\u09c0" }, |
| 321 | + { code: "bn", text: "\u09ac\u09be\u0982\u09b2\u09be" }, |
| 322 | + { code: "pa", text: "\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40" }, |
| 323 | + { code: "gu", text: "\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0" }, |
| 324 | + { code: "or", text: "\u0b13\u0b21\u0b3c\u0b3f\u0b06" }, |
| 325 | + { code: "ta", text: "\u0ba4\u0bae\u0bbf\u0bb4\u0bcd" }, |
| 326 | + { code: "te", text: "\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41" }, |
| 327 | + { code: "sma", text: "\u00c5arjelsaemien" }, |
| 328 | + { code: "kn", text: "\u0c95\u0ca8\u0ccd\u0ca8\u0ca1" }, |
| 329 | + { code: "tcy", text: "\u0ca4\u0cc1\u0cb3\u0cc1" }, |
| 330 | + { code: "ml", text: "\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02" }, |
| 331 | + { code: "si", text: "\u0dc3\u0dd2\u0d82\u0dc4\u0dbd" }, |
| 332 | + { code: "th", text: "\u0e44\u0e17\u0e22" }, |
| 333 | + { code: "lo", text: "\u0ea5\u0eb2\u0ea7" }, |
| 334 | + { code: "dz", text: "\u0f47\u0f7c\u0f44\u0f0b\u0f41" }, |
| 335 | + { code: "bo", text: "\u0f56\u0f7c\u0f51\u0f0b\u0f61\u0f72\u0f42" }, |
| 336 | + { code: "my", text: "\u1019\u103c\u1014\u103a\u1019\u102c\u1018\u102c\u101e\u102c" }, |
| 337 | + { code: "cs", text: "\u010cesky" }, |
| 338 | + { code: "xmf", text: "\u10db\u10d0\u10e0\u10d2\u10d0\u10da\u10e3\u10e0\u10d8" }, |
| 339 | + { code: "ka", text: "\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8" }, |
| 340 | + { code: "ti", text: "\u1275\u130d\u122d\u129b" }, |
| 341 | + { code: "am", text: "\u12a0\u121b\u122d\u129b" }, |
| 342 | + { code: "chr", text: "\u13e3\u13b3\u13a9" }, |
| 343 | + { code: "ike-cans", text: "\u1403\u14c4\u1483\u144e\u1450\u1466" }, |
| 344 | + { code: "iu", text: "\u1403\u14c4\u1483\u144e\u1450\u1466\/inuktitut" }, |
| 345 | + { code: "km", text: "\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a" }, |
| 346 | + { code: "bat-smg", text: "\u017demait\u0117\u0161ka" }, |
| 347 | + { code: "bug", text: "\u1a05\u1a14 \u1a15\u1a18\u1a01\u1a17" }, |
| 348 | + { code: "grc", text: "\u1f08\u03c1\u03c7\u03b1\u03af\u03b1 \u1f11\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u1f74" }, |
| 349 | + { code: "ku-latn", text: "\u202aKurd\u00ee (lat\u00een\u00ee)\u202c" }, |
| 350 | + { code: "kk-tr", text: "\u202aQazaq\u015fa (T\u00fcrk\u00efya)\u202c" }, |
| 351 | + { code: "kk-latn", text: "\u202aQazaq\u015fa (lat\u0131n)\u202c" }, |
| 352 | + { code: "crh-latn", text: "\u202aQ\u0131r\u0131mtatarca (Latin)\u202c" }, |
| 353 | + { code: "ckb-latn", text: "\u202aSoran\u00ee (lat\u00een\u00ee)\u202c" }, |
| 354 | + { code: "crh-cyrl", text: "\u202a\u041a\u044a\u044b\u0440\u044b\u043c\u0442\u0430\u0442\u0430\u0440\u0434\u0436\u0430 (\u041a\u0438\u0440\u0438\u043b\u043b)\u202c" }, |
| 355 | + { code: "kk-cyrl", text: "\u202a\u049a\u0430\u0437\u0430\u049b\u0448\u0430 (\u043a\u0438\u0440\u0438\u043b)\u202c" }, |
| 356 | + { code: "kk-kz", text: "\u202a\u049a\u0430\u0437\u0430\u049b\u0448\u0430 (\u049a\u0430\u0437\u0430\u049b\u0441\u0442\u0430\u043d)\u202c" }, |
| 357 | + { code: "kk-arab", text: "\u202b\u0642\u0627\u0632\u0627\u0642\u0634\u0627 (\u062a\u0674\u0648\u062a\u06d5)\u202c" }, |
| 358 | + { code: "kk-cn", text: "\u202b\u0642\u0627\u0632\u0627\u0642\u0634\u0627 (\u062c\u06c7\u0646\u06af\u0648)\u202c" }, |
| 359 | + { code: "ku-arab", text: "\u202b\u0643\u0648\u0631\u062f\u064a (\u0639\u06d5\u0631\u06d5\u0628\u06cc)\u202c" }, |
| 360 | + { code: "ckb-arab", text: "\u202b\u06a9\u0648\u0631\u062f\u06cc (\u0639\u06d5\u0631\u06d5\u0628\u06cc)\u202c" }, |
| 361 | + { code: "zh", text: "\u4e2d\u6587" }, |
| 362 | + { code: "zh-cn", text: "\u4e2d\u6587(\u4e2d\u56fd\u5927\u9646)" }, |
| 363 | + { code: "zh-tw", text: "\u4e2d\u6587(\u53f0\u7063)" }, |
| 364 | + { code: "zh-sg", text: "\u4e2d\u6587(\u65b0\u52a0\u5761)" }, |
| 365 | + { code: "zh-mo", text: "\u4e2d\u6587(\u6fb3\u9580)" }, |
| 366 | + { code: "zh-hans", text: "\u4e2d\u6587(\u7b80\u4f53)" }, |
| 367 | + { code: "zh-hant", text: "\u4e2d\u6587(\u7e41\u9ad4)" }, |
| 368 | + { code: "zh-hk", text: "\u4e2d\u6587(\u9999\u6e2f)" }, |
| 369 | + { code: "zh-my", text: "\u4e2d\u6587(\u9a6c\u6765\u897f\u4e9a)" }, |
| 370 | + { code: "wuu", text: "\u5434\u8bed" }, |
| 371 | + { code: "lzh", text: "\u6587\u8a00" }, |
| 372 | + { code: "zh-classical", text: "\u6587\u8a00" }, |
| 373 | + { code: "ja", text: "\u65e5\u672c\u8a9e" }, |
| 374 | + { code: "yue", text: "\u7cb5\u8a9e" }, |
| 375 | + { code: "zh-yue", text: "\u7cb5\u8a9e" }, |
| 376 | + { code: "gan", text: "\u8d1b\u8a9e" }, |
| 377 | + { code: "gan-hant", text: "\u8d1b\u8a9e(\u7e41\u9ad4)" }, |
| 378 | + { code: "gan-hans", text: "\u8d63\u8bed(\u7b80\u4f53)" }, |
| 379 | + { code: "ii", text: "\ua187\ua259" }, |
| 380 | + { code: "ko", text: "\ud55c\uad6d\uc5b4" }, |
| 381 | + { code: "ko-kp", text: "\ud55c\uad6d\uc5b4 (\uc870\uc120)" }, |
| 382 | + { code: "got", text: "\ud800\udf32\ud800\udf3f\ud800\udf44\ud800\udf39\ud800\udf43\ud800\udf3a" }, |
| 383 | + ], |
| 384 | + |
| 385 | + /** |
| 386 | + * cache some useful objects |
| 387 | + * 1) mostly ready-to-go language HTML menu. When/if we upgrade, make it a jQuery combobox |
| 388 | + * 2) dict of language code to name -- useful for testing for existence, maybe other things. |
| 389 | + */ |
| 390 | + initialize: function() { |
| 391 | + if ( mw.LanguageUpWiz.initialized ) { |
| 392 | + return; |
| 393 | + } |
| 394 | + mw.LanguageUpWiz._codes = {}; |
| 395 | + var select = $j( '<select/>' ); |
| 396 | + $j.each( mw.LanguageUpWiz.languages, function( i, language ) { |
| 397 | + select.append( |
| 398 | + $j( '<option>' ) |
| 399 | + .attr( 'value', language.code ) |
| 400 | + .append( language.text ) |
| 401 | + ); |
| 402 | + mw.LanguageUpWiz._codes[language.code] = language.text; |
| 403 | + } ); |
| 404 | + mw.LanguageUpWiz.$_select = select; |
| 405 | + mw.LanguageUpWiz.initialized = true; |
| 406 | + }, |
| 407 | + |
| 408 | + /** |
| 409 | + * Get an HTML select menu of all our languages. |
| 410 | + * @param name desired name of select element |
| 411 | + * @param code desired default language code |
| 412 | + * @return HTML select element configured as desired |
| 413 | + */ |
| 414 | + getMenu: function( name, code ) { |
| 415 | + mw.LanguageUpWiz.initialize(); |
| 416 | + var $select = mw.LanguageUpWiz.$_select.clone(); |
| 417 | + $select.attr( 'name', name ); |
| 418 | + if ( code === mw.LanguageUpWiz.UNKNOWN ) { |
| 419 | + // n.b. MediaWiki LanguageHandler has ability to add custom label for 'Unknown'; possibly as pseudo-label |
| 420 | + $select.prepend( $j( '<option>' ).attr( 'value', mw.LanguageUpWiz.UNKNOWN ).append( gM( 'mwe-code-unknown' )) ); |
| 421 | + $select.val( mw.LanguageUpWiz.UNKNOWN ); |
| 422 | + } else if ( code !== undefined ) { |
| 423 | + $select.val( mw.LanguageUpWiz.getClosest( code )); |
| 424 | + } |
| 425 | + return $select.get( 0 ); |
| 426 | + }, |
| 427 | + |
| 428 | + /** |
| 429 | + * Figure out the closest language we have to a supplied language code. |
| 430 | + * It seems that people on Mediawiki set their language code as freetext, and it could be anything, even |
| 431 | + * variants we don't have a record for, or ones that are not in any ISO standard. |
| 432 | + * |
| 433 | + * Logic copied from MediaWiki:LanguageHandler.js |
| 434 | + * handle null cases, special cases for some Chinese variants |
| 435 | + * Otherwise, if handed "foo-bar-baz" language, try to match most specific language, |
| 436 | + * "foo-bar-baz", then "foo-bar", then "foo" |
| 437 | + * |
| 438 | + * @param code A string representing a language code, which we may or may not have. |
| 439 | + * Expected to be separated with dashes as codes from ISO 639, e.g. "zh-tw" for Chinese ( Traditional ) |
| 440 | + * @return a language code which is close to the supplied parameter, or fall back to mw.LanguageUpWiz.defaultCode |
| 441 | + */ |
| 442 | + getClosest: function( code ) { |
| 443 | + mw.LanguageUpWiz.initialize(); |
| 444 | + if ( typeof ( code ) != 'string' || code === null || code.length === 0 ) { |
| 445 | + return mw.LanguageUpWiz.defaultCode; |
| 446 | + } |
| 447 | + if ( code == 'nan' || code == 'minnan' ) { |
| 448 | + return 'zh-min-nan'; |
| 449 | + } else if ( mw.LanguageUpWiz._codes[code] !== undefined ) { |
| 450 | + return code; |
| 451 | + } |
| 452 | + return mw.LanguageUpWiz.getClosest( code.substring( 0, code.indexOf( '-' )) ); |
| 453 | + }, |
| 454 | + |
| 455 | + |
| 456 | + // enhance a simple text input to be an autocompleting language menu |
| 457 | + // this will work when/if we move to jQuery 1.4. As of now the autocomplete is too underpowered for our needs without |
| 458 | + // serious hackery |
| 459 | + /* |
| 460 | + $j.fn.languageMenu = function( options ) { |
| 461 | + var _this = this; |
| 462 | + _this.autocomplete( null, { |
| 463 | + minChars: 0, |
| 464 | + width: 310, |
| 465 | + selectFirst: true, |
| 466 | + autoFill: true, |
| 467 | + mustMatch: true, |
| 468 | + matchContains: false, |
| 469 | + highlightItem: true, |
| 470 | + scroll: true, |
| 471 | + scrollHeight: 220, |
| 472 | + formatItem: function( row, i, max, term ) { |
| 473 | + return row.code + " " + row.code; |
| 474 | + }, |
| 475 | + formatMatch: function( row, i, max, term ) { |
| 476 | + return row.code + " " + row.code; |
| 477 | + }, |
| 478 | + formatResult: function( row ) { |
| 479 | + return row.code; |
| 480 | + } |
| 481 | + }, mw.Languages ); |
| 482 | + |
| 483 | + // and add a dropdown so we can see the thingy, too |
| 484 | + return _this; |
| 485 | + }; |
| 486 | + */ |
| 487 | + |
| 488 | + // XXX the concept of "internal language" exists in UploadForm.js -- seems to be how they handled i18n, with |
| 489 | + // language codes that has underscores rather than dashes, ( "en_gb" rather than the correct "en-gb" ). |
| 490 | + // although other info such as Information boxes was recorded correctly. |
| 491 | + // This is presumed not to apply to the shiny new world of JS2, where i18n is handled in other ways. |
| 492 | + |
| 493 | +} |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.LanguageUpWiz.js |
___________________________________________________________________ |
Name: svn:mergeinfo |
1 | 494 | + /branches/REL1_15/phase3/js2/mwEmbed/modules/UploadWizard/mw.Language.js:51646 |
/branches/sqlite/js2/mwEmbed/modules/UploadWizard/mw.Language.js:58211-58321 |
Name: svn:eol-style |
2 | 495 | + native |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/loader.js |
— | — | @@ -0,0 +1,105 @@ |
| 2 | +/* |
| 3 | +* Loader for UploadWizard module: |
| 4 | +*/ |
| 5 | + |
| 6 | +// Scope everythign in "mw" ( keeps the global namespace clean ) |
| 7 | +( function( mw ) { |
| 8 | + |
| 9 | + mw.addMessages( { |
| 10 | + "mwe-loading-upwiz" : "Loading upload wizard" |
| 11 | + }); |
| 12 | + |
| 13 | + // Add class file paths ( From ROOT ) |
| 14 | + mw.addClassFilePaths( { |
| 15 | + "mw.LanguageUpWiz" : "mw.LanguageUpWiz.js", |
| 16 | + "mw.UploadWizard" : "mw.UploadWizard.js", |
| 17 | + "mw.style.uploadWizard" : "css/uploadWizard.css", |
| 18 | + |
| 19 | + "mw.UploadApiProcessor" : "mw.UploadApiProcessor.js", |
| 20 | + "mw.IframeTransport" : "mw.IframeTransport.js", |
| 21 | + "mw.ApiUploadHandler" : "mw.ApiUploadHandler.js", |
| 22 | + "mw.DestinationChecker" : "mw.DestinationChecker.js", |
| 23 | + |
| 24 | + "mw.MockUploadHandler" : "mw.MockUploadHandler.js" |
| 25 | + |
| 26 | + }); |
| 27 | + |
| 28 | + //Set a variable for the base upload interface for easy inclution |
| 29 | + // |
| 30 | + // var baseUploadlibraries = [ |
| 31 | + // [ |
| 32 | + // 'mw.UploadHandler', |
| 33 | + // 'mw.UploadInterface', |
| 34 | + // '$j.ui' |
| 35 | + // ], |
| 36 | + // [ |
| 37 | + // '$j.ui.progressbar', |
| 38 | + // '$j.ui.dialog', |
| 39 | + // '$j.ui.draggable', |
| 40 | + // '$j.fn.autocomplete' |
| 41 | + // ] |
| 42 | + // ]; |
| 43 | + // |
| 44 | + // var mwBaseFirefoggReq = baseUploadlibraries.slice( 0 ) |
| 45 | + // mwBaseFirefoggReq[0].push('mw.Firefogg'); |
| 46 | + // |
| 47 | + |
| 48 | + var libraries = [ |
| 49 | + [ |
| 50 | + '$j.ui', |
| 51 | + '$j.ui.progressbar', |
| 52 | + '$j.ui.dialog', |
| 53 | + '$j.ui.draggable', |
| 54 | + '$j.ui.datepicker', |
| 55 | + '$j.effects.core', |
| 56 | + '$j.effects.slide', |
| 57 | + //'$j.effects.pulsate', |
| 58 | + '$j.fn.autocomplete', |
| 59 | + '$j.fn.tipsy', |
| 60 | + 'mw.style.tipsy', |
| 61 | + 'mw.style.autocomplete' |
| 62 | + ], |
| 63 | + [ |
| 64 | + 'mw.LanguageUpWiz', |
| 65 | + 'mw.IframeTransport', |
| 66 | + 'mw.ApiUploadHandler', |
| 67 | + 'mw.DestinationChecker', |
| 68 | + 'mw.UploadWizard', |
| 69 | + 'mw.style.uploadWizard' |
| 70 | + ], |
| 71 | + ]; |
| 72 | + |
| 73 | + var testLibraries = libraries.slice( 0 ) |
| 74 | + testLibraries.push( [ 'mw.MockUploadHandler' ] ); |
| 75 | + |
| 76 | + /** |
| 77 | + * Note: We should move relevant parts of these style sheets to the addMedia/css folder |
| 78 | + * phase 2: We should separate out sheet sets per sub-module: |
| 79 | + */ |
| 80 | + |
| 81 | + mw.addModuleLoader( 'UploadWizard.UploadWizard', function( callback ) { |
| 82 | + //Clone the array: |
| 83 | + //var request = mwBaseFirefoggReq.slice( 0 ) ; |
| 84 | + |
| 85 | + //Add uploadwizard classes to a new "request" var: |
| 86 | + //request.push( libraries ); |
| 87 | + mw.load( libraries, function() { |
| 88 | + callback( 'UploadWizard.UploadWizard' ); |
| 89 | + } ); |
| 90 | + |
| 91 | + } ); |
| 92 | + |
| 93 | + mw.addModuleLoader( 'UploadWizard.UploadWizardTest', function( callback ) { |
| 94 | + //Clone the array: |
| 95 | + //var request = mwBaseFirefoggReq.slice( 0 ) ; |
| 96 | + |
| 97 | + //Add uploadwizard classes to a new "request" var: |
| 98 | + //request.push( testLibraries ); |
| 99 | + //debugger; |
| 100 | + mw.load( testLibraries, function() { |
| 101 | + callback( 'UploadWizard.UploadWizardTest' ); |
| 102 | + } ); |
| 103 | + |
| 104 | + } ); |
| 105 | + |
| 106 | +} )( window.mw ); |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/loader.js |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 107 | + native |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.UploadWizard.js |
— | — | @@ -0,0 +1,3170 @@ |
| 2 | +mw.addMessages( { |
| 3 | + "mwe-upwiz-step-file": "1. Upload your files", |
| 4 | + "mwe-upwiz-step-deeds": "2. Add licenses", |
| 5 | + "mwe-upwiz-step-details": "3. Add descriptions", |
| 6 | + "mwe-upwiz-step-thanks": "4. Use your files", |
| 7 | + "mwe-upwiz-intro": "Welcome to Wikimedia Commons, a repository of images, sounds, and movies that anyone can freely download and use. Add to humanity's knowledge by uploading files that could be used for an educational purpose.", |
| 8 | + |
| 9 | + "mwe-upwiz-add-file-n": "Add another file", |
| 10 | + "mwe-upwiz-add-file-0": "Click here to add a file for upload", |
| 11 | + "mwe-upwiz-browse": "Browse...", |
| 12 | + "mwe-upwiz-transported": "OK", |
| 13 | + "mwe-upwiz-click-here": "Click here to select a file", |
| 14 | + "mwe-upwiz-uploading": "uploading...", |
| 15 | + "mwe-upwiz-editing": "editing...", |
| 16 | + "mwe-upwiz-remove-upload": "Remove this file from the list of files to upload", |
| 17 | + "mwe-upwiz-remove-description": "Remove this description", |
| 18 | + "mwe-upwiz-upload": "Upload", |
| 19 | + "mwe-upwiz-upload-count": "$1 of $2 files uploaded", |
| 20 | + "mwe-upwiz-progressbar-uploading": "uploading", |
| 21 | + "mwe-upwiz-remaining": "$1 remaining", |
| 22 | + "mwe-upwiz-deeds-intro": "Thank you! Now we need to set a license for these files, so everyone can legally view or modify them. First, we'll have to know where you got them.", |
| 23 | + "mwe-upwiz-details-intro": "Now we need some basic information about the files.", |
| 24 | + "mwe-upwiz-source-ownwork": "This file is my own work.", |
| 25 | + "mwe-upwiz-source-ownwork-plural": "These files are my own work.", |
| 26 | + "mwe-upwiz-source-ownwork-assert": "I, $1, the copyright holder of this work, hereby grant anyone the right to use this work for any purpose, as long as they credit me and share derivative work under the same terms.", |
| 27 | + "mwe-upwiz-source-ownwork-assert-plural": "I, $1, the copyright holder of these works, hereby grant anyone the right to use these works for any purpose, as long as they credit me and share derivative work under the same terms.", |
| 28 | + "mwe-upwiz-source-ownwork-assert-custom": "I, $1, the copyright holder of this work, hereby publish this work under the following license(s):", |
| 29 | + "mwe-upwiz-source-ownwork-assert-custom-plural": "I, $1, the copyright holder of these works, hereby publish these works under the following license(s):", |
| 30 | + "mwe-upwiz-source-ownwork-assert-note": "This means you release your work under a double Creative Commons Attribution ShareAlike and GFDL license.", |
| 31 | + "mwe-upwiz-source-permission": "Their author gave you explicit permission to upload them", |
| 32 | + "mwe-upwiz-source-thirdparty": "This file is not my own work.", |
| 33 | + "mwe-upwiz-source-thirdparty-plural": "These files are not my own work.", |
| 34 | + "mwe-upwiz-source-thirdparty-intro" : "Please enter the address where you found each file.", |
| 35 | + "mwe-upwiz-source-thirdparty-custom-plural-intro" : "If all files have the same source, author, and copyright status, you may enter them only once for all of them.", |
| 36 | + "mwe-upwiz-source-thirdparty-license" : "The copyright holder of this work published them under the following license(s):", |
| 37 | + "mwe-upwiz-source-thirdparty-license-plural" : "The copyright holder of these works published them under the following license(s):", |
| 38 | + "mwe-upwiz-source-thirdparty-accept": "OK", |
| 39 | + "mwe-upwiz-source-custom": "Did you know? You can <a href=\"$1\">customize</a> the default options you see here.", |
| 40 | + "mwe-upwiz-more-options": "more options...", |
| 41 | + "mwe-upwiz-fewer-options": "fewer options...", |
| 42 | + "mwe-upwiz-desc": "Description in", |
| 43 | + "mwe-upwiz-desc-add-n": "add a description in another language", |
| 44 | + "mwe-upwiz-desc-add-0": "add a description", |
| 45 | + "mwe-upwiz-title": "Title", |
| 46 | + "mwe-upwiz-categories-intro": "Help people find your works by adding categories", |
| 47 | + "mwe-upwiz-categories-another": "Add other categories", |
| 48 | + "mwe-upwiz-previously-uploaded": "This file was previously uploaded to $1 and is already available <a href=\"$2\">here</a>.", |
| 49 | + "mwe-upwiz-about-this-work": "About this work", |
| 50 | + "mwe-upwiz-media-type": "Media type", |
| 51 | + "mwe-upwiz-date-created": "Date created", |
| 52 | + "mwe-upwiz-location": "Location", |
| 53 | + "mwe-upwiz-copyright-info": "Copyright information", |
| 54 | + "mwe-upwiz-author": "Author(s)", |
| 55 | + "mwe-upwiz-license": "License", |
| 56 | + "mwe-upwiz-about-format": "About the file", |
| 57 | + "mwe-upwiz-autoconverted": "This file was automatically converted to the $1 format", |
| 58 | + "mwe-upwiz-filename-tag": "File name:", |
| 59 | + "mwe-upwiz-other": "Other information", |
| 60 | + "mwe-upwiz-other-prefill": "Free wikitext field", |
| 61 | + "mwe-upwiz-showall": "show all", |
| 62 | + "mwe-upwiz-source": "Source", |
| 63 | + "mwe-upwiz-macro-edit-intro": "Choose a license first above, then you can add some descriptions and other information to your uploads.", |
| 64 | + "mwe-upwiz-macro-edit": "Update descriptions", |
| 65 | + "mwe-upwiz-thanks-intro": "Thanks for uploading your works! You can now use your files on a Wikipedia article or link to them from elsewhere on the web.", |
| 66 | + "mwe-upwiz-thanks-link": "This file is now available at <b><tt>$1</tt></b>.", |
| 67 | + "mwe-upwiz-thanks-wikitext": "<b>To use it in a Wikipedia article</b>, copy this text into an article: ", |
| 68 | + "mwe-upwiz-thanks-url": "<b>To link to it in HTML</b>, copy this HTML code: ", |
| 69 | + |
| 70 | + "mwe-upwiz-upload-error-bad-filename-extension": "This wiki does not accept filenames with the extension \"$1\".", |
| 71 | + "mwe-upwiz-upload-error-duplicate": "This file was previously uploaded to this wiki.", |
| 72 | + "mwe-upwiz-upload-error-stashed-anyway": "Post anyway?", |
| 73 | + "mwe-upwiz-ok": "OK", |
| 74 | + "mwe-upwiz-cancel": "Cancel", |
| 75 | + "mwe-upwiz-change": "(change)", |
| 76 | + |
| 77 | + "mwe-fileexists" : "A file with this name exists already. Please check <b><tt>$1<\/tt><\/b> if you are not sure if you want to replace it.", |
| 78 | + "mwe-thumbnail-more" : "Enlarge", |
| 79 | + "mwe-upwiz-overwrite" : "Replace the file", |
| 80 | + |
| 81 | + "mwe-copyright-macro": "As above", |
| 82 | + "mwe-copyright-custom": "Custom", |
| 83 | + |
| 84 | + "mwe-upwiz-next": "Next", |
| 85 | + "mwe-upwiz-home": "Go to Wiki home page", |
| 86 | + "mwe-upwiz-upload-another": "Upload more files", |
| 87 | + |
| 88 | + "mwe-prevent-close": "Your files are still uploading. Are you sure you want to navigate away from this page?", |
| 89 | + |
| 90 | + "mwe-upwiz-files-complete": "Your files finished uploading!", |
| 91 | + "mwe-upwiz-deeds-later": "Set deeds and licenses for each file individually on the next page", |
| 92 | + |
| 93 | + "mwe-upwiz-tooltip-author": "The name of the person who took the photo, or painted the picture, drew the drawing, etc.", |
| 94 | + "mwe-upwiz-tooltip-source": "Where this digital file came from -- could be a URL, or a book or publication", |
| 95 | + "mwe-upwiz-tooltip-sign": "You can use your wiki User name or your real name. In both cases, this will be linked to your wiki User page", |
| 96 | + "mwe-upwiz-tooltip-title": "A short title for the image. You may use plain language with spaces, but no line breaks. This title must be unlike all other titles in this wiki.", |
| 97 | + "mwe-upwiz-tooltip-description": "Briefly describe everything notable about the work. For a photo, mention the main things that are depicted, the occasion or the place.", |
| 98 | + "mwe-upwiz-tooltip-other": "Any other information you want to include about this work. You may use wikitext code." |
| 99 | + |
| 100 | +} ); |
| 101 | + |
| 102 | + |
| 103 | +/** |
| 104 | + * |
| 105 | + */ |
| 106 | +mw.UploadWizardDeed = function() { |
| 107 | + var _this = this; |
| 108 | + // prevent from instantiating directly? |
| 109 | +}; |
| 110 | + |
| 111 | +/* sort of an abstract class */ |
| 112 | +mw.UploadWizardDeed.prototype = { |
| 113 | + isReady: function() { |
| 114 | + return false; |
| 115 | + }, |
| 116 | + |
| 117 | + getSourceWikiText: function() { |
| 118 | + return $j( this.sourceInput ).val(); |
| 119 | + }, |
| 120 | + |
| 121 | + getAuthorWikiText: function() { |
| 122 | + return $j( this.authorInput ).val(); |
| 123 | + }, |
| 124 | + |
| 125 | + /** |
| 126 | + * Get wikitext representing the licenses selected in the license object |
| 127 | + * @return wikitext of all applicable license templates. |
| 128 | + */ |
| 129 | + getLicenseWikiText: function() { |
| 130 | + var _this = this; |
| 131 | + var wikiText = ''; |
| 132 | + $j.each ( _this.licenseInput.getTemplates(), function( i, template ) { |
| 133 | + wikiText += "{{" + template + "}}\n"; |
| 134 | + } ); |
| 135 | + |
| 136 | + return wikiText; |
| 137 | + } |
| 138 | + |
| 139 | +}; |
| 140 | + |
| 141 | +mw.ProgressBar = function( selector, text ) { |
| 142 | + var _this = this; |
| 143 | + // XXX need to figure out a way to put text inside bar |
| 144 | + |
| 145 | + _this.progressBarDiv = $j('<div></div>') |
| 146 | + .addClass("mwe-upwiz-progress-bar") |
| 147 | + .progressbar( { value: 0 } ); |
| 148 | + |
| 149 | + _this.timeRemainingDiv = $j('<div></div>').addClass("mwe-upwiz-etr"); |
| 150 | + |
| 151 | + _this.countDiv = $j('<div></div>').addClass("mwe-upwiz-count"); |
| 152 | + |
| 153 | + _this.beginTime = undefined; |
| 154 | + |
| 155 | + $j( selector ).html( |
| 156 | + $j('<div />').addClass( 'mwe-upwiz-progress' ) |
| 157 | + .append( $j( '<div></div>' ) |
| 158 | + .addClass( 'mwe-upwiz-progress-bar-etr' ) |
| 159 | + .append( _this.progressBarDiv ) |
| 160 | + .append( _this.timeRemainingDiv ) ) |
| 161 | + .append( $j( _this.countDiv ) ) |
| 162 | + ); |
| 163 | + |
| 164 | +}; |
| 165 | + |
| 166 | +mw.ProgressBar.prototype = { |
| 167 | + |
| 168 | + /** |
| 169 | + * sets the beginning time (useful for figuring out estimated time remaining) |
| 170 | + * if time parameter omitted, will set beginning time to now |
| 171 | + * |
| 172 | + * @param time optional; the time this bar is presumed to have started (epoch milliseconds) |
| 173 | + */ |
| 174 | + setBeginTime: function( time ) { |
| 175 | + var _this = this; |
| 176 | + _this.beginTime = time ? time : ( new Date() ).getTime(); |
| 177 | + }, |
| 178 | + |
| 179 | + /** |
| 180 | + * sets the total number of things we are tracking |
| 181 | + * @param total an integer, for display e.g. uploaded 1 of 5, this is the 5 |
| 182 | + */ |
| 183 | + setTotal: function(total) { |
| 184 | + var _this = this; |
| 185 | + _this.total = total; |
| 186 | + }, |
| 187 | + |
| 188 | + /** |
| 189 | + * Show overall progress for the entire UploadWizard |
| 190 | + * The current design doesn't have individual progress bars, just one giant one. |
| 191 | + * We did some tricky calculations in startUploads to try to weight each individual file's progress against |
| 192 | + * the overall progress. |
| 193 | + * @param fraction the amount of whatever it is that's done whatever it's done |
| 194 | + */ |
| 195 | + showProgress: function( fraction ) { |
| 196 | + var _this = this; |
| 197 | + |
| 198 | + _this.progressBarDiv.progressbar( 'value', parseInt( fraction * 100, 10 ) ); |
| 199 | + |
| 200 | + var remainingTime; |
| 201 | + if (_this.beginTime === null) { |
| 202 | + remainingTime = 0; |
| 203 | + } else { |
| 204 | + remainingTime = _this.getRemainingTime( fraction ); |
| 205 | + } |
| 206 | + |
| 207 | + if ( remainingTime !== null ) { |
| 208 | + _this.timeRemainingDiv |
| 209 | + .html( gM( 'mwe-upwiz-remaining', mw.seconds2npt(parseInt(remainingTime / 1000), 10) ) ); |
| 210 | + } |
| 211 | + }, |
| 212 | + |
| 213 | + /** |
| 214 | + * Calculate remaining time for all uploads to complete. |
| 215 | + * |
| 216 | + * @param fraction fraction of progress to show |
| 217 | + * @return estimated time remaining (in milliseconds) |
| 218 | + */ |
| 219 | + getRemainingTime: function ( fraction ) { |
| 220 | + var _this = this; |
| 221 | + if ( _this.beginTime ) { |
| 222 | + var elapsedTime = ( new Date() ).getTime() - _this.beginTime; |
| 223 | + if ( fraction > 0.0 && elapsedTime > 0 ) { // or some other minimums for good data |
| 224 | + var rate = fraction / elapsedTime; |
| 225 | + return parseInt( ( 1.0 - fraction ) / rate, 10 ); |
| 226 | + } |
| 227 | + } |
| 228 | + return null; |
| 229 | + }, |
| 230 | + |
| 231 | + |
| 232 | + /** |
| 233 | + * Show the overall count as we upload |
| 234 | + * @param count -- the number of items that have done whatever has been done e.g. in "uploaded 2 of 5", this is the 2 |
| 235 | + */ |
| 236 | + showCount: function( count ) { |
| 237 | + var _this = this; |
| 238 | + _this.countDiv.html( gM( 'mwe-upwiz-upload-count', [ count, _this.total ] ) ); |
| 239 | + } |
| 240 | + |
| 241 | + |
| 242 | +}; |
| 243 | + |
| 244 | + |
| 245 | + |
| 246 | +//mw.setConfig('uploadHandlerClass', mw.MockUploadHandler); // ApiUploadHandler? |
| 247 | + |
| 248 | +// available licenses should be a configuration of the MediaWiki instance, |
| 249 | +// not hardcoded here. |
| 250 | +// but, MediaWiki has no real concept of a License as a first class object -- there are templates and then specially - parsed |
| 251 | +// texts to create menus -- hack on top of hacks -- a bit too much to deal with ATM |
| 252 | +/** |
| 253 | + * Create a group of checkboxes for licenses |
| 254 | + * @param div |
| 255 | + * @param values (optional) array of license key names to activate by default |
| 256 | + * @param change (optional) function to execute when any value changes |
| 257 | + */ |
| 258 | +mw.UploadWizardLicenseInput = function( div, values ) { |
| 259 | + var _this = this; |
| 260 | + |
| 261 | + _this.change = function() {}; |
| 262 | + |
| 263 | + var c = mw.UploadWizardLicenseInput.prototype.count++; |
| 264 | + |
| 265 | + // XXX get these for real |
| 266 | + _this.licenses = { |
| 267 | + pd: { template: 'pd', text: 'Public Domain' }, |
| 268 | + cc0: { template: 'cc0', text: 'Creative Commons Zero waiver' }, |
| 269 | + cc_by_30: { template: 'cc-by-30', text: 'Creative Commons Attribution 3.0' }, |
| 270 | + cc_by_sa_30: { template: 'cc-by-sa-30', text: 'Creative Commons Attribution ShareAlike 3.0' }, |
| 271 | + gfdl: { template: 'gfdl', text: 'GFDL (GNU Free Documentation License)' } |
| 272 | + }; |
| 273 | + |
| 274 | + _this.inputs = []; |
| 275 | + |
| 276 | + $div = $j( div ); |
| 277 | + $j.each( _this.licenses, function( key, data ) { |
| 278 | + var id = 'license_' + key + '_' + c; |
| 279 | + var input = $j( '<input />' ) |
| 280 | + .attr( { id: id, type: 'checkbox', value: key } ) |
| 281 | + .click( function() { _this.change() } ); |
| 282 | + data.input = input.get(0); |
| 283 | + $div.append( |
| 284 | + data.input, |
| 285 | + $j( '<label />' ).attr( { 'for': id } ).html( data.text ), |
| 286 | + $j( '<br/>' ) |
| 287 | + ); |
| 288 | + } ); |
| 289 | + |
| 290 | + if ( values ) { |
| 291 | + _this.setValues( values ); |
| 292 | + } |
| 293 | +}; |
| 294 | + |
| 295 | +mw.UploadWizardLicenseInput.prototype = { |
| 296 | + count: 0, |
| 297 | + |
| 298 | + /** |
| 299 | + * Sets the value(s) of a license input. |
| 300 | + * @param object of license-key to boolean values, e.g. { cc_by_sa_30: true, gfdl: true } |
| 301 | + */ |
| 302 | + setValues: function( licenseValues ) { |
| 303 | + var _this = this; |
| 304 | + $j.each( _this.licenses, function( key, data ) { |
| 305 | + var checked = ~~!!licenseValues[key]; |
| 306 | + $j( _this.licenses[key].input ).attr( { 'checked' : checked } ); |
| 307 | + } ); |
| 308 | + _this.change(); |
| 309 | + }, |
| 310 | + |
| 311 | + /** |
| 312 | + * Set the default configured licenses - should change per wiki |
| 313 | + */ |
| 314 | + setDefaultValues: function() { |
| 315 | + var _this = this; |
| 316 | + var values = {}; |
| 317 | + $j.each( mw.getConfig('defaultLicenses'), function( i, license ) { |
| 318 | + values[license] = true; |
| 319 | + } ); |
| 320 | + _this.setValues( values ); |
| 321 | + }, |
| 322 | + |
| 323 | + /** |
| 324 | + * Gets which values are set to true |
| 325 | + * @return object of object of license-key to boolean values, e.g. { cc_by_sa_30: true, gfdl: true } |
| 326 | + */ |
| 327 | + getValues: function() { |
| 328 | + var _this = this; |
| 329 | + var values = {}; |
| 330 | + $j.each( _this.licenses, function( key, data ) { |
| 331 | + if ( $j( _this.licenses[key].input ).is( ':checked' ) ) { |
| 332 | + values[key] = data; |
| 333 | + } |
| 334 | + } ); |
| 335 | + return values; |
| 336 | + }, |
| 337 | + |
| 338 | + /** |
| 339 | + * Gets the templates associated with values set to true |
| 340 | + * @return templates of licenses selected |
| 341 | + */ |
| 342 | + getTemplates: function() { |
| 343 | + var _this = this; |
| 344 | + var templates = []; |
| 345 | + $j.each( _this.getValues(), function( key, data ) { |
| 346 | + templates.push( data.template ); |
| 347 | + } ); |
| 348 | + return templates; |
| 349 | + }, |
| 350 | + |
| 351 | + /** |
| 352 | + * Returns true if any license is set |
| 353 | + * @return boolean |
| 354 | + */ |
| 355 | + isSet: function() { |
| 356 | + var _this = this; |
| 357 | + return ( _this.getValues().length !== 0 ); |
| 358 | + } |
| 359 | + |
| 360 | +}; |
| 361 | + |
| 362 | + |
| 363 | +/** |
| 364 | + * Represents the upload -- in its local and remote state. (Possibly those could be separate objects too...) |
| 365 | + * This is our 'model' object if we are thinking MVC. Needs to be better factored, lots of feature envy with the UploadWizard |
| 366 | + * states: |
| 367 | + * 'new' 'transporting' 'transported' 'details' 'submitting-details' 'complete' |
| 368 | + * should fork this into two -- local and remote, e.g. filename |
| 369 | + */ |
| 370 | +mw.UploadWizardUpload = function( filesDiv ) { |
| 371 | + var _this = this; |
| 372 | + _this.state = 'new'; |
| 373 | + _this.transportWeight = 1; // default |
| 374 | + _this.detailsWeight = 1; // default |
| 375 | + _this._thumbnails = {}; |
| 376 | + _this.imageinfo = {}; |
| 377 | + _this.title = undefined; |
| 378 | + _this.filename = undefined; |
| 379 | + _this.originalFilename = undefined; |
| 380 | + _this.mimetype = undefined; |
| 381 | + _this.extension = undefined; |
| 382 | + |
| 383 | + // details |
| 384 | + _this.ui = new mw.UploadWizardUploadInterface( _this, filesDiv ); |
| 385 | + |
| 386 | + // handler -- usually ApiUploadHandler |
| 387 | + // _this.handler = new ( mw.getConfig( 'uploadHandlerClass' ) )( _this ); |
| 388 | + // _this.handler = new mw.MockUploadHandler( _this ); |
| 389 | + _this.handler = new mw.ApiUploadHandler( _this ); |
| 390 | +}; |
| 391 | + |
| 392 | +mw.UploadWizardUpload.prototype = { |
| 393 | + |
| 394 | + acceptDeed: function( deed ) { |
| 395 | + var _this = this; |
| 396 | + _this.deed.applyDeed( _this ); |
| 397 | + }, |
| 398 | + |
| 399 | + /** |
| 400 | + * start |
| 401 | + */ |
| 402 | + start: function() { |
| 403 | + var _this = this; |
| 404 | + _this.setTransportProgress(0.0); |
| 405 | + _this.handler.start(); |
| 406 | + _this.ui.start(); |
| 407 | + }, |
| 408 | + |
| 409 | + |
| 410 | + /** |
| 411 | + * remove |
| 412 | + */ |
| 413 | + remove: function() { |
| 414 | + var _this = this; |
| 415 | + $j( _this.ui.div ).remove(); |
| 416 | + $j( _this.details.div ).remove(); |
| 417 | + $j( _this ).trigger( 'removeUpload' ); |
| 418 | + }, |
| 419 | + |
| 420 | + /** |
| 421 | + * Wear our current progress, for observing processes to see |
| 422 | + * @param fraction |
| 423 | + */ |
| 424 | + setTransportProgress: function ( fraction ) { |
| 425 | + var _this = this; |
| 426 | + _this.state = 'transporting'; |
| 427 | + _this.transportProgress = fraction; |
| 428 | + $j( _this ).trigger( 'transportProgressEvent' ); |
| 429 | + }, |
| 430 | + |
| 431 | + /** |
| 432 | + * To be executed when an individual upload finishes. Processes the result and updates step 2's details |
| 433 | + * @param result the API result in parsed JSON form |
| 434 | + */ |
| 435 | + setTransported: function( result ) { |
| 436 | + var _this = this; |
| 437 | + _this.state = 'transported'; |
| 438 | + _this.transportProgress = 1; |
| 439 | + $j( _this ).trigger( 'transportedEvent' ); |
| 440 | + |
| 441 | + if ( result.upload && result.upload.imageinfo && result.upload.imageinfo.descriptionurl ) { |
| 442 | + // success |
| 443 | + _this.extractUploadInfo( result ); |
| 444 | + _this.deedPreview.setup(); |
| 445 | + _this.details.populate(); |
| 446 | + |
| 447 | + } else if ( result.upload && result.upload.sessionkey ) { |
| 448 | + // there was a warning - type error which prevented it from adding the result to the db |
| 449 | + if ( result.upload.warnings.duplicate ) { |
| 450 | + var duplicates = result.upload.warnings.duplicate; |
| 451 | + _this.details.errorDuplicate( result.upload.sessionkey, duplicates ); |
| 452 | + } |
| 453 | + |
| 454 | + // and other errors that result in a stash |
| 455 | + } else if ( 0 /* actual failure */ ) { |
| 456 | + // we may want to tag or otherwise queue it as an upload to retry |
| 457 | + } |
| 458 | + |
| 459 | + |
| 460 | + }, |
| 461 | + |
| 462 | + |
| 463 | + /** |
| 464 | + * call when the file is entered into the file input |
| 465 | + * get as much data as possible -- maybe exif, even thumbnail maybe |
| 466 | + */ |
| 467 | + extractLocalFileInfo: function( localFilename ) { |
| 468 | + var _this = this; |
| 469 | + if (false) { // FileAPI, one day |
| 470 | + _this.transportWeight = getFileSize(); |
| 471 | + } |
| 472 | + _this.extension = mw.UploadWizardUtil.getExtension( localFilename ); |
| 473 | + // XXX add filename, original filename, extension, whatever else is interesting. |
| 474 | + }, |
| 475 | + |
| 476 | + |
| 477 | + /** |
| 478 | + * Accept the result from a successful API upload transport, and fill our own info |
| 479 | + * |
| 480 | + * @param result The JSON object from a successful API upload result. |
| 481 | + */ |
| 482 | + extractUploadInfo: function( result ) { |
| 483 | + var _this = this; |
| 484 | + |
| 485 | + _this.filename = result.upload.filename; |
| 486 | + _this.title = mw.getConfig( 'fileNamespace' ) + ':' + _this.filename; |
| 487 | + |
| 488 | + _this.extractImageInfo( result.upload.imageinfo ); |
| 489 | + |
| 490 | + }, |
| 491 | + |
| 492 | + /** |
| 493 | + * Extract image info into our upload object |
| 494 | + * Image info is obtained from various different API methods |
| 495 | + * @param imageinfo JSON object obtained from API result. |
| 496 | + */ |
| 497 | + extractImageInfo: function( imageinfo ) { |
| 498 | + var _this = this; |
| 499 | + for ( var key in imageinfo ) { |
| 500 | + // we get metadata as list of key-val pairs; convert to object for easier lookup. Assuming that EXIF fields are unique. |
| 501 | + if ( key == 'metadata' ) { |
| 502 | + _this.imageinfo.metadata = {}; |
| 503 | + if ( imageinfo.metadata && imageinfo.metadata.length ) { |
| 504 | + $j.each( imageinfo.metadata, function( i, pair ) { |
| 505 | + if ( pair !== undefined ) { |
| 506 | + _this.imageinfo.metadata[pair['name'].toLowerCase()] = pair['value']; |
| 507 | + } |
| 508 | + } ); |
| 509 | + } |
| 510 | + } else { |
| 511 | + _this.imageinfo[key] = imageinfo[key]; |
| 512 | + } |
| 513 | + } |
| 514 | + |
| 515 | + // we should already have an extension, but if we don't... |
| 516 | + if ( _this.extension === undefined ) { |
| 517 | + var extension = mw.UploadWizardUtil.getExtension( _this.imageinfo.url ); |
| 518 | + if ( !extension ) { |
| 519 | + if ( _this.imageinfo.mimetype ) { |
| 520 | + if ( mw.UploadWizardUtil.mimetypeToExtension[ _this.imageinfo.mimetype ] ) { |
| 521 | + extension = mw.UploadWizardUtil.mimetypeToExtension[ _this.imageinfo.mimetype ]; |
| 522 | + } |
| 523 | + } |
| 524 | + } |
| 525 | + } |
| 526 | + }, |
| 527 | + |
| 528 | + /** |
| 529 | + * Supply information to create a thumbnail for this Upload. Runs async, with a callback. |
| 530 | + * It is assumed you don't call this until it's been transported. |
| 531 | + * |
| 532 | + * XXX should check if we really need this second API call or if we can get MediaWiki to make us a thumbnail URL upon upload |
| 533 | + * |
| 534 | + * @param width - desired width of thumbnail (height will scale to match) |
| 535 | + * @param callback - callback to execute once thumbnail has been obtained -- must accept object with properties of width, height, and url. |
| 536 | + */ |
| 537 | + getThumbnail: function( width, callback ) { |
| 538 | + var _this = this; |
| 539 | + if ( _this._thumbnails[ "width" + width ] !== undefined ) { |
| 540 | + callback( _this._thumbnails[ "width" + width ] ); |
| 541 | + return; |
| 542 | + } |
| 543 | + |
| 544 | + var apiUrl = mw.getLocalApiUrl(); |
| 545 | + |
| 546 | + var params = { |
| 547 | + 'titles': _this.title, |
| 548 | + 'prop': 'imageinfo', |
| 549 | + 'iiurlwidth': width, |
| 550 | + 'iiprop': 'url' |
| 551 | + }; |
| 552 | + |
| 553 | + mw.getJSON( apiUrl, params, function( data ) { |
| 554 | + if ( !data || !data.query || !data.query.pages ) { |
| 555 | + mw.log(" No data? "); |
| 556 | + // XXX do something about the thumbnail spinner, maybe call the callback with a broken image. |
| 557 | + return; |
| 558 | + } |
| 559 | + |
| 560 | + if ( data.query.pages[-1] ) { |
| 561 | + // XXX do something about the thumbnail spinner, maybe call the callback with a broken image. |
| 562 | + return; |
| 563 | + } |
| 564 | + for ( var page_id in data.query.pages ) { |
| 565 | + var page = data.query.pages[ page_id ]; |
| 566 | + if ( ! page.imageinfo ) { |
| 567 | + // not found? error |
| 568 | + } else { |
| 569 | + var imageInfo = page.imageinfo[0]; |
| 570 | + var thumbnail = { |
| 571 | + width: imageInfo.thumbwidth, |
| 572 | + height: imageInfo.thumbheight, |
| 573 | + url: imageInfo.thumburl |
| 574 | + }; |
| 575 | + _this._thumbnails[ "width" + width ] = thumbnail; |
| 576 | + callback( thumbnail ); |
| 577 | + } |
| 578 | + } |
| 579 | + } ); |
| 580 | + |
| 581 | + }, |
| 582 | + |
| 583 | + |
| 584 | + /** |
| 585 | + * look up thumbnail info and set it in HTML, with loading spinner |
| 586 | + * it might be interesting to make this more of a publish/subscribe thing, since we have to do this 3x |
| 587 | + * the callbacks may pile up, getting unnecessary info |
| 588 | + * |
| 589 | + * @param selector |
| 590 | + * @param width |
| 591 | + */ |
| 592 | + setThumbnail: function( selector, width, imgClass ) { |
| 593 | + var _this = this; |
| 594 | + if ( typeof width === 'undefined' || width === null || width <= 0 ) { |
| 595 | + width = mw.getConfig( 'thumbnailWidth' ); |
| 596 | + } |
| 597 | + width = parseInt( width, 10 ); |
| 598 | + |
| 599 | + if ( typeof imgClass === 'undefined' || imgClass === null ) { |
| 600 | + imgClass = 'mwe-upwiz-thumbnail'; |
| 601 | + } |
| 602 | + |
| 603 | + var callback = function( thumbnail ) { |
| 604 | + // side effect: will replace thumbnail's loadingSpinner |
| 605 | + $j( selector ).html( |
| 606 | + $j('<a/>') |
| 607 | + .attr( { 'href': _this.imageinfo.descriptionurl, |
| 608 | + 'target' : '_new' } ) |
| 609 | + .append( |
| 610 | + $j( '<img/>' ) |
| 611 | + .addClass( imgClass ) |
| 612 | + .attr( 'width', thumbnail.width ) |
| 613 | + .attr( 'height', thumbnail.height ) |
| 614 | + .attr( 'src', thumbnail.url ) ) ); |
| 615 | + }; |
| 616 | + |
| 617 | + $j( selector ).loadingSpinner(); |
| 618 | + _this.getThumbnail( width, callback ); |
| 619 | + } |
| 620 | + |
| 621 | + |
| 622 | + |
| 623 | +}; |
| 624 | + |
| 625 | +/** |
| 626 | + * Create an interface fragment corresponding to a file input, suitable for Upload Wizard. |
| 627 | + * @param upload |
| 628 | + * @param div to insert file interface |
| 629 | + * @param addInterface interface to add a new one (assumed that we start out there) |
| 630 | + */ |
| 631 | +mw.UploadWizardUploadInterface = function( upload, filesDiv ) { |
| 632 | + var _this = this; |
| 633 | + |
| 634 | + _this.upload = upload; |
| 635 | + |
| 636 | + // may need to collaborate with the particular upload type sometimes |
| 637 | + // for the interface, as well as the uploadwizard. OY. |
| 638 | + _this.div = $j('<div class="mwe-upwiz-file"></div>').get(0); |
| 639 | + _this.isFilled = false; |
| 640 | + |
| 641 | + _this.fileInputCtrl = $j('<input size="1" class="mwe-upwiz-file-input" name="file" type="file"/>') |
| 642 | + .change( function() { _this.fileChanged(); } ) |
| 643 | + .get(0); |
| 644 | + |
| 645 | + |
| 646 | + // XXX better class for helper, we probably have a standard already |
| 647 | + _this.visibleFilename = $j('<div class="mwe-upwiz-visible-file"></div>').hide(); |
| 648 | + |
| 649 | + // XXX not sure if we will have a filename here -- we may want to autogenerate a "stashed" filename, |
| 650 | + // with this flow |
| 651 | + _this.filenameCtrl = $j('<input type="hidden" name="filename" value=""/>').get(0); |
| 652 | + |
| 653 | + // this file Ctrl container is placed over other interface elements, intercepts clicks and gives them to the file input control. |
| 654 | + // however, we want to pass hover events to interface elements that we are over, hence the bindings. |
| 655 | + // n.b. not using toggleClass because it often gets this event wrong -- relies on previous state to know what to do |
| 656 | + _this.fileCtrlContainer = $j('<div class="mwe-upwiz-file-ctrl-container">') |
| 657 | + .bind( 'mouseenter', function(e) { _this.addFileCtrlHover(e); } ) |
| 658 | + .bind( 'mouseleave', function(e) { _this.removeFileCtrlHover(e); } ); |
| 659 | + |
| 660 | + |
| 661 | + // the css trickery (along with css) |
| 662 | + // here creates a giant size file input control which is contained within a div and then |
| 663 | + // clipped for overflow. The effect is that we have a div (ctrl-container) we can position anywhere |
| 664 | + // which works as a file input. It will be set to opacity:0 and then we can do whatever we want with |
| 665 | + // interface "below". |
| 666 | + // XXX caution -- if the add file input changes size we won't match, unless we add some sort of event to catch this. |
| 667 | + _this.form = $j('<form class="mwe-upwiz-form"></form>') |
| 668 | + .append( _this.visibleFilename ) |
| 669 | + .append( _this.fileCtrlContainer |
| 670 | + .append( _this.fileInputCtrl ) |
| 671 | + ) |
| 672 | + .append( _this.filenameCtrl ).get( 0 ); |
| 673 | + |
| 674 | + _this.progressMessage = $j('<span class="mwe-upwiz-status-message mwe-upwiz-file-indicator" style="display: none"></span>').get(0); |
| 675 | + |
| 676 | + |
| 677 | + _this.errorDiv = $j('<div class="mwe-upwiz-upload-error mwe-upwiz-file-indicator" style="display: none;"></div>').get(0); |
| 678 | + |
| 679 | + _this.removeCtrl = $j( '<div class="mwe-upwiz-file-indicator"><a title="' |
| 680 | + + gM( 'mwe-upwiz-remove-upload' ) |
| 681 | + + '" href="#" class="mwe-upwiz-remove">x</a></div>' ) |
| 682 | + .click( function() { _this.upload.remove(); } ) |
| 683 | + .hide() |
| 684 | + .get( 0 ); |
| 685 | + |
| 686 | + |
| 687 | + $j( _this.div ).append( _this.form ) |
| 688 | + .append( _this.progressMessage ) |
| 689 | + .append( _this.errorDiv ) |
| 690 | + .append( _this.removeCtrl ); |
| 691 | + |
| 692 | + // XXX evil hardcoded |
| 693 | + // we don't really need filesdiv if we do it this way? |
| 694 | + $j( _this.div ).insertBefore( '#mwe-upwiz-upload-ctrls' ); // append( _this.div ); |
| 695 | + |
| 696 | + // _this.progressBar = ( no progress bar for individual uploads yet ) |
| 697 | + // add a details thing to details |
| 698 | + // this should bind only to the FIRST transportProgress |
| 699 | + $j( upload ).bind( 'transportProgressEvent', function(e) { _this.showTransportProgress(); e.stopPropagation(); } ); |
| 700 | + $j( upload ).bind( 'transportedEvent', function(e) { _this.showTransported(); e.stopPropagation(); } ); |
| 701 | + |
| 702 | +}; |
| 703 | + |
| 704 | + |
| 705 | +mw.UploadWizardUploadInterface.prototype = { |
| 706 | + /** |
| 707 | + * Things to do to this interface once we start uploading |
| 708 | + */ |
| 709 | + start: function() { |
| 710 | + var _this = this; |
| 711 | + $j( _this.removeCtrl ).hide(); |
| 712 | + }, |
| 713 | + |
| 714 | + /** |
| 715 | + * Make this interface look "busy" (i.e. spinner) without indicating a particular percentage of file uploaded. |
| 716 | + * Will be useful for encoding phase of Firefogg, for example. |
| 717 | + */ |
| 718 | + busy: function() { |
| 719 | + var _this = this; |
| 720 | + // for now we implement this as looking like "100% progress" |
| 721 | + // e.g. an animated bar that takes up all the space |
| 722 | + _this.showTransportProgress( 1.0 ); |
| 723 | + }, |
| 724 | + |
| 725 | + /** |
| 726 | + * Show progress by a fraction |
| 727 | + * @param fraction The fraction of progress. Float between 0 and 1 |
| 728 | + */ |
| 729 | + showTransportProgress: function() { |
| 730 | + var _this = this; |
| 731 | + $j( _this.progressMessage ).addClass('mwe-upwiz-status-progress') |
| 732 | + .html(gM( 'mwe-upwiz-uploading' )) |
| 733 | + .show(); |
| 734 | + // since, in this iteration of the interface, we never need to know |
| 735 | + // about progress again, let's unbind |
| 736 | +/* |
| 737 | + // unbind is broken in jquery 1.4.1 -- raises exception but it still works |
| 738 | + try { |
| 739 | + $j( _this.upload ).unbind( 'transportProgressEvent' ); |
| 740 | + } catch (ex) { } |
| 741 | +*/ |
| 742 | + // update individual progress bar with fraction? |
| 743 | + }, |
| 744 | + |
| 745 | + /** |
| 746 | + * Execute when this upload is transported; cleans up interface. |
| 747 | + * @param result AJAx result object |
| 748 | + */ |
| 749 | + showTransported: function() { |
| 750 | + var _this = this; |
| 751 | + $j( _this.progressMessage ).removeClass( 'mwe-upwiz-status-progress' ) |
| 752 | + .addClass( 'mwe-upwiz-status-completed' ) |
| 753 | + .html( gM( 'mwe-upwiz-transported' ) ); |
| 754 | + }, |
| 755 | + |
| 756 | + /** |
| 757 | + * Run this when the value of the file input has changed. Check the file for various forms of goodness. |
| 758 | + * If okay, then update the visible filename (due to CSS trickery the real file input is invisible) |
| 759 | + */ |
| 760 | + fileChanged: function() { |
| 761 | + var _this = this; |
| 762 | + _this.clearErrors(); |
| 763 | + _this.upload.extractLocalFileInfo( $j( _this.fileInputCtrl ).val() ); |
| 764 | + if ( _this.isGoodExtension( _this.upload.extension ) ) { |
| 765 | + _this.updateFilename(); |
| 766 | + } else { |
| 767 | + //_this.error( 'bad-filename-extension', ext ); |
| 768 | + alert("bad extension"); |
| 769 | + } |
| 770 | + }, |
| 771 | + |
| 772 | + /** |
| 773 | + * Move the file input to cover a certain element on the page. |
| 774 | + * We use invisible file inputs because this is the only way to style a file input |
| 775 | + * or otherwise get it to do what you want. |
| 776 | + * It is helpful to sometimes move them to cover certain elements on the page, and |
| 777 | + * even to pass events like hover |
| 778 | + * @param selector jquery-compatible selector, for a single element |
| 779 | + */ |
| 780 | + moveFileInputToCover: function( selector ) { |
| 781 | + //mw.log( "moving to cover " + selector ); |
| 782 | + var _this = this; |
| 783 | + var $covered = $j( selector ); |
| 784 | + |
| 785 | + //mw.log( "position: " ); |
| 786 | + //mw.log( $covered.position() ); |
| 787 | + |
| 788 | + _this.fileCtrlContainer |
| 789 | + .css( $covered.position() ) |
| 790 | + .width( $covered.outerWidth() ) |
| 791 | + .height( $covered.outerHeight() ); |
| 792 | + |
| 793 | + // shift the file input over with negative margins, |
| 794 | + // internal to the overflow-containing div, so the div shows all button |
| 795 | + // and none of the textfield-like input |
| 796 | + $j( _this.fileInputCtrl ).css( { |
| 797 | + 'margin-left': '-' + ~~( $j( _this.fileInputCtrl).width() - $covered.outerWidth() - 10 ) + 'px', |
| 798 | + 'margin-top' : '-' + ~~( $j( _this.fileInputCtrl).height() - $covered.outerHeight() - 10 ) + 'px' |
| 799 | + } ); |
| 800 | + |
| 801 | + // we may be passing the file ctrl's hover events to another covered interface element |
| 802 | + // see toggleFileCtrlHover |
| 803 | + if ( _this.fileCtrlCovered ) { |
| 804 | + _this.fileCtrlCovered.removeClass( 'hover' ); |
| 805 | + } |
| 806 | + _this.fileCtrlCovered = $covered; |
| 807 | + |
| 808 | + }, |
| 809 | + |
| 810 | + /** |
| 811 | + * add class to an interface element covered by the fileCtrlContainer |
| 812 | + * we are not using jQuery.toggleClass because it seems to get this wrong too often -- dumbly activates when should deactivate & |
| 813 | + * vice versa. |
| 814 | + * @param jquery event |
| 815 | + */ |
| 816 | + addFileCtrlHover: function(e) { |
| 817 | + if ( this.fileCtrlCovered ) { |
| 818 | + this.fileCtrlCovered.addClass( 'hover' ); |
| 819 | + } |
| 820 | + }, |
| 821 | + |
| 822 | + /** |
| 823 | + * remove class from an jquery-wrapped interface element covered by the fileCtrlContainer |
| 824 | + * we are not using jQuery.toggleClass because it seems to get this wrong too often -- dumbly activates when should deactivate & |
| 825 | + * vice versa. |
| 826 | + * @param jquery event |
| 827 | + */ |
| 828 | + removeFileCtrlHover: function(e) { |
| 829 | + if ( this.fileCtrlCovered ) { |
| 830 | + this.fileCtrlCovered.removeClass( 'hover' ); |
| 831 | + } |
| 832 | + }, |
| 833 | + |
| 834 | + |
| 835 | + /** |
| 836 | + * this does two things: |
| 837 | + * 1 ) since the file input has been hidden with some clever CSS ( to avoid x-browser styling issues ), |
| 838 | + * update the visible filename |
| 839 | + * |
| 840 | + * 2 ) update the filename desired when added to MediaWiki. This should be RELATED to the filename on the filesystem, |
| 841 | + * but it should be silently fixed so that it does not trigger uniqueness conflicts. i.e. if server has cat.jpg we change ours to cat_2.jpg. |
| 842 | + * This is hard to do in a scalable fashion on the client; we don't want to do 12 api calls to get cat_12.jpg. |
| 843 | + * Ideally we should ask the SERVER for a decently unique filename related to our own. |
| 844 | + * So, at the moment, this is hacked with a guaranteed - unique filename instead. |
| 845 | + */ |
| 846 | + updateFilename: function() { |
| 847 | + var _this = this; |
| 848 | + var path = $j(_this.fileInputCtrl).attr('value'); |
| 849 | + |
| 850 | + |
| 851 | + // visible filename |
| 852 | + $j( _this.visibleFilename ).html( |
| 853 | + '<span class="ui-icon ui-icon-document" style="display: inline-block;"></span> ' + path |
| 854 | + ); |
| 855 | + |
| 856 | + // desired filename |
| 857 | + var filename = _this.convertPathToFilename( path ); |
| 858 | + _this.upload.originalFilename = filename; |
| 859 | + // this is a hack to get a filename guaranteed unique. |
| 860 | + uniqueFilename = mw.getConfig( 'userName' ) + "_" + ( new Date() ).getTime() + "_" + filename; |
| 861 | + $j( _this.filenameCtrl ).attr( 'value', uniqueFilename ); |
| 862 | + |
| 863 | + if ( ! _this.isFilled ) { |
| 864 | + _this.isFilled = true; |
| 865 | + $j( _this.div ).addClass( 'filled' ); |
| 866 | + $j( _this.visibleFilename ).show(); |
| 867 | + $j( _this.removeCtrl ).show(); |
| 868 | + $j(_this.div ).css( { |
| 869 | + 'position': 'relative', // own our own file input; it will move with us now. |
| 870 | + 'height': '24px' |
| 871 | + } ); |
| 872 | + _this.moveFileInputToCover( _this.visibleFilename ); |
| 873 | + $j( _this.upload ).trigger( 'filled' ); |
| 874 | + } else { |
| 875 | + $j( _this.upload ).trigger( 'filenameAccepted' ); |
| 876 | + } |
| 877 | + }, |
| 878 | + |
| 879 | + /** |
| 880 | + * Remove any complaints we had about errors and such |
| 881 | + * XXX this should be changed to something Theme compatible |
| 882 | + */ |
| 883 | + clearErrors: function() { |
| 884 | + var _this = this; |
| 885 | + $j( _this.div ).removeClass( 'mwe-upwiz-upload-error '); |
| 886 | + $j( _this.errorDiv ).hide().empty(); |
| 887 | + }, |
| 888 | + |
| 889 | + /** |
| 890 | + * Show an error with the upload |
| 891 | + */ |
| 892 | + error: function() { |
| 893 | + var _this = this; |
| 894 | + var args = Array.prototype.slice.call( arguments ); // copies arguments into a real array |
| 895 | + var msg = 'mwe-upwiz-upload-error-' + args[0]; |
| 896 | + $j( _this.errorDiv ).append( $j( '<p class="mwe-upwiz-upload-error">' + gM( msg, args.slice( 1 ) ) + '</p>') ); |
| 897 | + // apply a error style to entire did |
| 898 | + $j( _this.div ).addClass( 'mwe-upwiz-upload-error' ); |
| 899 | + $j( _this.errorDiv ).show(); |
| 900 | + }, |
| 901 | + |
| 902 | + /** |
| 903 | + * Get the extension of the path in fileInputCtrl |
| 904 | + * @return extension as string |
| 905 | + */ |
| 906 | + getExtension: function() { |
| 907 | + var _this = this; |
| 908 | + var path = $j(_this.fileInputCtrl).attr('value'); |
| 909 | + return mw.UploadWizardUtil.getExtension(path); |
| 910 | + }, |
| 911 | + |
| 912 | + /** |
| 913 | + * XXX this is common utility code |
| 914 | + * used when converting contents of a file input and coming up with a suitable "filename" for mediawiki |
| 915 | + * test: what if path is length 0 |
| 916 | + * what if path is all separators |
| 917 | + * what if path ends with a separator character |
| 918 | + * what if it ends with multiple separator characters |
| 919 | + * |
| 920 | + * @param path |
| 921 | + * @return filename suitable for mediawiki as string |
| 922 | + */ |
| 923 | + convertPathToFilename: function( path ) { |
| 924 | + if (path === undefined || path == '') { |
| 925 | + return ''; |
| 926 | + } |
| 927 | + |
| 928 | + var lastFileSeparatorIdx = Math.max(path.lastIndexOf( '/' ), path.lastIndexOf( '\\' )); |
| 929 | + // lastFileSeparatorIdx is now -1 if no separator found, or some index in the string. |
| 930 | + // so, +1, that is either 0 ( beginning of string ) or the character after last separator. |
| 931 | + // caution! could go past end of string... need to be more careful |
| 932 | + var filename = path.substr( lastFileSeparatorIdx + 1 ); |
| 933 | + return mw.UploadWizardUtil.pathToTitle( filename ); |
| 934 | + |
| 935 | + |
| 936 | + |
| 937 | + }, |
| 938 | + |
| 939 | + /** |
| 940 | + * XXX this is common utility code |
| 941 | + * copied because we'll probably need it... stripped from old doDestinationFill |
| 942 | + * this is used when checking for "bad" extensions in a filename. |
| 943 | + * @param ext |
| 944 | + * @return boolean if extension was acceptable |
| 945 | + */ |
| 946 | + isGoodExtension: function( ext ) { |
| 947 | + var _this = this; |
| 948 | + var found = false; |
| 949 | + var extensions = mw.getConfig('fileExtensions'); |
| 950 | + if ( extensions ) { |
| 951 | + for ( var i = 0; i < extensions.length; i++ ) { |
| 952 | + if ( extensions[i].toLowerCase() == ext ) { |
| 953 | + found = true; |
| 954 | + } |
| 955 | + } |
| 956 | + } |
| 957 | + return found; |
| 958 | + } |
| 959 | + |
| 960 | +}; |
| 961 | + |
| 962 | +/** |
| 963 | + * Object that represents an indvidual language description, in the details portion of Upload Wizard |
| 964 | + * @param languageCode |
| 965 | + */ |
| 966 | +mw.UploadWizardDescription = function( languageCode ) { |
| 967 | + var _this = this; |
| 968 | + |
| 969 | + // Logic copied from MediaWiki:UploadForm.js |
| 970 | + // Per request from Portuguese and Brazilian users, treat Brazilian Portuguese as Portuguese. |
| 971 | + if (languageCode == 'pt-br') { |
| 972 | + languageCode = 'pt'; |
| 973 | + // this was also in UploadForm.js, but without the heartwarming justification |
| 974 | + } else if (languageCode == 'en-gb') { |
| 975 | + languageCode = 'en'; |
| 976 | + } |
| 977 | + |
| 978 | + _this.languageMenu = mw.LanguageUpWiz.getMenu("lang", languageCode); |
| 979 | + $j(_this.languageMenu).addClass('mwe-upwiz-desc-lang-select'); |
| 980 | + _this.description = $j('<textarea name="desc" rows="2" cols="36" class="mwe-upwiz-desc-lang-text"></textarea>') |
| 981 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-description' ) ) |
| 982 | + .tipsy( { gravity: 'w', trigger: 'focus' } ) |
| 983 | + .growTextArea(); |
| 984 | + _this.div = $j('<div class="mwe-upwiz-desc-lang-container"></div>') |
| 985 | + .append( _this.languageMenu ) |
| 986 | + .append( _this.description ); |
| 987 | + |
| 988 | +}; |
| 989 | + |
| 990 | +mw.UploadWizardDescription.prototype = { |
| 991 | + |
| 992 | + /** |
| 993 | + * Obtain text of this description, suitable for including into Information template |
| 994 | + * @return wikitext as a string |
| 995 | + */ |
| 996 | + getWikiText: function() { |
| 997 | + var _this = this; |
| 998 | + var language = $j( _this.languageMenu ).val().trim(); |
| 999 | + var fix = mw.getConfig("languageTemplateFixups"); |
| 1000 | + if (fix[language]) { |
| 1001 | + language = fix[language]; |
| 1002 | + } |
| 1003 | + return '{{' + language + '|1=' + $j( _this.description ).val().trim() + '}}'; |
| 1004 | + } |
| 1005 | +}; |
| 1006 | + |
| 1007 | +/** |
| 1008 | + * Object that represents the Details (step 2) portion of the UploadWizard |
| 1009 | + * n.b. each upload gets its own details. |
| 1010 | + * |
| 1011 | + * XXX a lot of this construction is not really the jQuery way. |
| 1012 | + * The correct thing would be to have some hidden static HTML |
| 1013 | + * on the page which we clone and slice up with selectors. Inputs can still be members of the object |
| 1014 | + * but they'll be found by selectors, not by creating them as members and then adding them to a DOM structure. |
| 1015 | + * |
| 1016 | + * XXX this should have styles for what mode we're in |
| 1017 | + * |
| 1018 | + * @param UploadWizardUpload |
| 1019 | + * @param containerDiv The div to put the interface into |
| 1020 | + */ |
| 1021 | +mw.UploadWizardDetails = function( upload, containerDiv ) { |
| 1022 | + |
| 1023 | + var _this = this; |
| 1024 | + _this.upload = upload; |
| 1025 | + |
| 1026 | + _this.descriptions = []; |
| 1027 | + |
| 1028 | + _this.div = $j( '<div class="mwe-upwiz-info-file"></div>' ); |
| 1029 | + |
| 1030 | + _this.thumbnailDiv = $j( '<div class="mwe-upwiz-thumbnail"></div>' ); |
| 1031 | + |
| 1032 | + _this.errorDiv = $j( '<div class="mwe-upwiz-details-error"></div>' ); |
| 1033 | + |
| 1034 | + _this.dataDiv = $j( '<div class="mwe-upwiz-data"></div>' ); |
| 1035 | + |
| 1036 | + // descriptions |
| 1037 | + _this.descriptionsDiv = $j( '<div class="mwe-upwiz-details-descriptions mwe-upwiz-details-input"></div>' ); |
| 1038 | + |
| 1039 | + |
| 1040 | + _this.descriptionAdder = $j( '<a class="mwe-upwiz-desc-add"/>' ) |
| 1041 | + .attr( 'href', '#' ) |
| 1042 | + .html( gM( 'mwe-upwiz-desc-add-0' ) ) |
| 1043 | + .click( function( ) { _this.addDescription(); } ); |
| 1044 | + |
| 1045 | + _this.descriptionsContainerDiv = |
| 1046 | + $j( '<div class="mwe-upwiz-details-descriptions-container"></div>' ) |
| 1047 | + .append( $j( '<div class="mwe-upwiz-details-label">' + gM( 'mwe-upwiz-desc' ) + '</div>' ) ) |
| 1048 | + .append( _this.descriptionsDiv ) |
| 1049 | + .append( $j( '<div class="mwe-upwiz-details-descriptions-add"></div>' ) |
| 1050 | + .append( _this.descriptionAdder ) ); |
| 1051 | + // Commons specific help for titles |
| 1052 | + // http://commons.wikimedia.org/wiki/Commons:File_naming |
| 1053 | + // http://commons.wikimedia.org/wiki/MediaWiki:Filename-prefix-blacklist |
| 1054 | + // XXX make sure they can't use ctrl characters or returns or any other bad stuff. |
| 1055 | + _this.titleInput = $j( '<textarea type="text" rows="1" class="mwe-title mwe-long-textarea"></textarea>' ) |
| 1056 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-title' ) ) |
| 1057 | + .tipsy( { gravity: 'w', trigger: 'focus' } ) |
| 1058 | + .keyup( function() { |
| 1059 | + _this.setFilenameFromTitle(); |
| 1060 | + } ) |
| 1061 | + .growTextArea() |
| 1062 | + .destinationChecked( { |
| 1063 | + spinner: function(bool) { _this.toggleDestinationBusy(bool); }, |
| 1064 | + preprocess: function( name ) { return _this.getFilenameFromTitle(); }, // XXX this is no longer a pre-process |
| 1065 | + processResult: function( result ) { _this.processDestinationCheck( result ); } |
| 1066 | + } ) |
| 1067 | + ; |
| 1068 | + |
| 1069 | + _this.titleErrorDiv = $j('<div></div>'); |
| 1070 | + |
| 1071 | + _this.titleContainerDiv = $j('<div class="mwe-upwiz-details-label-input></div>') |
| 1072 | + .append( $j( '<div class="mwe-upwiz-details-label"></div>' ).append( gM( 'mwe-upwiz-title' ) ) ) |
| 1073 | + .append( $j( '<div class="mwe-upwiz-details-input"></div>' ).append( _this.titleInput ) ) |
| 1074 | + .append( _this.titleErrorDiv ); |
| 1075 | + |
| 1076 | + _this.deedDiv = $j( '<div class="mwe-upwiz-custom-deed" />' ); |
| 1077 | + |
| 1078 | + _this.copyrightInfoFieldset = $j('<fieldset class="mwe-fieldset mwe-upwiz-copyright-info"></fieldset>') |
| 1079 | + .hide() |
| 1080 | + .append( |
| 1081 | + $j( '<legend class="mwe-legend">' ).append( gM( 'mwe-upwiz-copyright-info' ) ), |
| 1082 | + _this.deedDiv |
| 1083 | + ); |
| 1084 | + |
| 1085 | + _this.moreDetailsDiv = $j('<div class="mwe-more-details"></div>').maskSafeHide(); |
| 1086 | + |
| 1087 | + _this.moreDetailsCtrlDiv = $j( '<div class="mwe-upwiz-details-more-options"></div>' ); |
| 1088 | + |
| 1089 | + |
| 1090 | + |
| 1091 | + _this.dateInput = $j( '<input type="text" class="mwe-date" size="20"/>' ); |
| 1092 | + // XXX suddenly this isn't working. Seems to be a problem with monobook. If I datepicker-ify an input outside the |
| 1093 | + // content area, it works. Vector is fine |
| 1094 | + $j( _this.dateInput ).datepicker( { |
| 1095 | + dateFormat: 'yy-mm-dd', // oddly, this means yyyy-mm-dd |
| 1096 | + buttonImage: '/js/mwEmbed/skins/common/images/calendar.gif', |
| 1097 | + buttonImageOnly: false // XXX determine what this does, docs are confusing |
| 1098 | + } ); |
| 1099 | + |
| 1100 | + _this.locationInput = $j( '<input type="text" class="mwe-location" size="20"/>' ); |
| 1101 | + |
| 1102 | + var aboutThisWorkFieldset = $j('<fieldset class="mwe-fieldset"></fieldset>') |
| 1103 | + .append( $j( '<legend class="mwe-legend">' ).append( gM( 'mwe-upwiz-about-this-work' ) ) ) |
| 1104 | + .append( $j( '<div class="mwe-upwiz-details-more-subdiv">' ) |
| 1105 | + .append( $j( '<div class="mwe-upwiz-details-label-input"></div>' ) |
| 1106 | + .append( $j( '<div class="mwe-upwiz-details-more-label"></div>' ).append( gM( 'mwe-upwiz-date-created' ) ) ) |
| 1107 | + .append( $j( '<div class="mwe-upwiz-details-more-input"></div>' ).append( _this.dateInput ) ) |
| 1108 | + ) |
| 1109 | + .append( $j ( '<div style="display: none;"></div>' ) // see prefillLocation |
| 1110 | + .append( $j( '<div class="mwe-upwiz-details-more-label"></div>' ).append( gM( 'mwe-upwiz-location' ) ) ) |
| 1111 | + .append( $j( '<div class="mwe-upwiz-details-more-input"></div>' ).append( _this.locationInput ) ) |
| 1112 | + ) |
| 1113 | + ); |
| 1114 | + |
| 1115 | + |
| 1116 | + |
| 1117 | + var aboutFileFieldset = $j('<fieldset class="mwe-fieldset"></fieldset>') |
| 1118 | + .append( $j( '<legend class="mwe-legend">' ).append( gM( 'mwe-upwiz-about-format' ) ) ) |
| 1119 | + .append( $j( '<div class="mwe-upwiz-details-more-subdiv">' ) |
| 1120 | + .append( $j( '<div></div>' ) |
| 1121 | + .append( $j( '<div class="mwe-upwiz-details-more-label"></div>' ).append( gM( 'mwe-upwiz-filename-tag' ) ) ) |
| 1122 | + .append( $j( '<div class="mwe-upwiz-details-filename mwe-upwiz-details-more-input"></div>' ) ) ) ); |
| 1123 | + |
| 1124 | + _this.otherInformationInput = $j( '<textarea class="mwe-upwiz-other-textarea"></textarea>' ) |
| 1125 | + .growTextArea() |
| 1126 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-other' ) ) |
| 1127 | + .tipsy( { gravity: 'w', trigger: 'focus' } ); |
| 1128 | + |
| 1129 | + var otherInformationDiv = $j('<div></div>') |
| 1130 | + .append( $j( '<div class="mwe-upwiz-details-more-label">' ).append( gM( 'mwe-upwiz-other' ) ) ) |
| 1131 | + .append( _this.otherInformationInput ); |
| 1132 | + |
| 1133 | + |
| 1134 | + $j( _this.div ) |
| 1135 | + .addClass( 'ui-helper-clearfix' ) |
| 1136 | + .append( _this.thumbnailDiv ) |
| 1137 | + .append( _this.errorDiv ) |
| 1138 | + .append( $j( _this.dataDiv ) |
| 1139 | + .append( _this.descriptionsContainerDiv ) |
| 1140 | + .append( _this.titleContainerDiv ) |
| 1141 | + .append( _this.copyrightInfoFieldset ) |
| 1142 | + .append( _this.moreDetailsCtrlDiv ) |
| 1143 | + .append( $j( _this.moreDetailsDiv ) |
| 1144 | + .append( aboutThisWorkFieldset ) |
| 1145 | + //.append( aboutFileFieldset ) |
| 1146 | + .append( otherInformationDiv ) |
| 1147 | + ) |
| 1148 | + ); |
| 1149 | + |
| 1150 | + mw.UploadWizardUtil.makeMoreToggler( _this.moreDetailsCtrlDiv, _this.moreDetailsDiv ); |
| 1151 | + |
| 1152 | + _this.addDescription(); |
| 1153 | + $j( containerDiv ).append( _this.div ); |
| 1154 | + |
| 1155 | + |
| 1156 | +}; |
| 1157 | + |
| 1158 | +mw.UploadWizardDetails.prototype = { |
| 1159 | + |
| 1160 | + /** |
| 1161 | + * toggles whether we use the 'macro' deed or our own |
| 1162 | + */ |
| 1163 | + useCustomDeedChooser: function() { |
| 1164 | + var _this = this; |
| 1165 | + _this.copyrightInfoFieldset.show(); |
| 1166 | + _this.upload.wizardDeedChooser = _this.upload.deedChooser; |
| 1167 | + _this.upload.deedChooser = new mw.UploadWizardDeedChooser( _this.deedDiv ); |
| 1168 | + }, |
| 1169 | + |
| 1170 | + useDeedChooser: function( deedChooser ) { |
| 1171 | + var _this = this; |
| 1172 | + _this.upload.deedChooser = deedChooser; |
| 1173 | + _this.deedDiv.empty(); |
| 1174 | + }, |
| 1175 | + |
| 1176 | + |
| 1177 | + /** |
| 1178 | + * Sets the filename from the title plus this upload's extension. |
| 1179 | + */ |
| 1180 | + setFilenameFromTitle: function() { |
| 1181 | + var _this = this; |
| 1182 | + _this.filename = mw.getConfig( 'fileNamespace' ) + ':' + _this.getFilenameFromTitle(); |
| 1183 | + $j( '#mwe-upwiz-details-filename' ).text( _this.filename ); |
| 1184 | + |
| 1185 | + }, |
| 1186 | + |
| 1187 | + /** |
| 1188 | + * Gets a filename from the human readable title, using upload's extension. |
| 1189 | + * @return Filename |
| 1190 | + */ |
| 1191 | + getFilenameFromTitle: function() { |
| 1192 | + var _this = this; |
| 1193 | + var name = $j( _this.titleInput ).val(); |
| 1194 | + return mw.UploadWizardUtil.pathToTitle( name ) + '.' + _this.upload.extension; |
| 1195 | + }, |
| 1196 | + |
| 1197 | + |
| 1198 | + /** |
| 1199 | + * show file destination field as "busy" while checking |
| 1200 | + * @param busy boolean true = show busy-ness, false = remove |
| 1201 | + */ |
| 1202 | + toggleDestinationBusy: function ( busy ) { |
| 1203 | + var _this = this; |
| 1204 | + if (busy) { |
| 1205 | + _this.titleInput.addClass( "busy" ); |
| 1206 | + } else { |
| 1207 | + _this.titleInput.removeClass( "busy" ); |
| 1208 | + } |
| 1209 | + }, |
| 1210 | + |
| 1211 | + /** |
| 1212 | + * Process the result of a destination filename check. |
| 1213 | + * See mw.DestinationChecker.js for documentation of result format |
| 1214 | + * XXX would be simpler if we created all these divs in the DOM and had a more jquery-friendly way of selecting |
| 1215 | + * attrs. Instead we create & destroy whole interface each time. Won't someone think of the DOM elements? |
| 1216 | + * @param result |
| 1217 | + */ |
| 1218 | + processDestinationCheck: function( result ) { |
| 1219 | + var _this = this; |
| 1220 | + |
| 1221 | + if ( result.isUnique ) { |
| 1222 | + _this.titleErrorDiv.hide().empty(); |
| 1223 | + _this.ignoreWarningsInput = undefined; |
| 1224 | + return; |
| 1225 | + } |
| 1226 | + |
| 1227 | + // result is NOT unique |
| 1228 | + var title = result.title; |
| 1229 | + var img = result.img; |
| 1230 | + var href = result.href; |
| 1231 | + |
| 1232 | + _this.ignoreWarningsInput = $j("<input />").attr( { type: 'checkbox', name: 'ignorewarnings' } ); |
| 1233 | + |
| 1234 | + var $fileAlreadyExists = $j('<div />') |
| 1235 | + .append( |
| 1236 | + gM( 'mwe-fileexists', |
| 1237 | + $j('<a />') |
| 1238 | + .attr( { target: '_new', href: href } ) |
| 1239 | + .text( title ) |
| 1240 | + ), |
| 1241 | + $j('<br />'), |
| 1242 | + _this.ignoreWarningsInput, |
| 1243 | + gM('mwe-upwiz-overwrite') |
| 1244 | + ); |
| 1245 | + |
| 1246 | + var $imageLink = $j('<a />') |
| 1247 | + .addClass( 'image' ) |
| 1248 | + .attr( { target: '_new', href: href } ) |
| 1249 | + .append( |
| 1250 | + $j( '<img />') |
| 1251 | + .addClass( 'thumbimage' ) |
| 1252 | + .attr( { |
| 1253 | + 'width' : img.thumbwidth, |
| 1254 | + 'height' : img.thumbheight, |
| 1255 | + 'border' : 0, |
| 1256 | + 'src' : img.thumburl, |
| 1257 | + 'alt' : title |
| 1258 | + } ) |
| 1259 | + ); |
| 1260 | + |
| 1261 | + var $imageCaption = $j( '<div />' ) |
| 1262 | + .addClass( 'thumbcaption' ) |
| 1263 | + .append( |
| 1264 | + $j('<div />') |
| 1265 | + .addClass( "magnify" ) |
| 1266 | + .append( |
| 1267 | + $j('<a />' ) |
| 1268 | + .addClass( 'internal' ) |
| 1269 | + .attr( { |
| 1270 | + 'title' : gM('mwe-thumbnail-more'), |
| 1271 | + 'href' : href |
| 1272 | + } ), |
| 1273 | + |
| 1274 | + $j( '<img />' ) |
| 1275 | + .attr( { |
| 1276 | + 'border' : 0, |
| 1277 | + 'width' : 15, |
| 1278 | + 'height' : 11, |
| 1279 | + 'src' : mw.getConfig( 'images_path' ) + 'magnify-clip.png' |
| 1280 | + } ), |
| 1281 | + |
| 1282 | + $j('<span />') |
| 1283 | + .html( gM( 'mwe-fileexists-thumb' ) ) |
| 1284 | + ) |
| 1285 | + ); |
| 1286 | + |
| 1287 | + $j( _this.titleErrorDiv ).html( |
| 1288 | + $j('<span />') // dummy argument since .html() only takes one arg |
| 1289 | + .append( |
| 1290 | + $fileAlreadyExists, |
| 1291 | + $j( '<div />' ) |
| 1292 | + .addClass( 'thumb tright' ) |
| 1293 | + .append( |
| 1294 | + $j( '<div />' ) |
| 1295 | + .addClass( 'thumbinner' ) |
| 1296 | + .css({ |
| 1297 | + 'width' : ( parseInt( img.thumbwidth ) + 2 ) + 'px;' |
| 1298 | + }) |
| 1299 | + .append( |
| 1300 | + $imageLink, |
| 1301 | + $imageCaption |
| 1302 | + ) |
| 1303 | + ) |
| 1304 | + ) |
| 1305 | + ).show(); |
| 1306 | + |
| 1307 | + }, |
| 1308 | + |
| 1309 | + /** |
| 1310 | + * Do anything related to a change in the number of descriptions |
| 1311 | + */ |
| 1312 | + recountDescriptions: function() { |
| 1313 | + var _this = this; |
| 1314 | + // if there is some maximum number of descriptions, deal with that here |
| 1315 | + $j( _this.descriptionAdder ).html( gM( 'mwe-upwiz-desc-add-' + ( _this.descriptions.length == 0 ? '0' : 'n' ) ) ); |
| 1316 | + }, |
| 1317 | + |
| 1318 | + |
| 1319 | + /** |
| 1320 | + * Add a new description |
| 1321 | + */ |
| 1322 | + addDescription: function() { |
| 1323 | + var _this = this; |
| 1324 | + var languageCode = _this.descriptions.length ? mw.LanguageUpWiz.UNKNOWN : mw.getConfig('userLanguage' ); |
| 1325 | + var description = new mw.UploadWizardDescription( languageCode ); |
| 1326 | + |
| 1327 | + description.removeCtrl = $j('<a title="' + gM( 'mwe-upwiz-remove-description' ) + '" href="#">x</a>' ) |
| 1328 | + .addClass('mwe-upwiz-remove' ) |
| 1329 | + .addClass('mwe-upwiz-remove-desc' ) |
| 1330 | + .click( function() { _this.removeDescription( description ) } ) |
| 1331 | + .get( 0 ); |
| 1332 | + $j( description.div ).append( description.removeCtrl ); |
| 1333 | + |
| 1334 | + $j( _this.descriptionsDiv ).append( description.div ); |
| 1335 | + _this.descriptions.push( description ); |
| 1336 | + _this.recountDescriptions(); |
| 1337 | + }, |
| 1338 | + |
| 1339 | + /** |
| 1340 | + * Remove a description |
| 1341 | + * @param description |
| 1342 | + */ |
| 1343 | + removeDescription: function( description ) { |
| 1344 | + var _this = this; |
| 1345 | + $j( description.div ).remove(); |
| 1346 | + mw.UploadWizardUtil.removeItem( _this.descriptions, description ); |
| 1347 | + _this.recountDescriptions(); |
| 1348 | + }, |
| 1349 | + |
| 1350 | + /** |
| 1351 | + * Display an error with details |
| 1352 | + * XXX this is a lot like upload ui's error -- should merge |
| 1353 | + */ |
| 1354 | + error: function() { |
| 1355 | + var _this = this; |
| 1356 | + var args = Array.prototype.slice.call( arguments ); // copies arguments into a real array |
| 1357 | + var msg = 'mwe-upwiz-upload-error-' + args[0]; |
| 1358 | + $j( _this.errorDiv ).append( $j( '<p class="mwe-upwiz-upload-error">' + gM( msg, args.slice( 1 ) ) + '</p>' ) ); |
| 1359 | + // apply a error style to entire did |
| 1360 | + $j( _this.div ).addClass( 'mwe-upwiz-upload-error' ); |
| 1361 | + $j( _this.dataDiv ).hide(); |
| 1362 | + $j( _this.errorDiv ).show(); |
| 1363 | + }, |
| 1364 | + |
| 1365 | + /** |
| 1366 | + * Given the API result pull some info into the form ( for instance, extracted from EXIF, desired filename ) |
| 1367 | + * @param result Upload API result object |
| 1368 | + */ |
| 1369 | + populate: function() { |
| 1370 | + var _this = this; |
| 1371 | + mw.log( "populating details from upload" ); |
| 1372 | + _this.upload.setThumbnail( _this.thumbnailDiv ); |
| 1373 | + _this.prefillDate(); |
| 1374 | + _this.prefillSource(); |
| 1375 | + _this.prefillAuthor(); |
| 1376 | + _this.prefillTitle(); |
| 1377 | + _this.prefillFilename(); |
| 1378 | + _this.prefillLocation(); |
| 1379 | + }, |
| 1380 | + |
| 1381 | + /** |
| 1382 | + * Check if we got an EXIF date back; otherwise use today's date; and enter it into the details |
| 1383 | + * XXX We ought to be using date + time here... |
| 1384 | + * EXIF examples tend to be in ISO 8601, but the separators are sometimes things like colons, and they have lots of trailing info |
| 1385 | + * (which we should actually be using, such as time and timezone) |
| 1386 | + */ |
| 1387 | + prefillDate: function() { |
| 1388 | + var _this = this; |
| 1389 | + var yyyyMmDdRegex = /^(\d\d\d\d)[:\/-](\d\d)[:\/-](\d\d)\D.*/; |
| 1390 | + var dateStr; |
| 1391 | + var metadata = _this.upload.imageinfo.metadata; |
| 1392 | + $j.each([metadata.datetimeoriginal, metadata.datetimedigitized, metadata.datetime, metadata['date']], |
| 1393 | + function( i, imageinfoDate ) { |
| 1394 | + if ( imageinfoDate !== undefined ) { |
| 1395 | + var d = imageinfoDate.trim(); |
| 1396 | + if ( d.match( yyyyMmDdRegex ) ) { |
| 1397 | + dateStr = d.replace( yyyyMmDdRegex, "$1-$2-$3" ); |
| 1398 | + return false; // break from $j.each |
| 1399 | + } |
| 1400 | + } |
| 1401 | + } |
| 1402 | + ); |
| 1403 | + // if we don't have EXIF or other metadata, let's use "now" |
| 1404 | + // XXX if we have FileAPI, it might be clever to look at file attrs, saved |
| 1405 | + // in the upload object for use here later, perhaps |
| 1406 | + function pad( n ) { |
| 1407 | + return n < 10 ? "0" + n : n; |
| 1408 | + } |
| 1409 | + |
| 1410 | + if (dateStr === undefined) { |
| 1411 | + d = new Date(); |
| 1412 | + dateStr = d.getUTCFullYear() + '-' + pad(d.getUTCMonth()) + '-' + pad(d.getUTCDate()); |
| 1413 | + } |
| 1414 | + |
| 1415 | + // ok by now we should definitely have a date string formatted in YYYY-MM-DD |
| 1416 | + $j( _this.dateInput ).val( dateStr ); |
| 1417 | + }, |
| 1418 | + |
| 1419 | + /** |
| 1420 | + * Set the title of the thing we just uploaded, visibly |
| 1421 | + * Note: the interface's notion of "filename" versus "title" is the opposite of MediaWiki |
| 1422 | + */ |
| 1423 | + prefillTitle: function() { |
| 1424 | + var _this = this; |
| 1425 | + var titleExt = mw.UploadWizardUtil.titleToPath( _this.upload.originalFilename ); |
| 1426 | + var title = titleExt.replace( /\.\w+$/, '' ); |
| 1427 | + $j( _this.titleInput ).val( title ); |
| 1428 | + }, |
| 1429 | + |
| 1430 | + /** |
| 1431 | + * Set the title of the thing we just uploaded, visibly |
| 1432 | + * Note: the interface's notion of "filename" versus "title" is the opposite of MediaWiki |
| 1433 | + */ |
| 1434 | + prefillFilename: function() { |
| 1435 | + var _this = this; |
| 1436 | + _this.setFilenameFromTitle(); |
| 1437 | + }, |
| 1438 | + |
| 1439 | + /** |
| 1440 | + * Prefill location inputs (and/or scroll to position on map) from image info and metadata |
| 1441 | + * |
| 1442 | + * At least for my test images, the EXIF parser on MediaWiki is not giving back any data for |
| 1443 | + * GPSLatitude, GPSLongitude, or GPSAltitudeRef. It is giving the lat/long Refs, the Altitude, and the MapDatum |
| 1444 | + * So, this is broken until we fix MediaWiki's parser, OR, parse it ourselves somehow |
| 1445 | + * |
| 1446 | + * in Image namespace |
| 1447 | + * GPSTag Long ?? |
| 1448 | + * |
| 1449 | + * in GPSInfo namespace |
| 1450 | + * GPSVersionID byte* 2000 = 2.0.0.0 |
| 1451 | + * GPSLatitude rational |
| 1452 | + * GPSLatitudeRef ascii (N | S) or North | South |
| 1453 | + * GPSLongitude rational |
| 1454 | + * GPSLongitudeRef ascii (E | W) or East | West |
| 1455 | + * GPSAltitude rational |
| 1456 | + * GPSAltitudeRef byte (0 | 1) above or below sea level |
| 1457 | + * GPSImgDirection rational |
| 1458 | + * GPSImgDirectionRef ascii (M | T) magnetic or true north |
| 1459 | + * GPSMapDatum ascii "WGS-84" is the standard |
| 1460 | + * |
| 1461 | + * A 'rational' is a string like this: |
| 1462 | + * "53/1 0/1 201867/4096" --> 53 deg 0 min 49.284 seconds |
| 1463 | + * "2/1 11/1 64639/4096" --> 2 deg 11 min 15.781 seconds |
| 1464 | + * "122/1" -- 122 m (altitude) |
| 1465 | + */ |
| 1466 | + prefillLocation: function() { |
| 1467 | + var _this = this; |
| 1468 | + var metadata = _this.upload.imageinfo.metadata; |
| 1469 | + if (metadata === undefined) { |
| 1470 | + return; |
| 1471 | + } |
| 1472 | + |
| 1473 | + |
| 1474 | + }, |
| 1475 | + |
| 1476 | + /** |
| 1477 | + * Given a decimal latitude and longitude, return filled out {{Location}} template |
| 1478 | + * @param latitude decimal latitude ( -90.0 >= n >= 90.0 ; south = negative ) |
| 1479 | + * @param longitude decimal longitude ( -180.0 >= n >= 180.0 ; west = negative ) |
| 1480 | + * @param scale (optional) how rough the geocoding is. |
| 1481 | + * @param heading (optional) what direction the camera is pointing in. (decimal 0.0-360.0, 0 = north, 90 = E) |
| 1482 | + * @return string with WikiText which will geotag this record |
| 1483 | + */ |
| 1484 | + coordsToWikiText: function(latitude, longitude, scale, heading) { |
| 1485 | + //Wikipedia |
| 1486 | + //http://en.wikipedia.org/wiki/Wikipedia:WikiProject_Geographical_coordinates#Parameters |
| 1487 | + // http://en.wikipedia.org/wiki/Template:Coord |
| 1488 | + //{{coord|61.1631|-149.9721|type:landmark_globe:earth_region:US-AK_scale:150000_source:gnis|name=Kulis Air National Guard Base}} |
| 1489 | + |
| 1490 | + //Wikimedia Commons |
| 1491 | + //{{Coor dms|41|19|20.4|N|19|38|36.7|E}} |
| 1492 | + //{{Location}} |
| 1493 | + |
| 1494 | + }, |
| 1495 | + |
| 1496 | + /** |
| 1497 | + * If there is a way to figure out source from image info, do so here |
| 1498 | + * XXX user pref? |
| 1499 | + */ |
| 1500 | + prefillSource: function() { |
| 1501 | + // we have no way to do this AFAICT |
| 1502 | + }, |
| 1503 | + |
| 1504 | + /** |
| 1505 | + * Prefill author (such as can be determined) from image info and metadata |
| 1506 | + * XXX user pref? |
| 1507 | + */ |
| 1508 | + prefillAuthor: function() { |
| 1509 | + var _this = this; |
| 1510 | + if (_this.upload.imageinfo.metadata.author !== undefined) { |
| 1511 | + $j( _this.authorInput ).val( _this.upload.imageinfo.metadata.author ); |
| 1512 | + } |
| 1513 | + |
| 1514 | + }, |
| 1515 | + |
| 1516 | + /** |
| 1517 | + * Prefill license (such as can be determined) from image info and metadata |
| 1518 | + * XXX user pref? |
| 1519 | + */ |
| 1520 | + prefillLicense: function() { |
| 1521 | + var _this = this; |
| 1522 | + var copyright = _this.upload.imageinfo.metadata.copyright; |
| 1523 | + if (copyright !== undefined) { |
| 1524 | + if (copyright.match(/\bcc-by-sa\b/i)) { |
| 1525 | + // set license to be that CC-BY-SA |
| 1526 | + } else if (copyright.match(/\bcc-by\b/i)) { |
| 1527 | + // set license to be that |
| 1528 | + } else if (copyright.match(/\bcc-zero\b/i)) { |
| 1529 | + // set license to be that |
| 1530 | + // XXX any other licenses we could guess from copyright statement |
| 1531 | + } else { |
| 1532 | + $j( _this.licenseInput ).val( copyright ); |
| 1533 | + } |
| 1534 | + } |
| 1535 | + }, |
| 1536 | + |
| 1537 | + |
| 1538 | + /** |
| 1539 | + * |
| 1540 | + showErrors: function() { |
| 1541 | + var _this = this; |
| 1542 | + $j.each( _this.errors, function() { |
| 1543 | + |
| 1544 | + } ); |
| 1545 | + }, |
| 1546 | + */ |
| 1547 | + |
| 1548 | + /** |
| 1549 | + * Convert entire details for this file into wikiText, which will then be posted to the file |
| 1550 | + * XXX there is a WikiText sanitizer in use on UploadForm -- use that here, or port it |
| 1551 | + * @return wikitext representing all details |
| 1552 | + */ |
| 1553 | + getWikiText: function() { |
| 1554 | + var _this = this; |
| 1555 | + |
| 1556 | + // XXX validate! |
| 1557 | + if ( ! _this.isReady() ) { |
| 1558 | + alert( "ZOMG THIS DEED IS NOT READY" ); |
| 1559 | + return null; |
| 1560 | + } |
| 1561 | + |
| 1562 | + |
| 1563 | + |
| 1564 | + wikiText = ''; |
| 1565 | + |
| 1566 | + |
| 1567 | + // http://commons.wikimedia.org / wiki / Template:Information |
| 1568 | + |
| 1569 | + // can we be more slick and do this with maps, applys, joins? |
| 1570 | + var information = { |
| 1571 | + 'description' : '', // {{lang|description in lang}}* required |
| 1572 | + 'date' : '', // YYYY, YYYY-MM, or YYYY-MM-DD required - use jquery but allow editing, then double check for sane date. |
| 1573 | + 'source' : '', // {{own}} or wikitext optional |
| 1574 | + 'author' : '', // any wikitext, but particularly {{Creator:Name Surname}} required |
| 1575 | + 'permission' : '', // leave blank unless OTRS pending; by default will be "see below" optional |
| 1576 | + 'other_versions' : '', // pipe separated list, other versions optional |
| 1577 | + 'other_fields' : '' // ??? additional table fields |
| 1578 | + }; |
| 1579 | + |
| 1580 | + // sanity check the descriptions -- do not have two in the same lang |
| 1581 | + // all should be a known lang |
| 1582 | + if ( _this.descriptions.length === 0 ) { |
| 1583 | + // ruh roh |
| 1584 | + // we should not even allow them to press the button ( ? ) but then what about the queue... |
| 1585 | + } |
| 1586 | + $j.each( _this.descriptions, function( i, desc ) { |
| 1587 | + information['description'] += desc.getWikiText(); |
| 1588 | + } ); |
| 1589 | + |
| 1590 | + // XXX add a sanity check here for good date |
| 1591 | + information['date'] = $j( _this.dateInput ).val().trim(); |
| 1592 | + |
| 1593 | + var deed = _this.upload.deedChooser.deed; |
| 1594 | + |
| 1595 | + information['source'] = deed.getSourceWikiText(); |
| 1596 | + |
| 1597 | + information['author'] = deed.getAuthorWikiText(); |
| 1598 | + |
| 1599 | + var info = ''; |
| 1600 | + for ( var key in information ) { |
| 1601 | + info += '|' + key + '=' + information[key] + "\n"; |
| 1602 | + } |
| 1603 | + |
| 1604 | + wikiText += "=={{int:filedesc}}==\n"; |
| 1605 | + |
| 1606 | + wikiText += '{{Information\n' + info + '}}\n'; |
| 1607 | + |
| 1608 | + |
| 1609 | + wikiText += "=={{int:license-header}}==\n"; |
| 1610 | + |
| 1611 | + wikiText += deed.getLicenseWikiText(); |
| 1612 | + |
| 1613 | + // add a location template |
| 1614 | + |
| 1615 | + // add an "anything else" template if needed |
| 1616 | + var otherInfoWikiText = $j( _this.otherInformationInput ).val().trim(); |
| 1617 | + if ( otherInfoWikiText != '' ) { |
| 1618 | + wikiText += "=={{int:otherinfo}}==\n"; |
| 1619 | + wikiText += otherInfoWikiText; |
| 1620 | + } |
| 1621 | + |
| 1622 | + return wikiText; |
| 1623 | + }, |
| 1624 | + |
| 1625 | + /** |
| 1626 | + * Check if we are ready to post wikitext |
| 1627 | + */ |
| 1628 | + isReady: function() { |
| 1629 | + var _this = this; |
| 1630 | + return _this.upload.deedChooser.deed.isReady(); |
| 1631 | + |
| 1632 | + // somehow, all the various issues discovered with this upload should be present in a single place |
| 1633 | + // where we can then check on |
| 1634 | + // perhaps as simple as _this.issues or _this.agenda |
| 1635 | + }, |
| 1636 | + |
| 1637 | + /** |
| 1638 | + * Post wikitext as edited here, to the file |
| 1639 | + * XXX This should be split up -- one part should get wikitext from the interface here, and the ajax call |
| 1640 | + * should be be part of upload |
| 1641 | + */ |
| 1642 | + submit: function() { |
| 1643 | + var _this = this; |
| 1644 | + |
| 1645 | + |
| 1646 | + // are we okay to submit? |
| 1647 | + // all necessary fields are ready |
| 1648 | + // check descriptions |
| 1649 | + // the filename is in a sane state |
| 1650 | + var desiredFilename = _this.filename; |
| 1651 | + shouldRename = ( desiredFilename != _this.upload.title ); |
| 1652 | + |
| 1653 | + // if ok to go |
| 1654 | + // XXX lock down the interface, spinnerify |
| 1655 | + // else |
| 1656 | + // point out problems |
| 1657 | + |
| 1658 | + |
| 1659 | + // XXX check state of details for okayness ( license selected, at least one desc, sane filename ) |
| 1660 | + var wikiText = _this.getWikiText(); |
| 1661 | + mw.log( wikiText ); |
| 1662 | + |
| 1663 | + var params = { |
| 1664 | + action: 'edit', |
| 1665 | + token: mw.getConfig( 'token' ), |
| 1666 | + title: _this.upload.title, |
| 1667 | + // section: 0, ?? causing issues? |
| 1668 | + text: wikiText, |
| 1669 | + summary: "User edited page with " + mw.UploadWizard.userAgent, |
| 1670 | + // notminor: 1, |
| 1671 | + // basetimestamp: _this.upload.imageinfo.timestamp, ( conflicts? ) |
| 1672 | + nocreate: 1 |
| 1673 | + }; |
| 1674 | + |
| 1675 | + var endCallback = function() { _this.completeDetailsSubmission(); } |
| 1676 | + |
| 1677 | + mw.log( "editing!" ); |
| 1678 | + mw.log( params ); |
| 1679 | + var callback = function( result ) { |
| 1680 | + mw.log( result ); |
| 1681 | + mw.log( "successful edit" ); |
| 1682 | + if ( shouldRename ) { |
| 1683 | + _this.rename( desiredFilename, endCallback ); |
| 1684 | + } else { |
| 1685 | + endCallback(); |
| 1686 | + } |
| 1687 | + }; |
| 1688 | + |
| 1689 | + _this.upload.state = 'submitting-details'; |
| 1690 | + _this.showProgress(); |
| 1691 | + mw.getJSON( params, callback ); |
| 1692 | + }, |
| 1693 | + |
| 1694 | + /** |
| 1695 | + * Rename the file |
| 1696 | + * |
| 1697 | + * THIS MAY NOT WORK ON ALL WIKIS. for instance, on Commons, it may be that only admins can move pages. This is another example of how |
| 1698 | + * we need an "incomplete" upload status |
| 1699 | + * we are presuming this File page is brand new, so let's not bother with the whole redirection deal. ('noredirect') |
| 1700 | + * |
| 1701 | + * use _this.ignoreWarningsInput (if it exists) to check if we can blithely move the file or if we have a problem if there |
| 1702 | + * is a file by that name already there |
| 1703 | + * |
| 1704 | + * @param filename to rename this file to |
| 1705 | + */ |
| 1706 | + rename: function( title, endCallback ) { |
| 1707 | + var _this = this; |
| 1708 | + mw.log("renaming!"); |
| 1709 | + params = { |
| 1710 | + action: 'move', |
| 1711 | + from: _this.upload.title, |
| 1712 | + to: title, |
| 1713 | + reason: "User edited page with " + mw.UploadWizard.userAgent, |
| 1714 | + movetalk: '', |
| 1715 | + noredirect: '', // presume it's too new |
| 1716 | + token: mw.getConfig('token') |
| 1717 | + }; |
| 1718 | + mw.log(params); |
| 1719 | + // despite the name, getJSON magically changes this into a POST request (it has a list of methods and what they require). |
| 1720 | + mw.getJSON( params, function( data ) { |
| 1721 | + // handle errors later |
| 1722 | + // possible error data: { code = 'missingtitle' } -- orig filename not there |
| 1723 | + // and many more |
| 1724 | + |
| 1725 | + // which should match our request. |
| 1726 | + // we should update the current upload filename |
| 1727 | + // then call the uploadwizard with our progress |
| 1728 | + |
| 1729 | + // success is |
| 1730 | + // { move = { from : ..., reason : ..., redirectcreated : ..., to : .... } |
| 1731 | + if (data !== undefined && data.move !== undefined && data.move.to !== undefined) { |
| 1732 | + _this.upload.title = data.move.to; |
| 1733 | + _this.refreshImageInfo( _this.upload, _this.upload.title, endCallback ); |
| 1734 | + } |
| 1735 | + } ); |
| 1736 | + }, |
| 1737 | + |
| 1738 | + /** |
| 1739 | + * Get new image info, for instance, after we renamed an image |
| 1740 | + * |
| 1741 | + * @param upload an UploadWizardUpload object |
| 1742 | + * @param title title to look up remotely |
| 1743 | + * @param endCallback execute upon completion |
| 1744 | + */ |
| 1745 | + refreshImageInfo: function( upload, title, endCallback ) { |
| 1746 | + var params = { |
| 1747 | + 'titles': title, |
| 1748 | + 'prop': 'imageinfo', |
| 1749 | + 'iiprop': 'timestamp|url|user|size|sha1|mime|metadata' |
| 1750 | + }; |
| 1751 | + // XXX timeout callback? |
| 1752 | + mw.getJSON( params, function( data ) { |
| 1753 | + if ( data && data.query && data.query.pages ) { |
| 1754 | + if ( ! data.query.pages[-1] ) { |
| 1755 | + for ( var page_id in data.query.pages ) { |
| 1756 | + var page = data.query.pages[ page_id ]; |
| 1757 | + if ( ! page.imageinfo ) { |
| 1758 | + // not found? error |
| 1759 | + } else { |
| 1760 | + upload.extractImageInfo( page.imageinfo[0] ); |
| 1761 | + } |
| 1762 | + } |
| 1763 | + } |
| 1764 | + } |
| 1765 | + endCallback(); |
| 1766 | + } ); |
| 1767 | + }, |
| 1768 | + |
| 1769 | + showProgress: function() { |
| 1770 | + var _this = this; |
| 1771 | + _this.div.mask(); |
| 1772 | + // XXX spinnerize |
| 1773 | + _this.upload.detailsProgress = 1.0; |
| 1774 | + }, |
| 1775 | + |
| 1776 | + completeDetailsSubmission: function() { |
| 1777 | + var _this = this; |
| 1778 | + _this.upload.state = 'complete'; |
| 1779 | + // XXX de-spinnerize |
| 1780 | + _this.div.unmask(); |
| 1781 | + } |
| 1782 | + |
| 1783 | +}; |
| 1784 | + |
| 1785 | + |
| 1786 | +/** |
| 1787 | + * Object that reperesents the entire multi-step Upload Wizard |
| 1788 | + */ |
| 1789 | +mw.UploadWizard = function() { |
| 1790 | + |
| 1791 | + this.uploads = []; |
| 1792 | + |
| 1793 | +}; |
| 1794 | + |
| 1795 | + |
| 1796 | +mw.UploadWizard.userAgent = "UploadWizard (alpha) on " + $j.browser.name + " " + $j.browser.version; |
| 1797 | + |
| 1798 | + |
| 1799 | +mw.UploadWizard.prototype = { |
| 1800 | + maxUploads: 10, // XXX get this from config |
| 1801 | + maxSimultaneousUploads: 2, // XXX get this from config |
| 1802 | + stepNames: [ 'file', 'deeds', 'details', 'thanks' ], |
| 1803 | + currentStepName: undefined, |
| 1804 | + |
| 1805 | + /* |
| 1806 | + // list possible upload handlers in order of preference |
| 1807 | + // these should all be in the mw.* namespace |
| 1808 | + // hardcoded for now. maybe some registry system might work later, like, all |
| 1809 | + // things which subclass off of UploadHandler |
| 1810 | + uploadHandlers: [ |
| 1811 | + 'FirefoggUploadHandler', |
| 1812 | + 'XhrUploadHandler', |
| 1813 | + 'ApiIframeUploadHandler', |
| 1814 | + 'SimpleUploadHandler', |
| 1815 | + 'NullUploadHandler' |
| 1816 | + ], |
| 1817 | + |
| 1818 | + * We can use various UploadHandlers based on the browser's capabilities. Let's pick one. |
| 1819 | + * For example, the ApiUploadHandler should work just about everywhere, but XhrUploadHandler |
| 1820 | + * allows for more fine-grained upload progress |
| 1821 | + * @return valid JS upload handler class constructor function |
| 1822 | + getUploadHandlerClass: function() { |
| 1823 | + // return mw.MockUploadHandler; |
| 1824 | + return mw.ApiUploadHandler; |
| 1825 | + var _this = this; |
| 1826 | + for ( var i = 0; i < uploadHandlers.length; i++ ) { |
| 1827 | + var klass = mw[uploadHandlers[i]]; |
| 1828 | + if ( klass != undefined && klass.canRun( this.config )) { |
| 1829 | + return klass; |
| 1830 | + } |
| 1831 | + } |
| 1832 | + // this should never happen; NullUploadHandler should always work |
| 1833 | + return null; |
| 1834 | + }, |
| 1835 | + */ |
| 1836 | + |
| 1837 | + reset: function() { |
| 1838 | + var _this = this; |
| 1839 | + $j.each( _this.uploads, function( i, upload ) { |
| 1840 | + _this.removeUpload( upload ); |
| 1841 | + } ); |
| 1842 | + _this.uploads = []; |
| 1843 | + }, |
| 1844 | + |
| 1845 | + |
| 1846 | + /** |
| 1847 | + * create the basic interface to make an upload in this div |
| 1848 | + * @param div The div in the DOM to put all of this into. |
| 1849 | + */ |
| 1850 | + createInterface: function( selector ) { |
| 1851 | + var _this = this; |
| 1852 | + var div = $j( selector ).get(0); |
| 1853 | + div.innerHTML = |
| 1854 | + |
| 1855 | + '<div id="mwe-upwiz-steparrows" class="ui-helper-clearfix">' |
| 1856 | + + '<ul>' |
| 1857 | + + '<li id="mwe-upwiz-step-file"><span class="mwe-arrow-text">' + gM('mwe-upwiz-step-file') + '<span class="mwe-arrow"/></span></span></li>' |
| 1858 | + + '<li id="mwe-upwiz-step-deeds"><span class="mwe-arrow-text">' + gM('mwe-upwiz-step-deeds') + '<span class="mwe-arrow"/></span></span></li>' |
| 1859 | + + '<li id="mwe-upwiz-step-details"><span class="mwe-arrow-text">' + gM('mwe-upwiz-step-details') + '<span class="mwe-arrow"/></span></span></li>' |
| 1860 | + + '<li id="mwe-upwiz-step-thanks"><span class="mwe-arrow-text">' + gM('mwe-upwiz-step-thanks') + '<span class="mwe-arrow"/></span></span></li>' |
| 1861 | + + '</ul>' |
| 1862 | + + '</div>' |
| 1863 | + |
| 1864 | + + '<div id="mwe-upwiz-content">' |
| 1865 | + |
| 1866 | + + '<div class="mwe-upwiz-stepdiv ui-helper-clearfix" id="mwe-upwiz-stepdiv-file">' |
| 1867 | + + '<div id="mwe-upwiz-intro">' + gM('mwe-upwiz-intro') + '</div>' |
| 1868 | + + '<div id="mwe-upwiz-files">' |
| 1869 | + + '<div class="shim" style="height: 120px"></div>' |
| 1870 | + + '<div id="mwe-upwiz-upload-ctrls" class="mwe-upwiz-file">' |
| 1871 | + + '<div id="mwe-upwiz-add-file-container" class="mwe-upwiz-add-files-0">' |
| 1872 | + + '<a id="mwe-upwiz-add-file">' + gM("mwe-upwiz-add-file-0") + '</a>' |
| 1873 | + + '</div>' |
| 1874 | + + '<div id="proceed" class="mwe-upwiz-file-indicator" style="display: none;">' |
| 1875 | + + '<button id="mwe-upwiz-upload-ctrl" disabled="disabled">' + gM("mwe-upwiz-upload") + '</button>' |
| 1876 | + + '</div>' |
| 1877 | + + '</div>' |
| 1878 | + + '<div id="mwe-upwiz-progress" class="ui-helper-clearfix"></div>' |
| 1879 | + + '</div>' |
| 1880 | + + '<div class="mwe-upwiz-buttons"/>' |
| 1881 | + + '<button class="mwe-upwiz-button-next" />' |
| 1882 | + + '</div>' |
| 1883 | + + '</div>' |
| 1884 | + |
| 1885 | + + '<div class="mwe-upwiz-stepdiv" id="mwe-upwiz-stepdiv-deeds">' |
| 1886 | + + '<div id="mwe-upwiz-deeds-intro"></div>' |
| 1887 | + + '<div id="mwe-upwiz-deeds-thumbnails" class="ui-helper-clearfix"></div>' |
| 1888 | + + '<div id="mwe-upwiz-deeds" class="ui-helper-clearfix"></div>' |
| 1889 | + + '<div id="mwe-upwiz-deeds-custom" class="ui-helper-clearfix"></div>' |
| 1890 | + + '<div class="mwe-upwiz-buttons"/>' |
| 1891 | + + '<button class="mwe-upwiz-button-next" />' |
| 1892 | + + '</div>' |
| 1893 | + + '</div>' |
| 1894 | + |
| 1895 | + + '<div class="mwe-upwiz-stepdiv" id="mwe-upwiz-stepdiv-details">' |
| 1896 | + + '<div id="mwe-upwiz-macro">' |
| 1897 | + + '<div id="mwe-upwiz-macro-progress" class="ui-helper-clearfix"></div>' |
| 1898 | + + '<div id="mwe-upwiz-macro-choice">' |
| 1899 | + + '<div>' + gM( 'mwe-upwiz-details-intro' ) + '</div>' |
| 1900 | + + '</div>' |
| 1901 | + + '<div id="mwe-upwiz-macro-files"></div>' |
| 1902 | + + '</div>' |
| 1903 | + + '<div class="mwe-upwiz-buttons"/>' |
| 1904 | + + '<button class="mwe-upwiz-button-next" />' |
| 1905 | + + '</div>' |
| 1906 | + + '</div>' |
| 1907 | + |
| 1908 | + + '<div class="mwe-upwiz-stepdiv" id="mwe-upwiz-stepdiv-thanks">' |
| 1909 | + + '<div id="mwe-upwiz-thanks"></div>' |
| 1910 | + + '<div class="mwe-upwiz-buttons"/>' |
| 1911 | + + '<button class="mwe-upwiz-button-begin"></button>' |
| 1912 | + + '<br/><button class="mwe-upwiz-button-home"></button>' |
| 1913 | + + '</div>' |
| 1914 | + + '</div>' |
| 1915 | + |
| 1916 | + + '</div>' |
| 1917 | + |
| 1918 | + + '<div class="mwe-upwiz-clearing"></div>'; |
| 1919 | + |
| 1920 | + |
| 1921 | + $j( '.mwe-upwiz-button-home' ) |
| 1922 | + .append( gM( 'mwe-upwiz-home' ) ) |
| 1923 | + .click( function() { window.location.href = '/' } ); |
| 1924 | + |
| 1925 | + $j( '.mwe-upwiz-button-begin' ) |
| 1926 | + .append( gM( 'mwe-upwiz-upload-another' ) ) |
| 1927 | + .click( _this.reset() ); |
| 1928 | + |
| 1929 | + $j( '.mwe-upwiz-button-next' ) |
| 1930 | + .append( gM( 'mwe-upwiz-next' ) ) |
| 1931 | + |
| 1932 | + // within FILE step div |
| 1933 | + $j('#mwe-upwiz-upload-ctrl').click( function() { |
| 1934 | + _this.removeEmptyUploads(); |
| 1935 | + _this.startUploads(); |
| 1936 | + } ); |
| 1937 | + |
| 1938 | + $j( '#mwe-upwiz-stepdiv-file .mwe-upwiz-button-next').click( function() { |
| 1939 | + _this.moveToStep( 'deeds' ); |
| 1940 | + } ); |
| 1941 | + |
| 1942 | + // DEEDS div |
| 1943 | + _this.deedChooser = new mw.UploadWizardDeedChooser( '#mwe-upwiz-deeds' ); |
| 1944 | + |
| 1945 | + var customDeed = $j.extend( new mw.UploadWizardDeed(), { |
| 1946 | + isReady: function() { return true; }, |
| 1947 | + setQuantity: function( n ) { return; } |
| 1948 | + } ); |
| 1949 | + |
| 1950 | + $j( '#mwe-upwiz-deeds-custom' ).append( |
| 1951 | + $j( '<div id="mwe-upwiz-deeds-later" />' ) |
| 1952 | + .hide() |
| 1953 | + .append( $j( '<label>' ) |
| 1954 | + .append( |
| 1955 | + $j( '<input />') |
| 1956 | + .attr( { type: 'checkbox', value: 'deeds-later' } ) |
| 1957 | + .addClass( 'mwe-accept-deed' ) |
| 1958 | + .click( function() { |
| 1959 | + if ( $j( this ).is( ':checked' ) ) { |
| 1960 | + _this.deedChooser.showDeedChoice(); |
| 1961 | + _this.deedChooser.choose( customDeed ); |
| 1962 | + //_this.deedChooser.trigger( 'isReady' ); |
| 1963 | + } else { |
| 1964 | + _this.deedChooser.choose( mw.UploadWizardNullDeed ); |
| 1965 | + } |
| 1966 | + } ), |
| 1967 | + $j( '<span />').append( gM( 'mwe-upwiz-deeds-later' ) ) |
| 1968 | + ) |
| 1969 | + ) |
| 1970 | + ); |
| 1971 | + |
| 1972 | + $j( _this.deedChooser ).bind( 'setQuantityEvent', function() { |
| 1973 | + mw.log( "checking this deedchooser count " + _this.deedChooser.count ); |
| 1974 | + if ( _this.deedChooser.count > 1 ) { |
| 1975 | + $j( '#mwe-upwiz-deeds-later' ).show(); |
| 1976 | + } else { |
| 1977 | + $j( '#mwe-upwiz-deeds-later' ).hide(); |
| 1978 | + } |
| 1979 | + } ); |
| 1980 | + |
| 1981 | + $j( '#mwe-upwiz-deeds-intro' ).html( gM( 'mwe-upwiz-deeds-intro' ) ); |
| 1982 | + |
| 1983 | + $j( '#mwe-upwiz-stepdiv-deeds .mwe-upwiz-button-next').click( function() { |
| 1984 | + _this.moveToStep('details'); |
| 1985 | + } ); |
| 1986 | + |
| 1987 | + // XXX perhaps we should defer this until we click next |
| 1988 | + $j( _this.deedChooser ).bind( 'chooseDeed', function() { |
| 1989 | + var isCustom = ( _this.deedChooser.deed === customDeed ); |
| 1990 | + $j.each( _this.uploads, function( i, upload ) { |
| 1991 | + if (isCustom) { |
| 1992 | + upload.details.useCustomDeedChooser(); |
| 1993 | + } else { |
| 1994 | + upload.details.useDeedChooser( _this.deedChooser ); |
| 1995 | + } |
| 1996 | + } ); |
| 1997 | + } ); |
| 1998 | + |
| 1999 | + // XXX perhaps we should defer this until we click next |
| 2000 | + $j( _this.deedChooser ).bind( 'chooseNullDeed', function() { |
| 2001 | + $j.each( _this.uploads, function( i, upload ) { |
| 2002 | + upload.details.deedChooser = _this.deedChooser; |
| 2003 | + } ); |
| 2004 | + // $j( '#mwe-upwiz-stepdiv-deeds' ).disableNextButton(); |
| 2005 | + } ); |
| 2006 | + |
| 2007 | + /* |
| 2008 | + // XXX figure out some way to make them ready / unready |
| 2009 | + // timeout after input, check for readiness, then trigger event? |
| 2010 | + $j( _this.deedChooser ).bind( 'isReady', function() { |
| 2011 | + $j( '#mwe-upwiz-stepdiv-deeds' ).enableNextButton(); |
| 2012 | + } ); |
| 2013 | + |
| 2014 | + $j( _this.deedChooser ).bind( 'isNotReady', function() { |
| 2015 | + $j( '#mwe-upwiz-stepdiv-deeds' ).disableNextButton(); |
| 2016 | + } ); |
| 2017 | +*/ |
| 2018 | + |
| 2019 | + |
| 2020 | + // DETAILS div |
| 2021 | + $j( '#mwe-upwiz-stepdiv-details .mwe-upwiz-button-next' ).click( function() { |
| 2022 | + _this.detailsSubmit( function() { |
| 2023 | + _this.prefillThanksPage(); |
| 2024 | + _this.moveToStep('thanks'); |
| 2025 | + } ); |
| 2026 | + } ); |
| 2027 | + |
| 2028 | + // add one to start |
| 2029 | + var upload = _this.newUpload( '#mwe-upwiz-add-file' ); |
| 2030 | + |
| 2031 | + // "select" the first step - highlight, make it visible, hide all others |
| 2032 | + _this.moveToStep( 'file', function() { |
| 2033 | + // XXX moving the file input doesn't seem to work at this point; we get its old position before |
| 2034 | + // CSS is applied. Hence, using a timeout. |
| 2035 | + // XXX using a timeout is lame, are there other options? |
| 2036 | + // XXX Trevor suggests that using addClass() may queue stuff unnecessarily; use 'concrete' HTML |
| 2037 | + setTimeout( function() { |
| 2038 | + upload.ui.moveFileInputToCover( '#mwe-upwiz-add-file' ); |
| 2039 | + }, 300 ); |
| 2040 | + } ); |
| 2041 | + |
| 2042 | + }, |
| 2043 | + |
| 2044 | + /** |
| 2045 | + * Advance one "step" in the wizard interface. |
| 2046 | + * It is assumed that the previous step to the current one was selected. |
| 2047 | + * We do not hide the tabs because this messes up certain calculations we'd like to make about dimensions, while elements are not |
| 2048 | + * on screen. So instead we make the tabs zero height and, in CSS, they are already overflow hidden |
| 2049 | + * @param selectedStepName |
| 2050 | + * @param callback to do after layout is ready? |
| 2051 | + */ |
| 2052 | + moveToStep: function( selectedStepName, callback ) { |
| 2053 | + var _this = this; |
| 2054 | + $j.each( _this.stepNames, function(i, stepName) { |
| 2055 | + |
| 2056 | + // the step indicator |
| 2057 | + var step = $j( '#mwe-upwiz-step-' + stepName ); |
| 2058 | + |
| 2059 | + // the step's contents |
| 2060 | + var stepDiv = $j( '#mwe-upwiz-stepdiv-' + stepName ); |
| 2061 | + |
| 2062 | + if ( _this.currentStepName == stepName ) { |
| 2063 | + // we hide the old stepDivs because we are afraid of some z-index elements that may interfere with later tabs |
| 2064 | + // this will break if we ever allow people to page back and forth. |
| 2065 | + step.hide( 1000 ); |
| 2066 | + stepDiv.hide(); |
| 2067 | + } else if ( selectedStepName == stepName ) { |
| 2068 | + stepDiv.maskSafeShow(); |
| 2069 | + step.addClass( 'mwe-upwiz-step-highlight' ); |
| 2070 | + } else { |
| 2071 | + // it's neither the formerly active nor the newly active one, so don't show it |
| 2072 | + // we don't use hide() because we want to manipulate elements within future tabs, and calculate their dimensions. |
| 2073 | + // stepDiv.maskSafeHide(); |
| 2074 | + } |
| 2075 | + } ); |
| 2076 | + |
| 2077 | + _this.currentStepName = selectedStepName; |
| 2078 | + |
| 2079 | + $j.each( _this.uploads, function(i, upload) { |
| 2080 | + upload.state = selectedStepName; |
| 2081 | + } ); |
| 2082 | + |
| 2083 | + if ( callback ) { |
| 2084 | + callback(); |
| 2085 | + } |
| 2086 | + }, |
| 2087 | + |
| 2088 | + /** |
| 2089 | + * add an Upload |
| 2090 | + * we create the upload interface, a handler to transport it to the server, |
| 2091 | + * and UI for the upload itself and the "details" at the second step of the wizard. |
| 2092 | + * we don't yet add it to the list of uploads; that only happens when it gets a real file. |
| 2093 | + * @return boolean success |
| 2094 | + */ |
| 2095 | + newUpload: function() { |
| 2096 | + var _this = this; |
| 2097 | + if ( _this.uploads.length == _this.maxUploads ) { |
| 2098 | + return false; |
| 2099 | + } |
| 2100 | + |
| 2101 | + var upload = new mw.UploadWizardUpload( _this, '#mwe-upwiz-files' ); |
| 2102 | + _this.uploadToAdd = upload; |
| 2103 | + |
| 2104 | + upload.ui.moveFileInputToCover( '#mwe-upwiz-add-file' ); |
| 2105 | + $j( upload ).bind( 'filenameAccepted', function(e) { _this.updateFileCounts(); e.stopPropagation(); } ); |
| 2106 | + $j( upload ).bind( 'removeUpload', function(e) { _this.removeUpload( upload ); e.stopPropagation(); } ); |
| 2107 | + $j( upload ).bind( 'filled', function(e) { |
| 2108 | + _this.newUpload(); |
| 2109 | + _this.setUploadFilled(upload); |
| 2110 | + e.stopPropagation(); |
| 2111 | + } ); |
| 2112 | + // XXX bind to some error state |
| 2113 | + |
| 2114 | + |
| 2115 | + return upload; |
| 2116 | + }, |
| 2117 | + |
| 2118 | + /** |
| 2119 | + * When an upload is filled with a real file, accept it in the wizard's list of uploads |
| 2120 | + * and set up some other interfaces |
| 2121 | + * @param UploadWizardUpload |
| 2122 | + */ |
| 2123 | + setUploadFilled: function( upload ) { |
| 2124 | + var _this = this; |
| 2125 | + |
| 2126 | + // XXX check if it has a file? |
| 2127 | + |
| 2128 | + _this.uploads.push( upload ); |
| 2129 | + _this.updateFileCounts(); |
| 2130 | + |
| 2131 | + upload.deedPreview = new mw.UploadWizardDeedPreview( upload ); |
| 2132 | + |
| 2133 | + // this will modify upload, so it has a .deedChooser property. |
| 2134 | + // We use a method to so we notify deedChooser that it has a new upload -- interface |
| 2135 | + // will change based on quantity etc. Maybe we could be clever with bind here. |
| 2136 | + _this.deedChooser.attach( upload ); |
| 2137 | + |
| 2138 | + // set up details |
| 2139 | + upload.details = new mw.UploadWizardDetails( upload, $j( '#mwe-upwiz-macro-files' )); |
| 2140 | + }, |
| 2141 | + |
| 2142 | + |
| 2143 | + /** |
| 2144 | + * Remove an upload from our array of uploads, and the HTML UI |
| 2145 | + * We can remove the HTML UI directly, as jquery will just get the parent. |
| 2146 | + * We need to grep through the array of uploads, since we don't know the current index. |
| 2147 | + * We need to update file counts for obvious reasons. |
| 2148 | + * |
| 2149 | + * @param upload |
| 2150 | + */ |
| 2151 | + removeUpload: function( upload ) { |
| 2152 | + var _this = this; |
| 2153 | + $j( upload ).unbind(); // everything |
| 2154 | + if ( upload.deedChooser ) { |
| 2155 | + upload.deedChooser.detach( upload ); |
| 2156 | + } |
| 2157 | + upload.details.div.remove(); |
| 2158 | + upload.thanksDiv.remove(); |
| 2159 | + |
| 2160 | + mw.UploadWizardUtil.removeItem( _this.uploads, upload ); |
| 2161 | + _this.updateFileCounts(); |
| 2162 | + }, |
| 2163 | + |
| 2164 | + /** |
| 2165 | + * This is useful to clean out unused upload file inputs if the user hits GO. |
| 2166 | + * We are using a second array to iterate, because we will be splicing the main one, _this.uploads |
| 2167 | + */ |
| 2168 | + removeEmptyUploads: function() { |
| 2169 | + var _this = this; |
| 2170 | + var toRemove = []; |
| 2171 | + |
| 2172 | + for ( var i = 0; i < _this.uploads.length; i++ ) { |
| 2173 | + if ( _this.uploads[i].ui.fileInputCtrl.value == "" ) { |
| 2174 | + toRemove.push( _this.uploads[i] ); |
| 2175 | + } |
| 2176 | + } |
| 2177 | + |
| 2178 | + for ( var i = 0; i < toRemove.length; i++ ) { |
| 2179 | + toRemove[i].remove(); |
| 2180 | + } |
| 2181 | + }, |
| 2182 | + |
| 2183 | + /** |
| 2184 | + * Manage transitioning all of our uploads from one state to another -- like from "new" to "uploaded". |
| 2185 | + * Shows progress bar with estimated time remaining. |
| 2186 | + * |
| 2187 | + * There are too many args here. How to fix? |
| 2188 | + * This is starting to feel like an object. |
| 2189 | + * |
| 2190 | + * @param beginState what state the upload should be in before starting. |
| 2191 | + * @param progressState the state to set the upload to while it's doing whatever |
| 2192 | + * @param endState the state to set the upload to after it's done whatever |
| 2193 | + * @param progressProperty the property on the upload showing current progress of whatever |
| 2194 | + * @param weightProperty the property on the upload giving how heavy to weight this item in total progress calculation |
| 2195 | + * @param starter function, taking single argument (upload) which starts the process we're interested in |
| 2196 | + * @param progressBarSelector where to put the progress bar |
| 2197 | + * @param endCallback function to call when all uploads are in the end state. |
| 2198 | + */ |
| 2199 | + makeTransitioner: function( beginState, |
| 2200 | + progressState, |
| 2201 | + endState, |
| 2202 | + progressProperty, |
| 2203 | + weightProperty, |
| 2204 | + progressBarSelector, |
| 2205 | + progressBarText, |
| 2206 | + starter, |
| 2207 | + endCallback ) { |
| 2208 | + |
| 2209 | + var wizard = this; |
| 2210 | + |
| 2211 | + var totalWeight = 0.0; |
| 2212 | + $j.each( wizard.uploads, function( i, upload ) { |
| 2213 | + totalWeight += upload[weightProperty]; |
| 2214 | + } ); |
| 2215 | + var totalCount = wizard.uploads.length; |
| 2216 | + |
| 2217 | + var progressBar = new mw.ProgressBar( progressBarSelector, progressBarText ); |
| 2218 | + progressBar.setTotal( totalCount ); |
| 2219 | + |
| 2220 | + transitioner = function() { |
| 2221 | + var fraction = 0.0; |
| 2222 | + var uploadsToStart = wizard.maxSimultaneousUploads; |
| 2223 | + var endStateCount = 0; |
| 2224 | + $j.each( wizard.uploads, function(i, upload) { |
| 2225 | + if ( upload.state == endState ) { |
| 2226 | + endStateCount++; |
| 2227 | + } else if ( upload.state == progressState ) { |
| 2228 | + uploadsToStart--; |
| 2229 | + } else if ( ( upload.state == beginState ) && ( uploadsToStart > 0 ) ) { |
| 2230 | + starter( upload ); |
| 2231 | + uploadsToStart--; |
| 2232 | + } |
| 2233 | + if (upload[progressProperty] !== undefined) { |
| 2234 | + fraction += upload[progressProperty] * ( upload[weightProperty] / totalWeight ); |
| 2235 | + } |
| 2236 | + } ); |
| 2237 | + |
| 2238 | + // perhaps this could be collected into a single progressbar obj |
| 2239 | + progressBar.showProgress( fraction ); |
| 2240 | + progressBar.showCount( endStateCount ); |
| 2241 | + |
| 2242 | + // build in a little delay even for the end state, so user can see progress bar in a complete state. |
| 2243 | + var nextAction = (endStateCount == totalCount) ? endCallback : transitioner; |
| 2244 | + setTimeout( nextAction, wizard.transitionerDelay ); |
| 2245 | + }; |
| 2246 | + |
| 2247 | + progressBar.setBeginTime(); |
| 2248 | + transitioner(); |
| 2249 | + }, |
| 2250 | + |
| 2251 | + transitionerDelay: 300, // milliseconds |
| 2252 | + |
| 2253 | + |
| 2254 | + |
| 2255 | + /** |
| 2256 | + * Kick off the upload processes. |
| 2257 | + * Does some precalculations, changes the interface to be less mutable, moves the uploads to a queue, |
| 2258 | + * and kicks off a thread which will take from the queue. |
| 2259 | + */ |
| 2260 | + startUploads: function( finishedCallback ) { |
| 2261 | + var _this = this; |
| 2262 | + // remove the upload button, and the add file button |
| 2263 | + $j( '#mwe-upwiz-upload-ctrl' ).hide(); |
| 2264 | + $j( '#mwe-upwiz-add-file' ).hide(); |
| 2265 | + |
| 2266 | + var allowCloseWindow = $j().preventCloseWindow( { |
| 2267 | + message: gM( 'mwe-prevent-close') |
| 2268 | + } ); |
| 2269 | + |
| 2270 | + |
| 2271 | + // remove ability to change files |
| 2272 | + // ideally also hide the "button"... but then we require styleable file input CSS trickery |
| 2273 | + // although, we COULD do this just for files already in progress... |
| 2274 | + |
| 2275 | + // it might be interesting to just make this creational -- attach it to the dom element representing |
| 2276 | + // the progress bar and elapsed time |
| 2277 | + _this.makeTransitioner( |
| 2278 | + 'new', |
| 2279 | + 'transporting', |
| 2280 | + 'transported', |
| 2281 | + 'transportProgress', |
| 2282 | + 'transportWeight', |
| 2283 | + '#mwe-upwiz-progress', |
| 2284 | + gM( 'mwe-upwiz-uploading' ), |
| 2285 | + function( upload ) { |
| 2286 | + upload.start(); |
| 2287 | + }, |
| 2288 | + function() { |
| 2289 | + allowCloseWindow(); |
| 2290 | + $j().notify( gM( 'mwe-upwiz-files-complete' ) ); |
| 2291 | + $j( '#mwe-upwiz-stepdiv-file' ).enableNextButton(); |
| 2292 | + } |
| 2293 | + ); |
| 2294 | + }, |
| 2295 | + |
| 2296 | + |
| 2297 | + |
| 2298 | + /** |
| 2299 | + * Occurs whenever we need to update the interface based on how many files there are |
| 2300 | + * Thhere is an uncounted upload, waiting to be used, which has a fileInput which covers the |
| 2301 | + * "add an upload" button. This is absolutely positioned, so it needs to be moved if another upload was removed. |
| 2302 | + * The uncounted upload is also styled differently between the zero and n files cases |
| 2303 | + */ |
| 2304 | + updateFileCounts: function() { |
| 2305 | + var _this = this; |
| 2306 | + |
| 2307 | + if ( _this.uploads.length ) { |
| 2308 | + $j( '#mwe-upwiz-upload-ctrl' ).removeAttr( 'disabled' ); |
| 2309 | + $j( '#proceed' ).show(); |
| 2310 | + $j( '#mwe-upwiz-add-file' ).html( gM( 'mwe-upwiz-add-file-n' ) ); |
| 2311 | + $j( '#mwe-upwiz-add-file-container' ).removeClass('mwe-upwiz-add-files-0'); |
| 2312 | + $j( '#mwe-upwiz-add-file-container' ).addClass('mwe-upwiz-add-files-n'); |
| 2313 | + } else { |
| 2314 | + $j( '#mwe-upwiz-upload-ctrl' ).attr( 'disabled', 'disabled' ); |
| 2315 | + $j( '#proceed' ).hide(); |
| 2316 | + $j( '#mwe-upwiz-add-file' ).html( gM( 'mwe-upwiz-add-file-0' ) ); |
| 2317 | + $j( '#mwe-upwiz-add-file-container' ).addClass('mwe-upwiz-add-files-0'); |
| 2318 | + $j( '#mwe-upwiz-add-file-container' ).removeClass('mwe-upwiz-add-files-n'); |
| 2319 | + } |
| 2320 | + |
| 2321 | + if ( _this.uploads.length < _this.maxUploads ) { |
| 2322 | + $j( '#mwe-upwiz-add-file' ).removeAttr( 'disabled' ); |
| 2323 | + $j( _this.uploadToAdd.ui.div ).show(); |
| 2324 | + _this.uploadToAdd.ui.moveFileInputToCover( '#mwe-upwiz-add-file' ); |
| 2325 | + } else { |
| 2326 | + $j( '#mwe-upwiz-add-file' ).attr( 'disabled', true ); |
| 2327 | + $j( _this.uploadToAdd.ui.div ).hide(); |
| 2328 | + } |
| 2329 | + |
| 2330 | + |
| 2331 | + }, |
| 2332 | + |
| 2333 | + /** |
| 2334 | + * Submit all edited details and other metadata |
| 2335 | + * Works just like startUploads -- parallel simultaneous submits with progress bar. |
| 2336 | + */ |
| 2337 | + detailsSubmit: function( endCallback ) { |
| 2338 | + var _this = this; |
| 2339 | + // some details blocks cannot be submitted (for instance, identical file hash) |
| 2340 | + _this.removeBlockedDetails(); |
| 2341 | + |
| 2342 | + // check that it's even possible to submit all |
| 2343 | + |
| 2344 | + // remove all controls |
| 2345 | + //$j( '#mwe-upwiz-upload-ctrl' ).hide(); |
| 2346 | + //$j( '#mwe-upwiz-add-file' ).hide(); |
| 2347 | + |
| 2348 | + // remove ability to edit details |
| 2349 | + // maybe add some sort of greyish semi-opaque thing |
| 2350 | + |
| 2351 | + // add the upload progress bar, with ETA |
| 2352 | + // add in the upload count |
| 2353 | + _this.makeTransitioner( |
| 2354 | + 'details', |
| 2355 | + 'submitting-details', |
| 2356 | + 'complete', |
| 2357 | + 'detailsProgress', |
| 2358 | + 'detailsWeight', |
| 2359 | + '#mwe-upwiz-macro-progress', |
| 2360 | + gM( 'mwe-upwiz-editing' ), |
| 2361 | + function( upload ) { |
| 2362 | + upload.details.submit(); |
| 2363 | + }, |
| 2364 | + endCallback |
| 2365 | + ); |
| 2366 | + }, |
| 2367 | + |
| 2368 | + /** |
| 2369 | + * Removes(?) details that we can't edit for whatever reason -- might just advance them to a different state? |
| 2370 | + */ |
| 2371 | + removeBlockedDetails: function() { |
| 2372 | + |
| 2373 | + }, |
| 2374 | + |
| 2375 | + |
| 2376 | + prefillThanksPage: function() { |
| 2377 | + var _this = this; |
| 2378 | + |
| 2379 | + $j( '#mwe-upwiz-thanks' ).append( $j( '<p>' ).append( gM( 'mwe-upwiz-thanks-intro' ) ) ); |
| 2380 | + var width = mw.getConfig( 'thumbnailWidth' ); |
| 2381 | + |
| 2382 | + $j.each( _this.uploads, function(i, upload) { |
| 2383 | + var thanksDiv = $j( '<div class="mwe-upwiz-thanks ui-helper-clearfix" />' ); |
| 2384 | + _this.thanksDiv = thanksDiv; |
| 2385 | + |
| 2386 | + var thumbnailDiv = $j( '<div></div>' ).addClass( 'mwe-upwiz-thumbnail' ); |
| 2387 | + thanksDiv.append( thumbnailDiv ); |
| 2388 | + upload.setThumbnail( thumbnailDiv ); |
| 2389 | + |
| 2390 | + //var thumbTitle = upload.title.replace(/^File/, 'Image'); // XXX is this really necessary? |
| 2391 | + var thumbWikiText = "[[" + upload.title + "|thumb]]"; |
| 2392 | + |
| 2393 | + thanksDiv.append( |
| 2394 | + $j( '<div class="mwe-upwiz-data"></div>' ) |
| 2395 | + .append( |
| 2396 | + $j('<p/>').append( |
| 2397 | + gM( 'mwe-upwiz-thanks-link', |
| 2398 | + $j( '<a />' ) |
| 2399 | + .attr( { target: '_new', |
| 2400 | + href: upload.imageinfo.descriptionurl } ) |
| 2401 | + .text( upload.title ) |
| 2402 | + ) |
| 2403 | + ), |
| 2404 | + $j('<p/>').append( |
| 2405 | + gM( 'mwe-upwiz-thanks-wikitext' ), |
| 2406 | + $j( '<br />' ), |
| 2407 | + $j( '<textarea class="mwe-long-textarea" rows="1"/>' ) |
| 2408 | + .growTextArea() |
| 2409 | + .append( thumbWikiText ) |
| 2410 | + .resizeIfNeeded() |
| 2411 | + ), |
| 2412 | + $j('<p/>').append( |
| 2413 | + gM( 'mwe-upwiz-thanks-url' ), |
| 2414 | + $j( '<br />' ), |
| 2415 | + $j( '<textarea class="mwe-long-textarea" rows="1"/>' ) |
| 2416 | + .growTextArea() |
| 2417 | + .append( upload.imageinfo.descriptionurl ) |
| 2418 | + .resizeIfNeeded() |
| 2419 | + ) |
| 2420 | + ) |
| 2421 | + ); |
| 2422 | + |
| 2423 | + $j( '#mwe-upwiz-thanks' ).append( thanksDiv ); |
| 2424 | + } ); |
| 2425 | + }, |
| 2426 | + |
| 2427 | + /** |
| 2428 | + * |
| 2429 | + */ |
| 2430 | + pause: function() { |
| 2431 | + |
| 2432 | + }, |
| 2433 | + |
| 2434 | + /** |
| 2435 | + * |
| 2436 | + */ |
| 2437 | + stop: function() { |
| 2438 | + |
| 2439 | + } |
| 2440 | +}; |
| 2441 | + |
| 2442 | + |
| 2443 | +mw.UploadWizardDeedPreview = function(upload) { |
| 2444 | + this.upload = upload; |
| 2445 | +}; |
| 2446 | + |
| 2447 | +mw.UploadWizardDeedPreview.prototype = { |
| 2448 | + setup: function() { |
| 2449 | + var _this = this; |
| 2450 | + // add a preview on the deeds page |
| 2451 | + var thumbnailDiv = $j( '<div></div>' ).addClass( 'mwe-upwiz-thumbnail' ); |
| 2452 | + $j( '#mwe-upwiz-deeds-thumbnails' ).append( thumbnailDiv ); |
| 2453 | + _this.upload.setThumbnail( thumbnailDiv, mw.getConfig( 'smallThumbnailWidth' ), 'mwe-upwiz-smallthumbnail' ); |
| 2454 | + } |
| 2455 | +}; |
| 2456 | + |
| 2457 | +mw.UploadWizardNullDeed = $j.extend( new mw.UploadWizardDeed(), { |
| 2458 | + isReady: function() { return false; }, |
| 2459 | + |
| 2460 | + setQuantity: function( n ) { return; } |
| 2461 | + |
| 2462 | +} ); |
| 2463 | + |
| 2464 | + |
| 2465 | + |
| 2466 | + |
| 2467 | + |
| 2468 | +/** |
| 2469 | + * @param selector where to put this deed chooser |
| 2470 | + * @param isPlural whether this chooser applies to multiple files (changes messaging mostly) |
| 2471 | + */ |
| 2472 | +mw.UploadWizardDeedChooser = function( selector ) { |
| 2473 | + var _this = this; |
| 2474 | + _this.selector = selector; |
| 2475 | + _this.deed = mw.UploadWizardNullDeed; |
| 2476 | + |
| 2477 | + items = []; |
| 2478 | + $j.each( [ 'ownwork', 'thirdparty' ], function (i, key) { |
| 2479 | + var item = |
| 2480 | + '<div class="mwe-upwiz-macro-deed-' + key + ' mwe-upwiz-deed">' |
| 2481 | + + '<div class="mwe-upwiz-deed-option-title">' |
| 2482 | + + '<span class="mwe-upwiz-deed-header mwe-closed">' |
| 2483 | + + '<a class="mwe-upwiz-deed-header-link mwe-upwiz-deed-name"></a>' |
| 2484 | + + '</span>' |
| 2485 | + + '<span class="mwe-upwiz-deed-header mwe-open" style="display: none;">' |
| 2486 | + + '<span class="mwe-upwiz-deed-name"></span>' |
| 2487 | + + ' <a class="mwe-upwiz-macro-deeds-return">' + gM( 'mwe-upwiz-change' ) + '</a>' |
| 2488 | + + '</span>' |
| 2489 | + + '</div>' |
| 2490 | + + '<div class="mwe-upwiz-deed-form"></div>' |
| 2491 | + + '</div>' |
| 2492 | + items.push(item); |
| 2493 | + } ); |
| 2494 | + |
| 2495 | + $j( selector ).html( items.join('') ); |
| 2496 | + |
| 2497 | + $j( '.mwe-upwiz-macro-deeds-return' ).click( function() { |
| 2498 | + _this.choose( mw.UploadWizardNullDeed ); |
| 2499 | + _this.showDeedChoice(); |
| 2500 | + } ); |
| 2501 | + |
| 2502 | + _this.choose( mw.UploadWizardNullDeed ); |
| 2503 | + _this.showDeedChoice(); |
| 2504 | + |
| 2505 | + // set up the deed interfaces |
| 2506 | + _this.deeds = { |
| 2507 | + 'ownwork' : _this.setupDeedOwnWork(), |
| 2508 | + 'thirdparty' : _this.setupDeedThirdParty() |
| 2509 | + }; |
| 2510 | + |
| 2511 | + _this.setQuantity(); |
| 2512 | +}; |
| 2513 | + |
| 2514 | + |
| 2515 | +mw.UploadWizardDeedChooser.prototype = { |
| 2516 | + |
| 2517 | + count: 0, |
| 2518 | + |
| 2519 | + attach: function( upload ) { |
| 2520 | + var _this = this; |
| 2521 | + upload.deedChooser = _this; |
| 2522 | + _this.count++; |
| 2523 | + _this.setQuantity(); |
| 2524 | + }, |
| 2525 | + |
| 2526 | + detach: function( upload ) { |
| 2527 | + _this.count--; |
| 2528 | + _this.setQuantity(); |
| 2529 | + }, |
| 2530 | + |
| 2531 | + // modify various interface strings depending on singular, multiple deeds |
| 2532 | + setQuantity: function() { |
| 2533 | + var _this = this; |
| 2534 | + mw.log( "setting quantity of deed to " + _this.count ); |
| 2535 | + var isPlural = _this.count > 1; |
| 2536 | + $j.each( [ 'ownwork', 'thirdparty' ], function (i, key) { |
| 2537 | + |
| 2538 | + // fix the deed title that opens and closes |
| 2539 | + gMkey = isPlural ? key + '-plural' : key; |
| 2540 | + $j( _this.selector ) |
| 2541 | + .find( '.mwe-upwiz-macro-deed-' + key) |
| 2542 | + .find( '.mwe-upwiz-deed-name' ) |
| 2543 | + .html( gM( 'mwe-upwiz-source-' + gMkey ) ); |
| 2544 | + |
| 2545 | + // any other internal strings in the deed |
| 2546 | + if ( _this.deeds[key] ) { |
| 2547 | + _this.deeds[key].setQuantity( _this.count ); |
| 2548 | + } |
| 2549 | + |
| 2550 | + } ); |
| 2551 | + $j( _this ).trigger( 'setQuantityEvent' ); |
| 2552 | + |
| 2553 | + }, |
| 2554 | + |
| 2555 | + choose: function( deed ) { |
| 2556 | + var _this = this; |
| 2557 | + _this.deed = deed; |
| 2558 | + if ( deed === mw.UploadWizardNullDeed ) { |
| 2559 | + $j( _this ).trigger( 'chooseNullDeed' ); |
| 2560 | + //_this.trigger( 'isNotReady' ); |
| 2561 | + $j( _this.selector ) |
| 2562 | + .find( 'input.mwe-accept-deed' ) |
| 2563 | + .attr( 'checked', false ) |
| 2564 | + } else { |
| 2565 | + $j( _this ).trigger( 'chooseDeed' ); |
| 2566 | + } |
| 2567 | + }, |
| 2568 | + |
| 2569 | + /** |
| 2570 | + * Go back to original source choice. |
| 2571 | + * Assumed that we are in details mode. |
| 2572 | + */ |
| 2573 | + showDeedChoice: function() { |
| 2574 | + var _this = this; |
| 2575 | + $j( _this.selector ).find( '.mwe-upwiz-deed-header.mwe-open' ).hide(); |
| 2576 | + $j( _this.selector ).find( '.mwe-upwiz-deed-header.mwe-closed' ).show(); |
| 2577 | + $j( _this.selector ).find( '.mwe-upwiz-deed' ).maskSafeShow(); |
| 2578 | + $j( _this.selector ).find( '.mwe-upwiz-deed-form' ).maskSafeHide(); |
| 2579 | + }, |
| 2580 | + |
| 2581 | + /** |
| 2582 | + * From the deed choice page, show a particular deed |
| 2583 | + */ |
| 2584 | + showDeed: function( deedSelector ) { |
| 2585 | + $j( deedSelector ).find( '.mwe-upwiz-deed-header.mwe-open' ).show(); |
| 2586 | + $j( deedSelector ).find( '.mwe-upwiz-deed-header.mwe-closed' ).hide(); |
| 2587 | + $j( deedSelector ).siblings().maskSafeHide(); |
| 2588 | + $j( deedSelector ).find( '.mwe-upwiz-deed-form' ).maskSafeShow(); |
| 2589 | + }, |
| 2590 | + |
| 2591 | + /** |
| 2592 | + * Set up the form and deed object for the deed option that says these uploads are all the user's own work. |
| 2593 | + */ |
| 2594 | + setupDeedOwnWork: function() { |
| 2595 | + mw.log("setupdeed own work"); |
| 2596 | + var _this = this; |
| 2597 | + |
| 2598 | + var authorInput = $j( '<input />') |
| 2599 | + .attr( { name: "author" } ) |
| 2600 | + .addClass( 'mwe-upwiz-sign' ); |
| 2601 | + |
| 2602 | + var licenseInputDiv = $j( '<div class="mwe-upwiz-deed-license"></div>' ); |
| 2603 | + var licenseInput = new mw.UploadWizardLicenseInput( licenseInputDiv ); |
| 2604 | + licenseInput.setDefaultValues(); |
| 2605 | + |
| 2606 | + var ownWorkDeed = $j.extend( new mw.UploadWizardDeed(), { |
| 2607 | + |
| 2608 | + licenseInput: licenseInput, |
| 2609 | + |
| 2610 | + isReady: function() { |
| 2611 | + return ( |
| 2612 | + ( $j( _this.selector ).find( '.mwe-accept-deed' ).is( ':checked' ) !== 0 ) |
| 2613 | + && |
| 2614 | + licenseInput.isSet() |
| 2615 | + ); |
| 2616 | + }, |
| 2617 | + |
| 2618 | + getSourceWikiText: function() { |
| 2619 | + return '{{own}}'; |
| 2620 | + }, |
| 2621 | + |
| 2622 | + getAuthorWikiText: function() { |
| 2623 | + return "[[User:" + mw.getConfig('userName') + '|' + $j( authorInput ).val() + ']]'; |
| 2624 | + }, |
| 2625 | + |
| 2626 | + |
| 2627 | + getLicenseWikiText: function() { |
| 2628 | + var wikiText = '{{self'; |
| 2629 | + $j.each ( licenseInput.getTemplates(), function( i, template ) { |
| 2630 | + wikiText += '|' + template; |
| 2631 | + } ); |
| 2632 | + wikiText += '}}'; |
| 2633 | + return wikiText; |
| 2634 | + }, |
| 2635 | + |
| 2636 | + createInterface: function( deedChooser ) { |
| 2637 | + var _this = this; |
| 2638 | + _this.deedChooser = deedChooser; |
| 2639 | + |
| 2640 | + var standardDiv = $j( '<div />' ) |
| 2641 | + .append( |
| 2642 | + $j( '<input />') |
| 2643 | + .attr( { type: 'checkbox' } ) |
| 2644 | + .click( function() { |
| 2645 | + if ( $j( this ).is( ':checked' ) ) { |
| 2646 | + $j( deedChooser.selector ) |
| 2647 | + .find( '.mwe-upwiz-deed-accept-ownwork-custom' ) |
| 2648 | + .attr( 'checked', false ); |
| 2649 | + _this.licenseInput.setDefaultValues(); |
| 2650 | + deedChooser.choose( ownWorkDeed ); |
| 2651 | + } else { |
| 2652 | + deedChooser.choose( mw.UploadWizardNullDeed ); |
| 2653 | + } |
| 2654 | + } ) |
| 2655 | + .addClass( 'mwe-upwiz-deed-accept-ownwork-default mwe-accept-deed mwe-checkbox-hang-indent' ), |
| 2656 | + |
| 2657 | + // text added in setQuantit |
| 2658 | + $j( '<p class="mwe-upwiz-source-ownwork-assert mwe-checkbox-hang-indent-text"/>' ), |
| 2659 | + |
| 2660 | + $j( '<p />' ) |
| 2661 | + .addClass( 'mwe-checkbox-hang-indent-text' ) |
| 2662 | + .addClass( 'mwe-small-print' ) |
| 2663 | + .html( gM ( 'mwe-upwiz-source-ownwork-assert-note' ) ) |
| 2664 | + ); |
| 2665 | + |
| 2666 | + var toggleDiv = $j('<div />'); |
| 2667 | + |
| 2668 | + var customDiv = $j('<div/>') |
| 2669 | + .maskSafeHide() |
| 2670 | + .append( |
| 2671 | + $j( '<input />') |
| 2672 | + .attr( { type: 'checkbox' } ) |
| 2673 | + .click( function() { |
| 2674 | + if ( $j( this ).is( ':checked' ) ) { |
| 2675 | + $j( deedChooser.selector ) |
| 2676 | + .find( '.mwe-upwiz-deed-accept-ownwork-default' ) |
| 2677 | + .attr( 'checked', false ) |
| 2678 | + deedChooser.choose( ownWorkDeed ); |
| 2679 | + } else { |
| 2680 | + deedChooser.choose( mw.UploadWizardNullDeed ); |
| 2681 | + } |
| 2682 | + } ) |
| 2683 | + .addClass( 'mwe-upwiz-deed-accept-ownwork-custom mwe-accept-deed mwe-checkbox-hang-indent' ), |
| 2684 | + $j( '<p class="mwe-upwiz-source-ownwork-assert-custom mwe-checkbox-hang-indent-text" />' ), |
| 2685 | + licenseInputDiv |
| 2686 | + ); |
| 2687 | + |
| 2688 | + $j( deedChooser.selector ).find( '.mwe-upwiz-macro-deed-ownwork .mwe-upwiz-deed-form' ) |
| 2689 | + .append( $j( '<div class="mwe-upwiz-deed-form-internal" />' ) |
| 2690 | + .append( standardDiv, |
| 2691 | + toggleDiv, |
| 2692 | + customDiv ) |
| 2693 | + ); |
| 2694 | + |
| 2695 | + |
| 2696 | + |
| 2697 | + mw.UploadWizardUtil.makeFadingToggler( standardDiv, toggleDiv, customDiv ); |
| 2698 | + |
| 2699 | + // if they select 'fewer options', and they have selected the deed in there, we should unselect it |
| 2700 | + $j( toggleDiv ).bind( 'close', function(e) { |
| 2701 | + e.stopPropagation(); |
| 2702 | + if ( $j( deedChooser.selector ).find( '.mwe-upwiz-deed-accept-ownwork-custom' ).is( ':checked' ) ) { |
| 2703 | + $j( deedChooser.selector ) |
| 2704 | + .find( '.mwe-upwiz-deed-accept-ownwork-custom' ) |
| 2705 | + .attr( 'checked', false ); |
| 2706 | + deedChooser.choose( mw.UploadWizardNullDeed ); |
| 2707 | + } |
| 2708 | + } ); |
| 2709 | + |
| 2710 | + _this.setQuantity( 1 ); |
| 2711 | + |
| 2712 | + |
| 2713 | + }, |
| 2714 | + |
| 2715 | + |
| 2716 | + setQuantity: function( n ) { |
| 2717 | + var _this = this; |
| 2718 | + var plural = n > 1 ? '-plural' : ''; |
| 2719 | + $j( _this.deedChooser.selector ) |
| 2720 | + .find( 'p.mwe-upwiz-source-ownwork-assert' ) |
| 2721 | + .html( gM( 'mwe-upwiz-source-ownwork-assert' + plural, |
| 2722 | + $j( '<input />' ) |
| 2723 | + .attr( { name: 'author' } ) |
| 2724 | + .addClass( 'mwe-upwiz-sign' ) ) ); |
| 2725 | + |
| 2726 | + $j( _this.deedChooser.selector ) |
| 2727 | + .find( 'p.mwe-upwiz-source-ownwork-assert-custom' ) |
| 2728 | + .html( gM( 'mwe-upwiz-source-ownwork-assert-custom' + plural, |
| 2729 | + '<span class="mwe-custom-author-input"></span>' ) ); |
| 2730 | + |
| 2731 | + // have to add the author input this way -- gM() will flatten it to a string and we'll lose it as a dom object |
| 2732 | + $j( _this.deedChooser.selector ).find( '.mwe-custom-author-input' ).append( authorInput ); |
| 2733 | + |
| 2734 | + // synchronize both username signatures |
| 2735 | + // set initial value to configured username |
| 2736 | + // if one changes all the others change (keyup event) |
| 2737 | + // |
| 2738 | + // also set tooltips ( the title, tipsy() ) |
| 2739 | + $j( _this.deedChooser.selector ).find( '.mwe-upwiz-sign' ) |
| 2740 | + .attr( { |
| 2741 | + title: gM( 'mwe-upwiz-tooltip-sign' ), |
| 2742 | + value: mw.getConfig( 'userName' ) |
| 2743 | + } ) |
| 2744 | + .tipsy( { trigger: 'focus', gravity: 'w' } ) |
| 2745 | + .keyup( function() { |
| 2746 | + var thisInput = this; |
| 2747 | + var thisVal = $j( thisInput ).val(); |
| 2748 | + $j.each( $j( '.mwe-upwiz-sign' ), function( i, input ) { |
| 2749 | + if (thisInput !== input) { |
| 2750 | + $j( input ).val( thisVal ); |
| 2751 | + } |
| 2752 | + } ); |
| 2753 | + } ); |
| 2754 | + |
| 2755 | + |
| 2756 | + } |
| 2757 | + } ); |
| 2758 | + |
| 2759 | + ownWorkDeed.createInterface( _this ); |
| 2760 | + |
| 2761 | + $j( _this.selector ).find( '.mwe-upwiz-macro-deed-ownwork .mwe-upwiz-deed-header-link').click( |
| 2762 | + function() { |
| 2763 | + _this.showDeed( $j( _this.selector ).find( '.mwe-upwiz-macro-deed-ownwork' ) ); |
| 2764 | + } |
| 2765 | + ); |
| 2766 | + |
| 2767 | + return ownWorkDeed; |
| 2768 | + |
| 2769 | + }, |
| 2770 | + |
| 2771 | + setupDeedThirdParty: function() { |
| 2772 | + var _this = this; |
| 2773 | + var sourceInput = $j('<textarea class="mwe-source mwe-long-textarea" name="source" rows="1" cols="40"></textarea>' ) |
| 2774 | + .growTextArea() |
| 2775 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-source' ) ) |
| 2776 | + .tipsy( { trigger: 'focus', gravity: 'w' } ); |
| 2777 | + var authorInput = $j('<textarea class="mwe-author mwe-long-textarea" name="author" rows="1" cols="40"></textarea>' ) |
| 2778 | + .growTextArea() |
| 2779 | + .attr( 'title', gM( 'mwe-upwiz-tooltip-author' ) ) |
| 2780 | + .tipsy( { trigger: 'focus', gravity: 'w' } ); |
| 2781 | + licenseInputDiv = $j( '<div class="mwe-upwiz-deed-license"></div>' ); |
| 2782 | + licenseInput = new mw.UploadWizardLicenseInput( licenseInputDiv ); |
| 2783 | + licenseInput.setDefaultValues(); |
| 2784 | + |
| 2785 | + var thirdPartyDeed = $j.extend( new mw.UploadWizardDeed(), { |
| 2786 | + sourceInput: sourceInput, |
| 2787 | + authorInput: authorInput, |
| 2788 | + licenseInput: licenseInput, |
| 2789 | + |
| 2790 | + isReady: function() { |
| 2791 | + return (! mw.isEmpty( $j( this.sourceInput ).val() ) ) |
| 2792 | + && (! mw.isEmpty( $j( this.authorInput ).val() ) ) |
| 2793 | + && this.licenseInput.isSet() |
| 2794 | + }, |
| 2795 | + |
| 2796 | + createInterface: function( deedChooser ) { |
| 2797 | + var _this = this; |
| 2798 | + _this.deedChooser = deedChooser; |
| 2799 | + |
| 2800 | + $j( deedChooser.selector ).find( '.mwe-upwiz-macro-deed-thirdparty .mwe-upwiz-deed-form' ).append( |
| 2801 | + $j( '<div class="mwe-upwiz-deed-form-internal"/>' ) |
| 2802 | + .append( |
| 2803 | + $j( '<div class="mwe-upwiz-source-thirdparty-custom-plural-intro" />' ), |
| 2804 | + $j( '<div />' ) |
| 2805 | + .addClass( "mwe-upwiz-thirdparty-fields" ) |
| 2806 | + .append( $j( '<label />' ) |
| 2807 | + .attr( { 'for' : 'source' } ) |
| 2808 | + .text( gM( 'mwe-upwiz-source' ) ) ) |
| 2809 | + .append( sourceInput ), |
| 2810 | + $j( '<div />' ) |
| 2811 | + .addClass( "mwe-upwiz-thirdparty-fields" ) |
| 2812 | + .append( $j( '<label />' ) |
| 2813 | + .attr( { 'for' : 'author' } ) |
| 2814 | + .text( gM( 'mwe-upwiz-author' ) ) ) |
| 2815 | + .append( authorInput ), |
| 2816 | + $j( '<div />' ).addClass( 'mwe-upwiz-thirdparty-license' ) |
| 2817 | + .text( gM( 'mwe-upwiz-source-thirdparty-license' ) ), |
| 2818 | + licenseInputDiv |
| 2819 | + ) |
| 2820 | + ); |
| 2821 | + |
| 2822 | + thirdPartyDeed.setQuantity( 1 ); |
| 2823 | + }, |
| 2824 | + |
| 2825 | + |
| 2826 | + setQuantity: function( n ) { |
| 2827 | + var _this = this; |
| 2828 | + $j( _this.deedChooser.selector ) |
| 2829 | + .find( 'div.mwe-upwiz-source-thirdparty-custom-plural-intro' ) |
| 2830 | + .html( n > 1 ? gM( 'mwe-upwiz-source-thirdparty-custom-plural-intro' ) : '' ); |
| 2831 | + } |
| 2832 | + |
| 2833 | + |
| 2834 | + } ); |
| 2835 | + |
| 2836 | + thirdPartyDeed.createInterface( _this ); |
| 2837 | + |
| 2838 | + // set up the header |
| 2839 | + $j( _this.selector ).find( '.mwe-upwiz-macro-deed-thirdparty .mwe-upwiz-deed-header-link').click( |
| 2840 | + function() { |
| 2841 | + _this.showDeed( $j( _this.selector ).find( '.mwe-upwiz-macro-deed-thirdparty' ) ); |
| 2842 | + _this.choose( thirdPartyDeed ); |
| 2843 | + } |
| 2844 | + ); |
| 2845 | + |
| 2846 | + return thirdPartyDeed; |
| 2847 | + } |
| 2848 | +}; |
| 2849 | + |
| 2850 | + |
| 2851 | + |
| 2852 | +/** |
| 2853 | + * Miscellaneous utilities |
| 2854 | + */ |
| 2855 | +mw.UploadWizardUtil = { |
| 2856 | + |
| 2857 | + /** |
| 2858 | + * make a "more" options toggle which makes the standard div fade when 'more' is open |
| 2859 | + * @param standardDiv the div representing the standard options |
| 2860 | + * @param toggleDiv the div which has the control to open and shut custom options |
| 2861 | + * @param customDiv the div containing the custom options |
| 2862 | + */ |
| 2863 | + makeFadingToggler: function( standardDiv, toggleDiv, moreDiv ) { |
| 2864 | + return mw.UploadWizardUtil._makeToggler( standardDiv, toggleDiv, moreDiv, true ); |
| 2865 | + }, |
| 2866 | + |
| 2867 | + |
| 2868 | + /** |
| 2869 | + * make a standard toggler that just opens up a new panel |
| 2870 | + * @param toggle div -- where to put the 'more/fewer options' button |
| 2871 | + * @param moreDiv -- the div to hide or show |
| 2872 | + */ |
| 2873 | + makeMoreToggler: function( toggleDiv, moreDiv ) { |
| 2874 | + return mw.UploadWizardUtil._makeToggler( null, toggleDiv, moreDiv, false ); |
| 2875 | + }, |
| 2876 | + |
| 2877 | + |
| 2878 | + /** |
| 2879 | + * There is a common pattern of having standard options, and then a "more options" panel which |
| 2880 | + * disables the standard options panel. |
| 2881 | + * |
| 2882 | + * We do not do anything to disable the inputs on either panel. So far, we've implemented these |
| 2883 | + * in a way that the 'custom' panel is always controlling everything, but the standard panel just |
| 2884 | + * enters values into the custom one behind the scenes. |
| 2885 | + * |
| 2886 | + * @param standardDiv the div representing the standard options |
| 2887 | + * @param toggleDiv the div which has the control to open and shut custom options |
| 2888 | + * @param moreDiv the div containing the custom options |
| 2889 | + * @param fade whether to fade standardDiv when moreDiv is open |
| 2890 | + */ |
| 2891 | + _makeToggler: function ( standardDiv, toggleDiv, moreDiv, fade ) { |
| 2892 | + if (fade === undefined) { |
| 2893 | + fade = false; |
| 2894 | + } |
| 2895 | + |
| 2896 | + var icon = $j( '<div class="ui-icon ui-icon-triangle-1-e" style="display: inline-block; margin-top: 3px;">' ); |
| 2897 | + var text = $j( '<span>' ).append( gM( 'mwe-upwiz-more-options' ) ); |
| 2898 | + var toggle = function() { |
| 2899 | + var open = ! ( $j( this ).data( 'open' ) ) ; |
| 2900 | + $j( this ).data( 'open', open ); |
| 2901 | + // on toggle: |
| 2902 | + if ( open ) { |
| 2903 | + // set out class to show the "close" message |
| 2904 | + text.text( gM( 'mwe-upwiz-fewer-options' ) ); |
| 2905 | + icon.removeClass( "ui-icon-triangle-1-e" ) |
| 2906 | + .addClass( "ui-icon-triangle-1-s" ); |
| 2907 | + moreDiv.maskSafeShow(); |
| 2908 | + if (fade) { |
| 2909 | + standardDiv.mask(); |
| 2910 | + } |
| 2911 | + } else { |
| 2912 | + text.text( gM( 'mwe-upwiz-more-options' ) ); |
| 2913 | + icon.removeClass( "ui-icon-triangle-1-s" ) |
| 2914 | + .addClass( "ui-icon-triangle-1-e" ) |
| 2915 | + moreDiv.maskSafeHide(); |
| 2916 | + if (fade) { |
| 2917 | + standardDiv.unmask(); |
| 2918 | + } |
| 2919 | + $j( this ).trigger( 'close' ); |
| 2920 | + } |
| 2921 | + }; |
| 2922 | + |
| 2923 | + $j( toggleDiv ) |
| 2924 | + .addClass( 'mwe-more-options' ) |
| 2925 | + .append( $j( '<a />' ) |
| 2926 | + .click( toggle ) |
| 2927 | + .data( 'open', false ) |
| 2928 | + .append( icon, text ) ); |
| 2929 | + |
| 2930 | + }, |
| 2931 | + |
| 2932 | + /** |
| 2933 | + * remove an item from an array. Tests for === identity to remove the item |
| 2934 | + * XXX the entire rationale for this file may be wrong. |
| 2935 | + * XXX The jQuery way would be to query the DOM for objects, not to keep a separate array hanging around |
| 2936 | + * @param items the array where we want to remove an item |
| 2937 | + * @param item the item to remove |
| 2938 | + */ |
| 2939 | + removeItem: function( items, item ) { |
| 2940 | + for ( var i = 0; i < items.length; i++ ) { |
| 2941 | + if ( items[i] === item ) { |
| 2942 | + items.splice( i, 1 ); |
| 2943 | + break; |
| 2944 | + } |
| 2945 | + } |
| 2946 | + }, |
| 2947 | + |
| 2948 | + /** |
| 2949 | + * Capitalise first letter and replace spaces by underscores |
| 2950 | + * @param filename (basename, without directories) |
| 2951 | + * @return typical title as would appear on MediaWiki |
| 2952 | + */ |
| 2953 | + pathToTitle: function ( filename ) { |
| 2954 | + return mw.ucfirst( filename.replace(/ /g, '_' ) ); |
| 2955 | + }, |
| 2956 | + |
| 2957 | + /** |
| 2958 | + * Capitalise first letter and replace underscores by spaces |
| 2959 | + * @param title typical title as would appear on MediaWiki |
| 2960 | + * @return plausible local filename, with spaces changed to underscores. |
| 2961 | + */ |
| 2962 | + titleToPath: function ( title ) { |
| 2963 | + return mw.ucfirst( title.replace(/_/g, ' ' ) ); |
| 2964 | + }, |
| 2965 | + |
| 2966 | + /** |
| 2967 | + * Slice extension off a path |
| 2968 | + * We assume that extensions are 1-4 characters in length |
| 2969 | + * @param path to file, like "foo/bar/baz.jpg" |
| 2970 | + * @return extension, like ".jpg" or undefined if it doesn't look lke an extension. |
| 2971 | + */ |
| 2972 | + getExtension: function( path ) { |
| 2973 | + var extension = undefined; |
| 2974 | + var idx = path.lastIndexOf( '.' ); |
| 2975 | + if (idx > 0 && ( idx > ( path.length - 5 ) ) && ( idx < ( path.length - 1 ) ) ) { |
| 2976 | + extension = path.substr( idx + 1 ).toLowerCase(); |
| 2977 | + } |
| 2978 | + return extension; |
| 2979 | + }, |
| 2980 | + |
| 2981 | + /** |
| 2982 | + * Last resort to guess a proper extension |
| 2983 | + */ |
| 2984 | + mimetypeToExtension: { |
| 2985 | + 'image/jpeg': 'jpg', |
| 2986 | + 'image/gif': 'gif' |
| 2987 | + // fill as needed |
| 2988 | + } |
| 2989 | + |
| 2990 | + |
| 2991 | +}; |
| 2992 | + |
| 2993 | +/** |
| 2994 | + * Upper-case the first letter of a string. XXX move to common library |
| 2995 | + * @param string |
| 2996 | + * @return string with first letter uppercased. |
| 2997 | + */ |
| 2998 | +mw.ucfirst = function( s ) { |
| 2999 | + return s.substring(0,1).toUpperCase() + s.substr(1); |
| 3000 | +}; |
| 3001 | + |
| 3002 | + |
| 3003 | +/** |
| 3004 | + * jQuery extension. Makes a textarea automatically grow if you enter overflow |
| 3005 | + * (This feature was in the old Commons interface with a confusing arrow icon; it's nicer to make it automatic.) |
| 3006 | + */ |
| 3007 | +jQuery.fn.growTextArea = function( options ) { |
| 3008 | + |
| 3009 | + // this is a jquery-style object |
| 3010 | + |
| 3011 | + // in MSIE, this makes it possible to know what scrollheight is |
| 3012 | + // Technically this means text could now dangle over the edge, |
| 3013 | + // but it shouldn't because it will always grow to accomodate very quickly. |
| 3014 | + |
| 3015 | + if ($j.msie) { |
| 3016 | + this.each( function(i, textArea) { |
| 3017 | + textArea.style.overflow = 'visible'; |
| 3018 | + } ); |
| 3019 | + } |
| 3020 | + |
| 3021 | + this.resizeIfNeeded = function() { |
| 3022 | + // this is the dom element |
| 3023 | + while (this.scrollHeight > this.offsetHeight) { |
| 3024 | + this.rows++; |
| 3025 | + } |
| 3026 | + return this; |
| 3027 | + }; |
| 3028 | + |
| 3029 | + this.addClass( 'mwe-grow-textarea' ); |
| 3030 | + |
| 3031 | + this.change(this.resizeIfNeeded); |
| 3032 | + this.keyup(this.resizeIfNeeded); |
| 3033 | + |
| 3034 | + |
| 3035 | + return this; |
| 3036 | +}; |
| 3037 | + |
| 3038 | +jQuery.fn.mask = function( options ) { |
| 3039 | + |
| 3040 | + // intercept clicks... |
| 3041 | + // Note: the size of the div must be obtainable. Hence, this cannot be a div without layout (e.g. display:none). |
| 3042 | + // some of this is borrowed from http://code.google.com/p/jquery-loadmask/ , but simplified |
| 3043 | + $j.each( this, function( i, el ) { |
| 3044 | + |
| 3045 | + if ( ! $j( el ).data( 'mask' ) ) { |
| 3046 | + |
| 3047 | + |
| 3048 | + //fix for z-index bug with selects in IE6 |
| 3049 | + if ( $j.browser.msie && $j.browser.version.substring(0,1) === '6' ){ |
| 3050 | + el.find( "select" ).addClass( "masked-hidden" ); |
| 3051 | + } |
| 3052 | + |
| 3053 | + var mask = $j( '<div />' ) |
| 3054 | + .css( { 'position' : 'absolute', |
| 3055 | + 'top' : '0px', |
| 3056 | + 'left' : '0px', |
| 3057 | + 'width' : el.offsetWidth + 'px', |
| 3058 | + 'height' : el.offsetHeight + 'px', |
| 3059 | + 'z-index' : 100 } ) |
| 3060 | + .click( function( e ) { e.stopPropagation(); } ); |
| 3061 | + |
| 3062 | + $j( el ).css( { 'position' : 'relative' } ) |
| 3063 | + .fadeTo( 'fast', 0.5 ) |
| 3064 | + .append( mask ) |
| 3065 | + .data( 'mask', mask ); |
| 3066 | + |
| 3067 | + //auto height fix for IE -- not sure about this, i think offsetWidth + Height is a better solution. Test! |
| 3068 | + /* |
| 3069 | + if( $j.browser.msie ) { |
| 3070 | + mask.height(el.height() + parseInt(el.css("padding-top")) + parseInt(el.css("padding-bottom"))); |
| 3071 | + mask.width(el.width() + parseInt(el.css("padding-left")) + parseInt(el.css("padding-right"))); |
| 3072 | + } |
| 3073 | + */ |
| 3074 | + |
| 3075 | + } |
| 3076 | + |
| 3077 | + // XXX bind to a custom event in case the div size changes : ? |
| 3078 | + |
| 3079 | + } ); |
| 3080 | + |
| 3081 | + return this; |
| 3082 | + |
| 3083 | +}; |
| 3084 | + |
| 3085 | +jQuery.fn.unmask = function( options ) { |
| 3086 | + |
| 3087 | + $j.each( this, function( i, el ) { |
| 3088 | + if ( $j( el ).data( 'mask' ) ) { |
| 3089 | + var mask = $j( el ).data( 'mask' ); |
| 3090 | + $j( el ).removeData( 'mask' ); // from the data |
| 3091 | + mask.remove(); // from the DOM |
| 3092 | + $j( el ).fadeTo( 'fast', 1.0 ); |
| 3093 | + } |
| 3094 | + } ); |
| 3095 | + |
| 3096 | + |
| 3097 | + return this; |
| 3098 | +}; |
| 3099 | + |
| 3100 | + |
| 3101 | +/** |
| 3102 | + * Safe hide and show |
| 3103 | + * Rather than use display: none, this collapses the divs to zero height |
| 3104 | + * This is good because then the elements in the divs still have layout and we can do things like mask and unmask (above) |
| 3105 | + * XXX may be obsolete as we are not really doing this any more |
| 3106 | + */ |
| 3107 | + |
| 3108 | +jQuery.fn.maskSafeHide = function( options ) { |
| 3109 | + return this.css( { 'height' : '0px', 'overflow' : 'hidden' } ); |
| 3110 | +}; |
| 3111 | + |
| 3112 | +// XXX check overflow properties, is auto/auto not the right thing? |
| 3113 | +// may be causing scrollbar to appear when div changes size |
| 3114 | +jQuery.fn.maskSafeShow = function( options ) { |
| 3115 | + return this.css( { 'height' : 'auto', 'overflow' : 'visible' } ); |
| 3116 | +}; |
| 3117 | + |
| 3118 | + |
| 3119 | +( function( $j ) { |
| 3120 | + |
| 3121 | + /** |
| 3122 | + * Prevent the closing of a window with a confirm message (the onbeforeunload event seems to |
| 3123 | + * work in most browsers |
| 3124 | + * e.g. |
| 3125 | + * var allowCloseWindow = jQuery().preventCloseWindow( { message: "Don't go away!" } ); |
| 3126 | + * // ... do stuff that can't be interrupted ... |
| 3127 | + * allowCloseWindow(); |
| 3128 | + * |
| 3129 | + * @param options object which should have a message string, already internationalized |
| 3130 | + * @return closure execute this when you want to allow the user to close the window |
| 3131 | + */ |
| 3132 | + $j.fn.preventCloseWindow = function( options ) { |
| 3133 | + if ( typeof options === 'undefined' ) { |
| 3134 | + options = {}; |
| 3135 | + } |
| 3136 | + |
| 3137 | + if ( typeof options.message === 'undefined' ) { |
| 3138 | + options.message = 'Are you sure you want to close this window?'; |
| 3139 | + } |
| 3140 | + |
| 3141 | + $j( window ).unload( function() { |
| 3142 | + return options.message; |
| 3143 | + } ); |
| 3144 | + |
| 3145 | + return function() { |
| 3146 | + $j( window ).removeAttr( 'unload' ); |
| 3147 | + }; |
| 3148 | + |
| 3149 | + }; |
| 3150 | + |
| 3151 | + |
| 3152 | + $j.fn.notify = function ( message ) { |
| 3153 | + // could do something here with Chrome's in-browser growl-like notifications. |
| 3154 | + // play a sound? |
| 3155 | + // if the current tab does not have focus, use an alert? |
| 3156 | + // alert( message ); |
| 3157 | + }; |
| 3158 | + |
| 3159 | + $j.fn.enableNextButton = function() { |
| 3160 | + this.find( '.mwe-upwiz-button-next' ) |
| 3161 | + .removeAttr( 'disabled' ); |
| 3162 | + // .effect( 'pulsate', { times: 3 }, 1000 ); |
| 3163 | + }; |
| 3164 | + |
| 3165 | + $j.fn.disableNextButton = function() { |
| 3166 | + this.find( '.mwe-upwiz-button-next' ) |
| 3167 | + .attr( 'disabled', true ); |
| 3168 | + } |
| 3169 | + |
| 3170 | + |
| 3171 | +} )( jQuery ); |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.UploadWizard.js |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 3172 | + native |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.DestinationChecker.js |
— | — | @@ -0,0 +1,207 @@ |
| 2 | +/** |
| 3 | + * Object to attach to a file name input, to be run on its change() event |
| 4 | + * Largely derived from wgUploadWarningObj in old upload.js |
| 5 | + * Perhaps this could be a jQuery ext |
| 6 | + * @param options dictionary of options |
| 7 | + * selector required, the selector for the input to check |
| 8 | + * processResult required, function to execute on results. accepts two args: |
| 9 | + * 1) filename that invoked this request -- should check if this is still current filename |
| 10 | + * 2) an object with the following fields |
| 11 | + * isUnique: boolean |
| 12 | + * img: thumbnail image src (if not unique) |
| 13 | + * href: the url of the full image (if not unique) |
| 14 | + * title: normalized title of file (if not unique) |
| 15 | + * spinner required, closure to execute to show progress: accepts true to start, false to stop |
| 16 | + * apiUrl optional url to call for api. falls back to local api url |
| 17 | + * delay optional how long to delay after a change in ms. falls back to configured default |
| 18 | + * preprocess optional: function to apply to the contents of selector before testing |
| 19 | + * events what events on the input trigger a check. |
| 20 | + */ |
| 21 | +mw.DestinationChecker = function( options ) { |
| 22 | + |
| 23 | + var _this = this; |
| 24 | + _this.selector = options.selector; |
| 25 | + _this.spinner = options.spinner; |
| 26 | + _this.processResult = options.processResult; |
| 27 | + |
| 28 | + // optional overrides |
| 29 | + |
| 30 | + if (options.apiUrl) { |
| 31 | + _this.apiUrl = options.apiUrl; |
| 32 | + } else { |
| 33 | + _this.apiUrl = mw.getLocalApiUrl(); |
| 34 | + } |
| 35 | + |
| 36 | + $j.each( ['preprocess', 'delay', 'events'], function( i, option ) { |
| 37 | + if ( options[option] ) { |
| 38 | + _this[option] = options[option]; |
| 39 | + } |
| 40 | + } ); |
| 41 | + |
| 42 | + |
| 43 | + // initialize! |
| 44 | + |
| 45 | + var check = _this.getDelayedChecker(); |
| 46 | + |
| 47 | + $j.each( _this.events, function(i, eventName) { |
| 48 | + $j( _this.selector )[eventName]( check ); |
| 49 | + } ); |
| 50 | + |
| 51 | +} |
| 52 | + |
| 53 | +mw.DestinationChecker.prototype = { |
| 54 | + |
| 55 | + // events that the input undergoes which fire off a check |
| 56 | + events: [ 'change', 'keyup' ], |
| 57 | + |
| 58 | + // how long the input muse be "idle" before doing call (don't want to check on each key press) |
| 59 | + delay: 500, // ms; |
| 60 | + |
| 61 | + // what tracks the wait |
| 62 | + timeoutId: null, |
| 63 | + |
| 64 | + // cached results from api calls |
| 65 | + cachedResult: {}, |
| 66 | + |
| 67 | + /** |
| 68 | + * There is an option to preprocess the name (in order to perhaps convert it from |
| 69 | + * title to path, e.g. spaces to underscores, or to add the "File:" part.) Depends on |
| 70 | + * exactly what your input field represents. |
| 71 | + * In the event that the invoker doesn't supply a name preprocessor, use this identity function |
| 72 | + * as default |
| 73 | + * |
| 74 | + * @param something |
| 75 | + * @return that same thing |
| 76 | + */ |
| 77 | + preprocess: function(x) { return x }, |
| 78 | + |
| 79 | + /** |
| 80 | + * fire when the input changes value or keypress |
| 81 | + * will trigger a check of the name if the field has been idle for delay ms. |
| 82 | + */ |
| 83 | + getDelayedChecker: function() { |
| 84 | + var checker = this; |
| 85 | + return function() { |
| 86 | + var el = this; // but we don't use it, since we already have it in _this.selector |
| 87 | + |
| 88 | + // if we changed before the old timeout ran, clear that timeout. |
| 89 | + if ( checker.timeoutId ) { |
| 90 | + window.clearTimeout( checker.timeoutId ); |
| 91 | + } |
| 92 | + |
| 93 | + // and start another, hoping this time we'll be idle for delay ms. |
| 94 | + checker.timeoutId = window.setTimeout( |
| 95 | + function() { checker.checkUnique(); }, |
| 96 | + checker.delay |
| 97 | + ); |
| 98 | + } |
| 99 | + }, |
| 100 | + |
| 101 | + /** |
| 102 | + * Get the current value of the input, with optional preprocessing |
| 103 | + * @return the current input value, with optional processing |
| 104 | + */ |
| 105 | + getName: function() { |
| 106 | + var _this = this; |
| 107 | + return _this.preprocess( $j( _this.selector ).val() ); |
| 108 | + }, |
| 109 | + |
| 110 | + /** |
| 111 | + * Async check if a filename is unique. Can be attached to a field's change() event |
| 112 | + * This is a more abstract version of AddMedia/UploadHandler.js::doDestCheck |
| 113 | + */ |
| 114 | + checkUnique: function() { |
| 115 | + var _this = this; |
| 116 | + |
| 117 | + var found = false; |
| 118 | + var name = _this.getName(); |
| 119 | + |
| 120 | + if ( _this.cachedResult[name] !== undefined ) { |
| 121 | + _this.processResult( _this.cachedResult[name] ); |
| 122 | + return; |
| 123 | + } |
| 124 | + |
| 125 | + // set the spinner to spin |
| 126 | + _this.spinner( true ); |
| 127 | + |
| 128 | + // Setup the request -- will return thumbnail data if it finds one |
| 129 | + var request = { |
| 130 | + 'titles': 'File:' + name, |
| 131 | + 'prop': 'imageinfo', |
| 132 | + 'iiprop': 'url|mime|size', |
| 133 | + 'iiurlwidth': 150 |
| 134 | + }; |
| 135 | + |
| 136 | + // Do the destination check |
| 137 | + mw.getJSON( _this.apiUrl, request, function( data ) { |
| 138 | + // Remove spinner |
| 139 | + _this.spinner( false ); |
| 140 | + |
| 141 | + // if the name's changed in the meantime, our result is useless |
| 142 | + if ( name != _this.getName() ) { |
| 143 | + return; |
| 144 | + } |
| 145 | + |
| 146 | + if ( !data || !data.query || !data.query.pages ) { |
| 147 | + // Ignore a null result |
| 148 | + mw.log(" No data in checkUnique result"); |
| 149 | + return; |
| 150 | + } |
| 151 | + |
| 152 | + var result = undefined; |
| 153 | + |
| 154 | + if ( data.query.pages[-1] ) { |
| 155 | + // No conflict found; this file name is unique |
| 156 | + mw.log(" No pages in checkUnique result"); |
| 157 | + result = { isUnique: true }; |
| 158 | + |
| 159 | + } else { |
| 160 | + |
| 161 | + for ( var page_id in data.query.pages ) { |
| 162 | + if ( !data.query.pages[ page_id ].imageinfo ) { |
| 163 | + continue; |
| 164 | + } |
| 165 | + |
| 166 | + // Conflict found, this filename is NOT unique |
| 167 | + mw.log( " conflict! " ); |
| 168 | + |
| 169 | + if ( data.query.normalized ) { |
| 170 | + var ntitle = data.query.normalized[0].to; |
| 171 | + } else { |
| 172 | + var ntitle = data.query.pages[ page_id ].title |
| 173 | + } |
| 174 | + |
| 175 | + var img = data.query.pages[ page_id ].imageinfo[0]; |
| 176 | + |
| 177 | + result = { |
| 178 | + isUnique: false, |
| 179 | + img: img, |
| 180 | + title: ntitle, |
| 181 | + href : img.descriptionurl |
| 182 | + }; |
| 183 | + |
| 184 | + break; |
| 185 | + } |
| 186 | + } |
| 187 | + |
| 188 | + if ( result !== undefined ) { |
| 189 | + _this.cachedResult[name] = result; |
| 190 | + _this.processResult( result ); |
| 191 | + } |
| 192 | + } ); |
| 193 | + } |
| 194 | + |
| 195 | +}; |
| 196 | + |
| 197 | + |
| 198 | +/** |
| 199 | + * jQuery extension to make a field upload-checkable |
| 200 | + */ |
| 201 | +( function ( $ ) { |
| 202 | + $.fn.destinationChecked = function( options ) { |
| 203 | + var _this = this; |
| 204 | + options.selector = _this; |
| 205 | + new mw.DestinationChecker( options ); |
| 206 | + return _this; |
| 207 | + }; |
| 208 | +} )( jQuery ); |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.DestinationChecker.js |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 209 | + native |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/images/black-angle.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/images/black-angle.gif |
___________________________________________________________________ |
Name: svn:mime-type |
2 | 210 | + application/octet-stream |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/images/grey-angle.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/images/grey-angle.gif |
___________________________________________________________________ |
Name: svn:mime-type |
3 | 211 | + application/octet-stream |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/css/uploadWizard.css |
— | — | @@ -0,0 +1,498 @@ |
| 2 | +form.mwe-upwiz-form { |
| 3 | + display: inline; |
| 4 | +} |
| 5 | + |
| 6 | +#upload-wizard { |
| 7 | + margin-top: 18px; |
| 8 | +} |
| 9 | + |
| 10 | +/* |
| 11 | +.upload-section { |
| 12 | + padding: 1em; |
| 13 | + margin-bottom: 0.5em; |
| 14 | + margin-top: 0.5em; |
| 15 | + border: 1px solid #e0e0e0; |
| 16 | +} |
| 17 | +*/ |
| 18 | + |
| 19 | +#mwe-upwiz-steparrows { |
| 20 | +} |
| 21 | + |
| 22 | +#mwe-upwiz-content { |
| 23 | +} |
| 24 | + |
| 25 | +.mwe-upwiz-clearing { |
| 26 | + clear: left; |
| 27 | + width: 100%; |
| 28 | +} |
| 29 | + |
| 30 | +#mwe-upwiz-steparrows ul { |
| 31 | + list-style-type: none; |
| 32 | + list-style-image: none; |
| 33 | + padding: 0px; |
| 34 | + margin: 0px; |
| 35 | + position: relative; |
| 36 | +} |
| 37 | + |
| 38 | +#mwe-upwiz-steparrows ul li { |
| 39 | + color: #999999; |
| 40 | + float: left; |
| 41 | + padding: 0px; |
| 42 | + margin: 0px; |
| 43 | +} |
| 44 | + |
| 45 | +#mwe-upwiz-steparrows ul li span { |
| 46 | + padding-top: 2px; |
| 47 | + padding-bottom: 4px; |
| 48 | +} |
| 49 | + |
| 50 | +#mwe-upwiz-steparrows ul li span.mwe-arrow-text { |
| 51 | + border-color: #e0e0e0; |
| 52 | + border-style: solid; |
| 53 | + border-width: 1px 0px 1px 0px; |
| 54 | + padding-right: 10px; |
| 55 | + padding-left: 24px; |
| 56 | + z-index: 1; |
| 57 | +} |
| 58 | + |
| 59 | +#mwe-upwiz-steparrows ul li span.mwe-arrow { |
| 60 | + position: relative; |
| 61 | + left: 21px; |
| 62 | + padding-right: 12px; |
| 63 | + z-index: 2; |
| 64 | + background: url('../images/grey-angle.gif') no-repeat right center; |
| 65 | +} |
| 66 | + |
| 67 | +#mwe-upwiz-steparrows ul li.mwe-upwiz-step-highlight span.mwe-arrow { |
| 68 | + background: url('../images/black-angle.gif') no-repeat right center; |
| 69 | +} |
| 70 | + |
| 71 | +#mwe-upwiz-steparrows ul li.mwe-upwiz-step-highlight span.mwe-arrow-text { |
| 72 | + border-color: #000000; |
| 73 | +} |
| 74 | + |
| 75 | +#mwe-upwiz-steparrows ul li.mwe-upwiz-step-highlight span.mwe-arrow-text { |
| 76 | + padding-left: 12px; |
| 77 | + font-weight: bold; |
| 78 | + color: black; |
| 79 | + border-left: 1px solid; |
| 80 | +} |
| 81 | + |
| 82 | +#mwe-upwiz-content { |
| 83 | + padding: 1em; |
| 84 | +} |
| 85 | + |
| 86 | +.mwe-upwiz-add-files-0 { |
| 87 | + margin-top: 20px; |
| 88 | + text-align: center; |
| 89 | + font-size: 16px; |
| 90 | +} |
| 91 | + |
| 92 | +#mwe-upwiz-add-file { |
| 93 | +} |
| 94 | + |
| 95 | +/* NOT a pseudoclass */ |
| 96 | +#mwe-upwiz-add-file.hover { |
| 97 | + text-decoration: underline; |
| 98 | +} |
| 99 | + |
| 100 | +/* perhaps a general class for links that are actually "buttons" */ |
| 101 | +#mwe-upwiz-add-file, .mwe-upwiz-remove, .mwe-upwiz-more-options { |
| 102 | + cursor: pointer; |
| 103 | +} |
| 104 | + |
| 105 | +/* CSS styling hack for file inputs - http://www.quirksmode.org/dom/inputfile.html */ |
| 106 | +.mwe-upwiz-file-ctrl-container { |
| 107 | + position: absolute; |
| 108 | + overflow: hidden; |
| 109 | +} |
| 110 | + |
| 111 | +.mwe-upwiz-file-input, .mwe-upwiz-visible-file { |
| 112 | + cursor: pointer; |
| 113 | +} |
| 114 | + |
| 115 | +/* file inputs are freakishly large to overflow the containing div -- we get a div |
| 116 | + that can act as a more styleable single-click file input */ |
| 117 | +.mwe-upwiz-file-input, .disabler { |
| 118 | + font-size: 100px; |
| 119 | + -moz-opacity: 0.3; |
| 120 | + filter:alpha(opacity: 0); |
| 121 | + opacity: 0; |
| 122 | + z-index: 2; |
| 123 | +} |
| 124 | + |
| 125 | +.mwe-upwiz-file.filled, #mwe-upwiz-upload-ctrls { |
| 126 | + padding: 4px; |
| 127 | +} |
| 128 | + |
| 129 | +.mwe-upwiz-file.filled { |
| 130 | + height: 24px; |
| 131 | + border-bottom: 1px solid #e0e0e0; |
| 132 | +} |
| 133 | + |
| 134 | +.mwe-upwiz-file-indicator, .mwe-upwiz-count { |
| 135 | + float: left; |
| 136 | + width: 92px; |
| 137 | + margin-left: 8px; |
| 138 | + text-align: center; |
| 139 | +} |
| 140 | + |
| 141 | +.mwe-upwiz-add-files-n { |
| 142 | + float: left; |
| 143 | + margin-top: 5px; |
| 144 | + margin-left: 4px; |
| 145 | +} |
| 146 | + |
| 147 | +#mwe-upwiz-add-file-container.mwe-upwiz-add-files-n, .mwe-upwiz-visible-file, .mwe-upwiz-progress-bar-etr { |
| 148 | + width: 300px; |
| 149 | + height: 18px; |
| 150 | + padding-left: 5px; |
| 151 | +} |
| 152 | + |
| 153 | +#mwe-upwiz-add-file-container.mwe-upwiz-add-files-n, .mwe-upwiz-visible-file { |
| 154 | + float: left; |
| 155 | +} |
| 156 | + |
| 157 | +.mwe-upwiz-visible-file { |
| 158 | + white-space: nowrap; |
| 159 | + overflow: hidden; |
| 160 | + |
| 161 | + background: white; |
| 162 | + |
| 163 | + border-style: solid; |
| 164 | + border-top-width: 2px; |
| 165 | + border-left-width: 2px; |
| 166 | + border-right-width: 1px; |
| 167 | + border-bottom-width: 1px; |
| 168 | + border-color: white; |
| 169 | + /* border: 1px solid blue; XXX just to make it visible for now */ |
| 170 | +} |
| 171 | + |
| 172 | +.mwe-upwiz-visible-file.hover { |
| 173 | + background: white; |
| 174 | + border-color: #0645ad; |
| 175 | +} |
| 176 | + |
| 177 | +/* XXX we probably have a standard for this */ |
| 178 | +.helper { |
| 179 | + color: #cccccc; /* or whatever we do for greyed out text */ |
| 180 | + font-variant: italic, oblique; |
| 181 | + text-align: center; |
| 182 | +} |
| 183 | + |
| 184 | + |
| 185 | +#mwe-upwiz-files { |
| 186 | + margin-left: 50px; |
| 187 | + margin-top: 20px; |
| 188 | +} |
| 189 | + |
| 190 | +.mwe-upwiz-file { |
| 191 | + width: 410px; |
| 192 | +} |
| 193 | + |
| 194 | +#mwe-upwiz-upload-ctrls { |
| 195 | + margin-top: 15px; |
| 196 | + |
| 197 | +} |
| 198 | + |
| 199 | +#mwe-upwiz-add-file-container { |
| 200 | + /* empty; this changes a lot */ |
| 201 | +} |
| 202 | + |
| 203 | +.mwe-upwiz-remove { |
| 204 | + padding: 5px; |
| 205 | + cursor: pointer; |
| 206 | + text-align: center; |
| 207 | +} |
| 208 | + |
| 209 | +a.mwe-upwiz-remove:link, |
| 210 | +a.mwe-upwiz-remove:active, |
| 211 | +a.mwe-upwiz-remove:visited { |
| 212 | + color: #999999; |
| 213 | + text-decoration: none; |
| 214 | +} |
| 215 | + |
| 216 | +a.mwe-upwiz-remove:hover { |
| 217 | + color: #ff0000; |
| 218 | + text-decoration: none; |
| 219 | + cursor: pointer; |
| 220 | +} |
| 221 | + |
| 222 | +.mwe-upwiz-remove-desc { |
| 223 | + vertical-align: top; |
| 224 | +} |
| 225 | + |
| 226 | +a[disabled=true] { |
| 227 | + color: #999999; |
| 228 | + text-decoration: none; |
| 229 | + cursor: default; |
| 230 | +} |
| 231 | + |
| 232 | +a[disabled=true]:hover { |
| 233 | + text-decoration: none; |
| 234 | +} |
| 235 | + |
| 236 | +.mwe-upwiz-status-progress { |
| 237 | + font-weight: bold; |
| 238 | + color: #ff9900; |
| 239 | +} |
| 240 | + |
| 241 | +/* XXX add background of checkmark, with left-padding offset */ |
| 242 | +.mwe-upwiz-status-completed { |
| 243 | + font-weight: bold; |
| 244 | + color: #009900; |
| 245 | +} |
| 246 | + |
| 247 | +.mwe-upwiz-progress-bar-etr { |
| 248 | + float: left; |
| 249 | +} |
| 250 | + |
| 251 | +.mwe-upwiz-etr { |
| 252 | + text-align: center; |
| 253 | +} |
| 254 | + |
| 255 | + |
| 256 | +.mwe-upwiz-upload-warning { |
| 257 | + background: #ffffe0; |
| 258 | +} |
| 259 | + |
| 260 | +.mwe-upwiz-details-error { |
| 261 | + display: none; |
| 262 | + background: #ffffe0; |
| 263 | +} |
| 264 | + |
| 265 | +.mwe-upwiz-thumbnail { |
| 266 | + width: 120px; /* XXX should be same as configured thumbnail width in uploadWizard.js */ |
| 267 | + float: left; |
| 268 | + margin-bottom: 12px; |
| 269 | + margin-right: 12px; |
| 270 | +} |
| 271 | + |
| 272 | +#mwe-upwiz-deeds-thumbnails { |
| 273 | + text-align: center; |
| 274 | + vertical-align: middle; |
| 275 | +} |
| 276 | + |
| 277 | +.mwe-upwiz-smallthumbnail { |
| 278 | + width: 60px; /* XXX should be same as configured small thumbnail width in uploadWizard.js */ |
| 279 | + margin: 6px; |
| 280 | +} |
| 281 | + |
| 282 | +.mwe-upwiz-data { |
| 283 | + float: left; |
| 284 | +} |
| 285 | + |
| 286 | + |
| 287 | +/* XXX add spinner gif instead, maybe */ |
| 288 | +.busy { |
| 289 | + background: yellow; |
| 290 | +} |
| 291 | + |
| 292 | +.mwe-upwiz-stepdiv { |
| 293 | + height: 0px; |
| 294 | + overflow: hidden; |
| 295 | +} |
| 296 | + |
| 297 | +#mwe-upwiz-stepdiv-file { |
| 298 | + width: 550px; |
| 299 | +} |
| 300 | + |
| 301 | + |
| 302 | +/* XXX this has to be 'centered' beneath the 'x's -- this is about right for english but we need a more |
| 303 | + robust layout strategy for i18n */ |
| 304 | +.proceed { |
| 305 | + text-align: right; |
| 306 | + width: 410px; |
| 307 | +} |
| 308 | + |
| 309 | +.shim { |
| 310 | + float:right; |
| 311 | + width: 1px; |
| 312 | +} |
| 313 | + |
| 314 | +.clearShim { |
| 315 | + clear: both; |
| 316 | + height: 1px; |
| 317 | + overflow: hidden; |
| 318 | +} |
| 319 | + |
| 320 | +.mwe-checkbox-hang-indent { |
| 321 | + float: left; |
| 322 | + width: 24px; |
| 323 | + margin-top: 6px; |
| 324 | +} |
| 325 | + |
| 326 | +.mwe-checkbox-hang-indent-text { |
| 327 | + margin-left: 24px; |
| 328 | +} |
| 329 | + |
| 330 | +.mwe-small-print { |
| 331 | + font-size: xx-small; |
| 332 | +} |
| 333 | + |
| 334 | +.mwe-upwiz-deed { |
| 335 | + margin-left: 24px; |
| 336 | +} |
| 337 | + |
| 338 | +.mwe-upwiz-deed-name { |
| 339 | + font-weight: bold; |
| 340 | +} |
| 341 | + |
| 342 | +.mwe-more-options, .mwe-fewer-options, .mwe-upwiz-macro-deeds-return, .mwe-upwiz-deed-header-link { |
| 343 | + cursor: pointer; |
| 344 | +} |
| 345 | + |
| 346 | +.mwe-more-options, .mwe-fewer-options { |
| 347 | + padding-bottom: 4px; |
| 348 | +} |
| 349 | + |
| 350 | +.mwe-more-options a:hover, .mwe-fewer-options a:hover { |
| 351 | + text-decoration: none; |
| 352 | +} |
| 353 | + |
| 354 | + |
| 355 | +.mwe-upwiz-deed-license { |
| 356 | + margin-left: 24px; |
| 357 | +} |
| 358 | + |
| 359 | +#mwe-upwiz-macro-deeds { |
| 360 | + margin-top: 12px; |
| 361 | + margin-bottom: 24px; |
| 362 | +} |
| 363 | + |
| 364 | +#mwe-upwiz-macro-files { |
| 365 | + margin-top: 12px; |
| 366 | +} |
| 367 | +.mwe-upwiz-info-file { |
| 368 | + border-bottom: 1px solid #e0e0e0; |
| 369 | + margin-bottom: 12px; |
| 370 | + padding-bottom: 12px; |
| 371 | + width: 660px; |
| 372 | +} |
| 373 | + |
| 374 | + |
| 375 | +.mwe-upwiz-details-input { |
| 376 | + width: 400px; |
| 377 | + float: left; |
| 378 | +} |
| 379 | + |
| 380 | +.mwe-upwiz-desc-lang-select { |
| 381 | + width: 130px; |
| 382 | + font-family: sans-serif; |
| 383 | + font-size: x-small; |
| 384 | +} |
| 385 | + |
| 386 | +.mwe-upwiz-desc-lang-container { |
| 387 | + float: left; |
| 388 | +} |
| 389 | + |
| 390 | +.mwe-upwiz-desc-lang-text { |
| 391 | + width: 240px; |
| 392 | + overflow: hidden; |
| 393 | + font-family: sans-serif; /* XXX is this right? */ |
| 394 | + font-size: x-small; |
| 395 | +} |
| 396 | + |
| 397 | +.mwe-upwiz-details-descriptions-add { |
| 398 | + margin-bottom: 12px; |
| 399 | + margin-left: 100px; |
| 400 | +} |
| 401 | + |
| 402 | +.mwe-grow-textarea, .mwe-long-textarea { |
| 403 | + overflow: hidden; |
| 404 | + font-family: sans-serif; /* XXX is this right? */ |
| 405 | + font-size: x-small; |
| 406 | +} |
| 407 | + |
| 408 | +.mwe-long-textarea { |
| 409 | + width: 370px; |
| 410 | +} |
| 411 | + |
| 412 | +fieldset .mwe-long-textarea { |
| 413 | + width: 280px; |
| 414 | +} |
| 415 | + |
| 416 | + |
| 417 | + |
| 418 | +.mwe-upwiz-details-label { |
| 419 | + width: 100px; |
| 420 | + float: left; |
| 421 | +} |
| 422 | + |
| 423 | +.mwe-upwiz-details-label-input { |
| 424 | + |
| 425 | +} |
| 426 | + |
| 427 | +.mwe-upwiz-details-filename. { |
| 428 | + overflow: hidden; |
| 429 | + width: 350px; |
| 430 | +} |
| 431 | + |
| 432 | +.mwe-upwiz-other-textarea { |
| 433 | + width: 468px; |
| 434 | +} |
| 435 | + |
| 436 | +fieldset.mwe-fieldset { |
| 437 | + width: 450px; |
| 438 | + border: 1px solid #cccccc; |
| 439 | + padding-left: 12px; |
| 440 | + padding-right: 12px; |
| 441 | +} |
| 442 | + |
| 443 | +legend.mwe-legend { |
| 444 | + padding: 0.5em 0.5em 0.5em 0.33em; |
| 445 | + color: #666666; |
| 446 | +} |
| 447 | + |
| 448 | +.masked-hidden { |
| 449 | + visibility: hidden !important; |
| 450 | +} |
| 451 | + |
| 452 | +.mwe-upwiz-thirdparty-fields label { |
| 453 | + width: 100px; |
| 454 | + display: inline-block; |
| 455 | +} |
| 456 | + |
| 457 | +.mwe-upwiz-thirdparty-fields textarea { |
| 458 | + margin: 5px 0px -3px 5px; |
| 459 | +} |
| 460 | + |
| 461 | +.mwe-upwiz-thirdparty-license { |
| 462 | + margin-top: 8px; |
| 463 | +} |
| 464 | + |
| 465 | +.mwe-upwiz-deed-form-internal { |
| 466 | + padding: 5px 0px 20px 20px; |
| 467 | +} |
| 468 | + |
| 469 | + |
| 470 | +.mwe-upwiz-copyright-info label { |
| 471 | + display: inline-block; |
| 472 | + padding-left: 15px; |
| 473 | + text-indent: -15px; |
| 474 | + margin-right: 15px; |
| 475 | +} |
| 476 | + |
| 477 | +/* the hanging indent checkboxes */ |
| 478 | +.mwe-upwiz-copyright-info input.mwe-accept-deed { |
| 479 | + width: 13px; |
| 480 | + height: 13px; |
| 481 | + padding: 0; |
| 482 | + margin: 0px 3px 0px 0px; |
| 483 | + vertical-align: bottom; |
| 484 | + position: relative; |
| 485 | + top: -1px; |
| 486 | +} |
| 487 | + |
| 488 | + |
| 489 | +.mwe-upwiz-custom-deed { |
| 490 | + margin-top: 5px; |
| 491 | +} |
| 492 | + |
| 493 | +.mwe-upwiz-buttons { |
| 494 | + margin-top: 10px; |
| 495 | + padding-top: 10px; |
| 496 | + border-top: 1px solid #e0e0e0; |
| 497 | + text-align: right; /* works for now, only one 'next' button */ |
| 498 | +} |
| 499 | + |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/css/uploadWizard.css |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 500 | + native |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/UploadWizard.i18n.php |
— | — | @@ -0,0 +1,155 @@ |
| 2 | +<?php |
| 3 | +/* |
| 4 | + * Internationalisation for UploadWizard |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup Extensions |
| 8 | + */ |
| 9 | + |
| 10 | +$messages = array(); |
| 11 | +$messages['en'] = array( |
| 12 | + 'mwe-code-unknown' => 'Unknown language', |
| 13 | + 'mwe-loading-upwiz' => 'Loading upload wizard', |
| 14 | + 'mwe-upwiz-tab-file' => '1. Upload your files', |
| 15 | + 'mwe-upwiz-tab-details' => '2. Add licenses and descriptions', |
| 16 | + 'mwe-upwiz-tab-thanks' => '3. Use your files', |
| 17 | + 'mwe-upwiz-intro' => 'Welcome to Wikimedia Commons, a repository of images, sounds, and movies that anyone can freely download and use. Add to humanity\'s knowledge by uploading files that could be used for an educational purpose.', |
| 18 | + 'mwe-upwiz-add-file-n' => 'Add another file', |
| 19 | + 'mwe-upwiz-add-file-0' => 'Click here to add a file for upload', |
| 20 | + 'mwe-upwiz-browse' => 'Browse...', |
| 21 | + 'mwe-upwiz-transported' => 'OK', |
| 22 | + 'mwe-upwiz-click-here' => 'Click here to select a file', |
| 23 | + 'mwe-upwiz-uploading' => 'uploading...', |
| 24 | + 'mwe-upwiz-editing' => 'editing...', |
| 25 | + 'mwe-upwiz-remove-upload' => 'Remove this file from the list of files to upload', |
| 26 | + 'mwe-upwiz-remove-description' => 'Remove this description', |
| 27 | + 'mwe-upwiz-upload' => 'Upload', |
| 28 | + 'mwe-upwiz-upload-count' => '$1 of $2 files uploaded', |
| 29 | + 'mwe-upwiz-progressbar-uploading' => 'uploading', |
| 30 | + 'mwe-upwiz-remaining' => '$1 remaining', |
| 31 | + 'mwe-upwiz-intro-details' => 'Thank you! Now we need some basic information about the files you just uploaded.', |
| 32 | + 'mwe-upwiz-source-ownwork' => 'These files are my own work.', |
| 33 | + 'mwe-upwiz-source-ownwork-assert' => 'I, $1, the copyright holder of this work, hereby grant anyone the right to use these works for any purpose, as long as they credit me and share derivative work under the same terms.', |
| 34 | + 'mwe-upwiz-source-ownwork-assert-custom' => 'I, $1, the copyright holder of this work, hereby publish these works under the following license(s):', |
| 35 | + 'mwe-upwiz-source-ownwork-assert-note' => 'This means you release your work under a double Creative Commons Attribution ShareAlike and GFDL license.', |
| 36 | + 'mwe-upwiz-source-permission' => 'Their author gave you explicit permission to upload them', |
| 37 | + 'mwe-upwiz-source-thirdparty' => 'These files are not my own work.', |
| 38 | + 'mwe-upwiz-source-thirdparty-intro' => 'Please enter the address where you found each file.', |
| 39 | + 'mwe-upwiz-source-thirdparty-custom-intro' => 'If all files have the same source, author, and copyright status, you may enter them only once for all of them.', |
| 40 | + 'mwe-upwiz-source-thirdparty-license' => 'The copyright holder of these works published them under the following license(s):', |
| 41 | + 'mwe-upwiz-source-thirdparty-accept' => 'OK', |
| 42 | + 'mwe-upwiz-source-custom' => 'Did you know? You can <a href="$1">customize</a> the default options you see here.', |
| 43 | + 'mwe-upwiz-more-options' => 'more options...', |
| 44 | + 'mwe-upwiz-fewer-options' => 'fewer options...', |
| 45 | + 'mwe-upwiz-desc' => 'Description in', |
| 46 | + 'mwe-upwiz-desc-add-n' => 'add a description in another language', |
| 47 | + 'mwe-upwiz-desc-add-0' => 'add a description', |
| 48 | + 'mwe-upwiz-title' => 'Title', |
| 49 | + 'mwe-upwiz-categories-intro' => 'Help people find your works by adding categories', |
| 50 | + 'mwe-upwiz-categories-another' => 'Add other categories', |
| 51 | + 'mwe-upwiz-previously-uploaded' => 'This file was previously uploaded to $1 and is already available <a href="$2">here</a>.', |
| 52 | + 'mwe-upwiz-about-this-work' => 'About this work', |
| 53 | + 'mwe-upwiz-media-type' => 'Media type', |
| 54 | + 'mwe-upwiz-date-created' => 'Date created', |
| 55 | + 'mwe-upwiz-location' => 'Location', |
| 56 | + 'mwe-upwiz-copyright-info' => 'Copyright information', |
| 57 | + 'mwe-upwiz-author' => 'Author(s)', |
| 58 | + 'mwe-upwiz-license' => 'License', |
| 59 | + 'mwe-upwiz-about-format' => 'About the file', |
| 60 | + 'mwe-upwiz-autoconverted' => 'This file was automatically converted to the $1 format', |
| 61 | + 'mwe-upwiz-filename-tag' => 'File name:', |
| 62 | + 'mwe-upwiz-other' => 'Other information', |
| 63 | + 'mwe-upwiz-other-prefill' => 'Free wikitext field', |
| 64 | + 'mwe-upwiz-showall' => 'show all', |
| 65 | + 'mwe-upwiz-source' => 'Source', |
| 66 | + 'mwe-upwiz-macro-edit-intro' => 'Please add some descriptions and other information to your uploads.', |
| 67 | + 'mwe-upwiz-macro-edit' => 'Update descriptions', |
| 68 | + 'mwe-upwiz-thanks-intro' => 'Thanks for uploading your works! You can now use your files on a Wikipedia article or link to them from elsewhere on the web.', |
| 69 | + 'mwe-upwiz-thanks-link' => 'This file is now available at <b><tt>$1</tt></b>.', |
| 70 | + 'mwe-upwiz-thanks-wikitext' => 'To use it in a Wikipedia article, copy this text into an article: ', |
| 71 | + 'mwe-upwiz-thanks-url' => 'To link to it in HTML, copy this HTML code: ', |
| 72 | + 'mwe-upwiz-upload-error-bad-filename-extension' => 'This wiki does not accept filenames with the extension "$1".', |
| 73 | + 'mwe-upwiz-upload-error-duplicate' => 'This file was previously uploaded to this wiki.', |
| 74 | + 'mwe-upwiz-upload-error-stashed-anyway' => 'Post anyway?', |
| 75 | + 'mwe-upwiz-ok' => 'OK', |
| 76 | + 'mwe-upwiz-cancel' => 'Cancel', |
| 77 | + 'mwe-upwiz-change' => '(change)', |
| 78 | + 'mwe-fileexists' => 'A file with this name exists already. Please check <b><tt>$1</tt></b> if you are not sure if you want to replace it.', |
| 79 | + 'mwe-thumbnail-more' => 'Enlarge', |
| 80 | + 'mwe-upwiz-overwrite' => 'Replace the file', |
| 81 | +); |
| 82 | +$messages['be-tarask'] = array( |
| 83 | + 'mwe-fileexists' => 'Файл з такой назвай ужо існуе. Калі ласка, праверце <b><tt>$1</tt></b>, калі Вы ня ўпэўненыя, што жадаеце яго замяніць.', |
| 84 | +); |
| 85 | +$messages['cs'] = array( |
| 86 | + 'mwe-fileexists' => 'Soubor s tímto jménem již existuje, prosím podívejte se na <b><tt>$1</tt></b>, pokud nevíte jistě, zda chcete tento soubor nahradit.', |
| 87 | +); |
| 88 | +$messages['de'] = array( |
| 89 | + 'mwe-fileexists' => 'Eine Datei mit diesem Namen existiert bereits. Bitte prüfe <b><tt>$1</tt></b>, wenn du dir bei der Änderung nicht sicher bist.', |
| 90 | +); |
| 91 | +$messages['diq'] = array( |
| 92 | + 'mwe-fileexists' => 'no name de ca ra yew dosya esta. eke şıma emin niê bıvurni, kerem kerê <b><tt>$1</tt></b> kontrol bıkerê.', |
| 93 | +); |
| 94 | +$messages['dsb'] = array( |
| 95 | + 'mwe-fileexists' => 'Dataja z toś tym mjenim južo eksistěrujo. Pšosym skontrolěruj <b><tt>$1</tt></b>, jolic njejsy wěsty, lěc coš ju změniś.', |
| 96 | +); |
| 97 | +$messages['es'] = array( |
| 98 | + 'mwe-fileexists' => 'Un archivo con este nombre ya existe. Por favor verifica <b><tt>$1</tt></b> si no est.ás seguro si deseas cambiarlo.', |
| 99 | +); |
| 100 | +$messages['fr'] = array( |
| 101 | + 'mwe-fileexists' => 'Un fichier existe déjà sous ce nom. Veuillez vérifier <b><tt>$1</tt></b> si vous n\'êtes pas sûr de vouloir le changer.', |
| 102 | +); |
| 103 | +$messages['gl'] = array( |
| 104 | + 'mwe-fileexists' => 'Xa existe un ficheiro con ese nome. Por favor, verifique <b><tt>$1</tt></b> se non está seguro de que quere cambialo.', |
| 105 | +); |
| 106 | +$messages['gsw'] = array( |
| 107 | + 'mwe-fileexists' => 'S het scho ne Datei mit däm Name. Bitte prief <b><tt>$1</tt></b>, wänn du nit sicher bisch, eb Du dr Name witt ändere.', |
| 108 | +); |
| 109 | +$messages['hsb'] = array( |
| 110 | + 'mwe-fileexists' => 'Dataja z tutym mjenom hižo eksistuje. Prošu skontroluj <b><tt>$1</tt></b>, jeli njesy sej wěsty, hač chceš ju změnić.', |
| 111 | +); |
| 112 | +$messages['hu'] = array( |
| 113 | + 'mwe-fileexists' => 'Már létezik ilyen nevű fájl. Ellenőrizd a(z) <b><tt>$1</tt></b> fájlt, ha nem vagy biztos benne, hogy le szeretnéd cserélni.', |
| 114 | +); |
| 115 | +$messages['ia'] = array( |
| 116 | + 'mwe-fileexists' => 'Un file con iste nomine ja existe. Per favor verifica <b><tt>$1</tt></b> si tu non es secur de voler cambiar lo.', |
| 117 | +); |
| 118 | +$messages['id'] = array( |
| 119 | + 'mwe-fileexists' => 'Suatu berkas dengan nama tersebut telah ada. Tolong cek <b><tt>$1</tt></b> jika Anda tidak yakin untuk mengubahnya.', |
| 120 | +); |
| 121 | +$messages['ja'] = array( |
| 122 | + 'mwe-fileexists' => '同名のファイルが既に存在しています。上書きしてよいかわからない場合は <b><tt>$1</tt></b> を確認してください。', |
| 123 | +); |
| 124 | +$messages['ksh'] = array( |
| 125 | + 'mwe-fileexists' => 'En Dattei met dämm Name jidd_et ald. Beß esu joot un donn <b><tt>$1</tt></b> prööfe, wann De Der nit sescher beß, of De jät ändere wells.', |
| 126 | +); |
| 127 | +$messages['lb'] = array( |
| 128 | + 'mwe-fileexists' => 'E Fichier mat dësem Numm gëtt et schonn. Kuckt w.e.g. op <b><tt>$1</tt></b> no wann Dir net sécher sidd ob Dir en ännere wëllt.', |
| 129 | +); |
| 130 | +$messages['ml'] = array( |
| 131 | + 'mwe-fileexists' => 'ഇതേ പേരിൽ ഒരു പ്രമാണം നിലവിലുണ്ട്. അതിൽ മാറ്റം വരുത്തണോ എന്നു താങ്കൾക്ക് ഉറപ്പില്ലങ്കിൽ ദയവായി <b><tt>$1</tt></b> കാണുക.', |
| 132 | +); |
| 133 | +$messages['nl'] = array( |
| 134 | + 'mwe-fileexists' => 'Er bestaat al een bestand met deze naam. Controleer <b><tt>$1</tt></b> als u niet zeker weet of u het huidige bestand wilt overschrijven.', |
| 135 | +); |
| 136 | +$messages['oc'] = array( |
| 137 | + 'mwe-fileexists' => 'Un fichièr amb aqueste nom existís ja. Mercé de verificar <b><tt>$1</tt></b> se sètz pas segur que lo volètz cambiar.', |
| 138 | +); |
| 139 | +$messages['pl'] = array( |
| 140 | + 'mwe-fileexists' => 'Plik o tej nazwie już istnieje. Sprawdź <b><tt>$1</tt></b> jeśli nie jesteś pewien czy chcesz go zastąpić.', |
| 141 | +); |
| 142 | +$messages['pt'] = array( |
| 143 | + 'mwe-fileexists' => 'Já existe um ficheiro com este nome. Por favor, verifique <b><tt>$1</tt></b> se não tem a certeza de que deseja alterá-lo.', |
| 144 | +); |
| 145 | +$messages['ru'] = array( |
| 146 | + 'mwe-fileexists' => 'Файл с этим именем уже существует. Пожалуйста, проверьте <b><tt>$1</tt></b>, если вы не уверены, что хотите заменить его.', |
| 147 | +); |
| 148 | +$messages['sk'] = array( |
| 149 | + 'mwe-fileexists' => 'Súbor s týmto názvom už existuje. Prosím, skontrolujte <b><tt>$1</tt></b> ak si nie ste istý, či ho chcete zmeniť.', |
| 150 | +); |
| 151 | +$messages['tr'] = array( |
| 152 | + 'mwe-fileexists' => 'Bu isimde bir dosya zaten mevcut. Değiştirmek istediğinize emin değilseniz lütfen <b><tt>$1</tt></b> kontrol edin.', |
| 153 | +); |
| 154 | +$messages['vi'] = array( |
| 155 | + 'mwe-fileexists' => 'Một tập tin với tên này đã tồn tại, xin hãy kiểm tra lại <b><tt>$1</tt></b> nếu bạn không chắc bạn có muốn thay đổi nó hay không.', |
| 156 | +); |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.ApiUploadHandler.js |
— | — | @@ -0,0 +1,100 @@ |
| 2 | +/** |
| 3 | + * An attempt to refactor out the stuff that does API-via-iframe transport |
| 4 | + * In the hopes that this will eventually work for AddMediaWizard too |
| 5 | + */ |
| 6 | + |
| 7 | +// n.b. if there are message strings, or any assumption about HTML structure of the form. |
| 8 | +// then we probably did it wrong |
| 9 | + |
| 10 | +/** |
| 11 | + * Represents an object which configures a form to upload its files via an iframe talking to the MediaWiki API. |
| 12 | + * @param an UploadInterface object, which contains a .form property which points to a real HTML form in the DOM |
| 13 | + */ |
| 14 | +mw.ApiUploadHandler = function( upload ) { |
| 15 | + var _this = this; |
| 16 | + _this.upload = upload; |
| 17 | + |
| 18 | + _this.configureForm(); |
| 19 | + |
| 20 | + // hardcoded for now |
| 21 | + // can also use Xhr Binary depending on config |
| 22 | + _this.transport = new mw.IframeTransport( |
| 23 | + _this.upload.ui.form, |
| 24 | + function( fraction ){ _this.upload.setTransportProgress( fraction ) }, |
| 25 | + function( result ) { _this.upload.setTransported( result ) } |
| 26 | + ); |
| 27 | + |
| 28 | +}; |
| 29 | + |
| 30 | +mw.ApiUploadHandler.prototype = { |
| 31 | + /** |
| 32 | + * Configure an HTML form so that it will submit its files to our transport (an iframe) |
| 33 | + * with proper params for the API |
| 34 | + * @param callback |
| 35 | + */ |
| 36 | + configureForm: function() { |
| 37 | + var apiUrl = mw.getLocalApiUrl(); // XXX or? throw new Error( "configuration", "no API url" ); |
| 38 | + if ( ! ( mw.getConfig( 'token' ) ) ) { |
| 39 | + throw new Error( "configuration", "no edit token" ); |
| 40 | + } |
| 41 | + |
| 42 | + var _this = this; |
| 43 | + mw.log( "configuring form for Upload API" ); |
| 44 | + |
| 45 | + // Set the form action |
| 46 | + try { |
| 47 | + $j( _this.upload.ui.form ) |
| 48 | + .attr( 'action', apiUrl ) |
| 49 | + .attr( 'method', 'POST' ) |
| 50 | + .attr( 'enctype', 'multipart/form-data' ); |
| 51 | + } catch ( e ) { |
| 52 | + alert( "oops, form modification didn't work in ApiUploadHandler" ); |
| 53 | + mw.log( "IE for some reason error's out when you change the action" ); |
| 54 | + // well, if IE fucks this up perhaps we should do something to make sure it writes correctly |
| 55 | + // from the outset? |
| 56 | + } |
| 57 | + |
| 58 | + _this.addFormInputIfMissing( 'token', mw.getConfig( 'token' )); |
| 59 | + _this.addFormInputIfMissing( 'action', 'upload' ); |
| 60 | + _this.addFormInputIfMissing( 'format', 'jsonfm' ); |
| 61 | + |
| 62 | + // XXX only for testing, so it stops complaining about dupes |
| 63 | + if ( mw.getConfig( 'debug' )) { |
| 64 | + _this.addFormInputIfMissing( 'ignorewarnings', '1' ); |
| 65 | + } |
| 66 | + }, |
| 67 | + |
| 68 | + /** |
| 69 | + * Add a hidden input to a form if it was not already there. |
| 70 | + * @param name the name of the input |
| 71 | + * @param value the value of the input |
| 72 | + */ |
| 73 | + addFormInputIfMissing: function( name, value ) { |
| 74 | + var _this = this; |
| 75 | + var $jForm = $j( _this.upload.ui.form ); |
| 76 | + if ( $jForm.find( "[name='" + name + "']" ).length == 0 ) { |
| 77 | + $jForm.append( |
| 78 | + $j( '<input />' ) |
| 79 | + .attr( { |
| 80 | + 'type': "hidden", |
| 81 | + 'name' : name, |
| 82 | + 'value' : value |
| 83 | + } ) |
| 84 | + ); |
| 85 | + } |
| 86 | + }, |
| 87 | + |
| 88 | + /** |
| 89 | + * Kick off the upload! |
| 90 | + */ |
| 91 | + start: function() { |
| 92 | + var _this = this; |
| 93 | + mw.log( "api: upload start!" ); |
| 94 | + _this.beginTime = ( new Date() ).getTime(); |
| 95 | + _this.upload.ui.busy(); |
| 96 | + $j( this.upload.ui.form ).submit(); |
| 97 | + }, |
| 98 | +}; |
| 99 | + |
| 100 | + |
| 101 | + |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.ApiUploadHandler.js |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 102 | + native |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.MockUploadHandler.js |
— | — | @@ -0,0 +1,37 @@ |
| 2 | +// TODO copy interface from ApiUploadHandler -- it changed |
| 3 | + |
| 4 | +// Currently this doesn't at all follow the interface that Mdale made in UploadHandler |
| 5 | +// will have to figure this out. |
| 6 | + |
| 7 | +// this should be loaded with test suites when appropriate. separate file. |
| 8 | +mw.MockUploadHandler = function(upload) { |
| 9 | + this.upload = upload; |
| 10 | + this.nextState = null; |
| 11 | + this.progress = 0.0; |
| 12 | + |
| 13 | +}; |
| 14 | + |
| 15 | +mw.MockUploadHandler.prototype = { |
| 16 | + |
| 17 | + start: function () { |
| 18 | + var _this = this; |
| 19 | + _this.beginTime = (new Date()).getTime(); |
| 20 | + _this.nextState = _this.cont; |
| 21 | + _this.nextState(); |
| 22 | + }, |
| 23 | + |
| 24 | + cont: function () { |
| 25 | + var _this = this; |
| 26 | + var delta = 0.0001; // static? |
| 27 | + _this.progress += ( Math.random() * 0.1 ); |
| 28 | + _this.upload.setTransportProgress(_this.progress); |
| 29 | + if (1.0 - _this.progress < delta) { |
| 30 | + _this.upload.setTransported(); |
| 31 | + } else { |
| 32 | + setTimeout( function() { _this.nextState() }, 200 ); |
| 33 | + } |
| 34 | + }, |
| 35 | + |
| 36 | +}; |
| 37 | + |
| 38 | + |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.MockUploadHandler.js |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 39 | + native |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.UploadApiProcessor.js |
— | — | @@ -0,0 +1,296 @@ |
| 2 | +// might be useful to raise exceptions when the api goes wrong |
| 3 | +// however, when everything is async, then who receives them? |
| 4 | +mw.UploadApiProcessor = function(doneCb, errorCb) { |
| 5 | + var _this = this; |
| 6 | + _this.doneCb = doneCb; |
| 7 | + _this.errorCb = errorCb; |
| 8 | +}; |
| 9 | + |
| 10 | + |
| 11 | +mw.UploadApiProcessor.prototype = { |
| 12 | + |
| 13 | + warnings: [ |
| 14 | + 'badfilename', // was the resultant filename different from desired? If so return what we actually got in the 'badfilename' warnings |
| 15 | + 'filetype-unwanted-type', // bad filetype, as determined from extenstion. content is the bad extension |
| 16 | + 'large-file', // $wgUploadSizeWarning, numeric value of largest size (bytes, kb, what?) |
| 17 | + 'emptyfile', // set to "true" if file was empty |
| 18 | + 'exists', // set to true if file by that name already existed |
| 19 | + 'duplicate', // hash collision found |
| 20 | + 'duplicate-archive' // hash collision found in archives |
| 21 | + ], |
| 22 | + |
| 23 | + errors: [ |
| 24 | + 'empty-file', |
| 25 | + 'filetype-missing', //(missing an extension) |
| 26 | + 'filetype-banned', // (extension banned) // also returns: { filetype => the filetype we thought it was, allowed => [ extensions ] } |
| 27 | + 'filename-tooshort', |
| 28 | + 'illegal-filename', // { filename => verification[filtered] } |
| 29 | + 'overwrite', // overwrite existing file (failed?) |
| 30 | + 'verification-error', // {details => verification-details} |
| 31 | + 'hookaborted', // error => verificationerror } |
| 32 | + 'unknown_error' |
| 33 | + ], |
| 34 | + |
| 35 | + |
| 36 | + // NB |
| 37 | + // It's not clear if we can even get these errors (error_msg_key, error_onlykey) any more |
| 38 | + |
| 39 | + // There are many possible error messages here, so we don't load all |
| 40 | + // message text in advance, instead we use mw.getRemoteMsg() for some. |
| 41 | + // |
| 42 | + // This code is similar to the error handling code formerly in |
| 43 | + // SpecialUpload::processUpload() |
| 44 | + error_msg_key: { |
| 45 | + '2' : 'largefileserver', |
| 46 | + '3' : 'emptyfile', |
| 47 | + '4' : 'minlength1', |
| 48 | + '5' : 'illegalfilename' |
| 49 | + }, |
| 50 | + |
| 51 | + // NOTE:: handle these error types |
| 52 | + error_onlykey: { |
| 53 | + '1': 'BEFORE_PROCESSING', |
| 54 | + '6': 'PROTECTED_PAGE', |
| 55 | + '7': 'OVERWRITE_EXISTING_FILE', |
| 56 | + '8': 'FILETYPE_MISSING', |
| 57 | + '9': 'FILETYPE_BADTYPE', |
| 58 | + '10': 'VERIFICATION_ERROR', |
| 59 | + '11': 'UPLOAD_VERIFICATION_ERROR', |
| 60 | + '12': 'UPLOAD_WARNING', |
| 61 | + '13': 'INTERNAL_ERROR', |
| 62 | + '14': 'MIN_LENGTH_PARTNAME' |
| 63 | + }, |
| 64 | + |
| 65 | + |
| 66 | + /** |
| 67 | + * Process the result of an action=upload API request, into a useful |
| 68 | + * data structure. |
| 69 | + * upload, |
| 70 | + * errors, |
| 71 | + * warnings |
| 72 | + * Augment with error messages in the local language if possible |
| 73 | + * |
| 74 | + */ |
| 75 | + processResult: function( result ) { |
| 76 | + var _this = this; |
| 77 | + mw.log( 'processResult::' ); |
| 78 | + |
| 79 | + // debugger; |
| 80 | + |
| 81 | + var parsedResult = _this.parseResult(result); |
| 82 | + |
| 83 | + if ( _this.doneCb && typeof _this.doneCb == 'function' ) { |
| 84 | + mw.log( "call doneCb" ); |
| 85 | + _this.doneCb( parsedResult ); |
| 86 | + |
| 87 | + } |
| 88 | + return true; |
| 89 | + }, |
| 90 | + |
| 91 | + |
| 92 | + parseResult: function( result ) { |
| 93 | + if ( result.upload && result.upload.imageinfo && result.upload.imageinfo.descriptionurl ) { |
| 94 | + result.isSuccess = true; |
| 95 | + } |
| 96 | + |
| 97 | + return result; |
| 98 | + |
| 99 | + } |
| 100 | + |
| 101 | + |
| 102 | +}; |
| 103 | + |
| 104 | + |
| 105 | + if ( result.error || ( result.upload && result.upload.result == "Failure" ) ) { |
| 106 | + |
| 107 | + // Check a few places for the error code |
| 108 | + var error_code = 0; |
| 109 | + var errorReplaceArg = ''; |
| 110 | + if ( result.error && result.error.code ) { |
| 111 | + error_code = result.error.code; |
| 112 | + } else if ( result.upload.code ) { |
| 113 | + if ( typeof result.upload.code == 'object' ) { |
| 114 | + if ( result.upload.code[0] ) { |
| 115 | + error_code = result.upload.code[0]; |
| 116 | + } |
| 117 | + if ( result.upload.code['status'] ) { |
| 118 | + error_code = result.upload.code['status']; |
| 119 | + if ( result.upload.code['filtered'] ) { |
| 120 | + errorReplaceArg = result.upload.code['filtered']; |
| 121 | + } |
| 122 | + } |
| 123 | + } else { |
| 124 | + result.upload.code; // XXX ?? |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + var error_msg = ''; |
| 129 | + if ( typeof result.error == 'string' ) { |
| 130 | + error_msg = result.error; |
| 131 | + } |
| 132 | + |
| 133 | + if ( !error_code || error_code == 'unknown-error' ) { |
| 134 | + if ( typeof JSON != 'undefined' ) { |
| 135 | + mw.log( 'Error: result: ' + JSON.stringify( result ) ); |
| 136 | + } |
| 137 | + if ( result.upload.error == 'internal-error' ) { |
| 138 | + // Do a remote message load |
| 139 | + errorKey = result.upload.details[0]; |
| 140 | + |
| 141 | + mw.getRemoteMsg( errorKey, function() { |
| 142 | + _this.ui.setPrompt( gM( 'mwe-uploaderror' ), gM( errorKey ), buttons ); |
| 143 | + |
| 144 | + }); |
| 145 | + return false; |
| 146 | + } |
| 147 | + |
| 148 | + _this.ui.setPrompt( |
| 149 | + gM('mwe-uploaderror'), |
| 150 | + gM('mwe-unknown-error') + '<br>' + error_msg, |
| 151 | + buttons ); |
| 152 | + return false; |
| 153 | + } |
| 154 | + |
| 155 | + if ( result.error && result.error.info ) { |
| 156 | + _this.ui.setPrompt( gM( 'mwe-uploaderror' ), result.error.info, buttons ); |
| 157 | + return false; |
| 158 | + } |
| 159 | + |
| 160 | + if ( typeof error_code == 'number' |
| 161 | + && typeof _this.error_msg_key[error_code] == 'undefined' ) |
| 162 | + { |
| 163 | + if ( result.upload.code.finalExt ) { |
| 164 | + _this.ui.setPrompt( |
| 165 | + gM( 'mwe-uploaderror' ), |
| 166 | + gM( 'mwe-wgfogg_warning_bad_extension', result.upload.code.finalExt ), |
| 167 | + buttons ); |
| 168 | + } else { |
| 169 | + _this.ui.setPrompt( |
| 170 | + gM( 'mwe-uploaderror' ), |
| 171 | + gM( 'mwe-unknown-error' ) + ' : ' + error_code, |
| 172 | + buttons ); |
| 173 | + } |
| 174 | + return false; |
| 175 | + } |
| 176 | + |
| 177 | + mw.log( 'get key: ' + _this.error_msg_key[ error_code ] ) |
| 178 | + mw.getRemoteMsg( _this.error_msg_key[ error_code ], function() { |
| 179 | + _this.ui.setPrompt( |
| 180 | + gM( 'mwe-uploaderror' ), |
| 181 | + gM( _this.error_msg_key[ error_code ], errorReplaceArg ), |
| 182 | + buttons ); |
| 183 | + }); |
| 184 | + mw.log( "api.error" ); |
| 185 | + return false; |
| 186 | + } |
| 187 | + |
| 188 | + |
| 189 | + } |
| 190 | + */ |
| 191 | + |
| 192 | +/* |
| 193 | + // this doesn't seem to do anything |
| 194 | + // Check for warnings: |
| 195 | + if ( result.upload && result.upload.warnings ) { |
| 196 | + for ( var wtype in result.upload.warnings ) { |
| 197 | + var winfo = result.upload.warnings[wtype] |
| 198 | + switch ( wtype ) { |
| 199 | + case 'duplicate': |
| 200 | + case 'exists': |
| 201 | + if ( winfo[1] && winfo[1].title && winfo[1].title.mTextform ) { |
| 202 | + push warnings, { type: wtype, text: winfo[1].title.mTextform }, |
| 203 | + } else { |
| 204 | + push warnings, { type: wtype } |
| 205 | + } |
| 206 | + break; |
| 207 | + case 'file-thumbnail-no': |
| 208 | + push warnings, { type: wtype, info: winfo } |
| 209 | + break; |
| 210 | + default: |
| 211 | + push warnings: { type: wtype, } |
| 212 | + break; |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + if ( result.upload.sessionkey ) { |
| 217 | + _this.warnings_sessionkey = result.upload.sessionkey; |
| 218 | + } |
| 219 | + |
| 220 | + } |
| 221 | +*/ |
| 222 | +/* |
| 223 | + // Check upload.error |
| 224 | + if ( result.upload && result.upload.error ) { |
| 225 | + mw.log( ' result.upload.error: ' + result.upload.error ); |
| 226 | + _this.ui.setPrompt( |
| 227 | + gM( 'mwe-uploaderror' ), |
| 228 | + gM( 'mwe-unknown-error' ) + '<br>', |
| 229 | + buttons ); |
| 230 | + return false; |
| 231 | + } |
| 232 | +*/ |
| 233 | + |
| 234 | + /* |
| 235 | + // this ONLY applies to copy by URL method -- factor out? |
| 236 | + if ( result.upload && result.upload.upload_session_key ) { |
| 237 | + // Async upload, do AJAX status polling |
| 238 | + _this.upload_session_key = result.upload.upload_session_key; |
| 239 | + _this.doAjaxUploadStatus(); |
| 240 | + mw.log( "set upload_session_key: " + _this.upload_session_key ); |
| 241 | + return; |
| 242 | + } |
| 243 | + */ |
| 244 | + |
| 245 | + /* |
| 246 | + var buttons = {}; |
| 247 | + // "Return" button |
| 248 | + buttons[ gM( 'mwe-return-to-form' ) ] = function() { |
| 249 | + $j( this ).dialog( 'destroy' ).remove(); |
| 250 | + _this.form_post_override = false; |
| 251 | + } |
| 252 | + // "Go to resource" button |
| 253 | + buttons[ gM('mwe-go-to-resource') ] = function() { |
| 254 | + window.location = url; |
| 255 | + }; |
| 256 | + _this.action_done = true; |
| 257 | + _this.interface.setPrompt( |
| 258 | + gM( 'mwe-successfulupload' ), |
| 259 | + gM( 'mwe-upload_done', url), |
| 260 | + buttons ); |
| 261 | + mw.log( 'result.upload.imageinfo::' + url ); |
| 262 | + return true; |
| 263 | + */ |
| 264 | + |
| 265 | +/* |
| 266 | + // Create the "ignore warning" button |
| 267 | + var buttons = {}; |
| 268 | + buttons[ gM( 'mwe-ignorewarning' ) ] = function() { |
| 269 | + // Check if we have a stashed key: |
| 270 | + if ( _this.warnings_sessionkey ) { |
| 271 | + //set to "loading" |
| 272 | + $j( '#upProgressDialog' ).html( mw.loading_spinner() ); |
| 273 | + //setup request: |
| 274 | + var request = { |
| 275 | + 'action': 'upload', |
| 276 | + 'sessionkey': _this.warnings_sessionkey, |
| 277 | + 'ignorewarnings': 1, |
| 278 | + 'filename': $j( '#wpDestFile' ).val(), |
| 279 | + 'token' : _this.editToken, |
| 280 | + 'comment' : _this.getUploadDescription() |
| 281 | + }; |
| 282 | + //run the upload from stash request |
| 283 | + mw.getJSON(_this.api_url, request, function( data ) { |
| 284 | + _this.processApiResult( data ); |
| 285 | + } ); |
| 286 | + } else { |
| 287 | + mw.log( 'No session key re-sending upload' ) |
| 288 | + |
| 289 | + |
| 290 | + //do a stashed upload |
| 291 | + $j( '#wpIgnoreWarning' ).attr( 'checked', true ); |
| 292 | + $j( _this.editForm ).submit(); |
| 293 | + } |
| 294 | + }; |
| 295 | + */ |
| 296 | + |
| 297 | + |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.UploadApiProcessor.js |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 298 | + native |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/README |
— | — | @@ -0,0 +1,66 @@ |
| 2 | +What this is |
| 3 | +============ |
| 4 | + |
| 5 | +It's the first stab at a more usable multimedia uploader, particularly for Wikimedia Commons. |
| 6 | + |
| 7 | +Main features: |
| 8 | + - Multiple simultaneous file uploads |
| 9 | + - Batch assertion of license ("all these files were CC-BY-SA") |
| 10 | + - "Incomplete" uploads, where the data can be uploaded and metadata edited without really adding the file to the collection. |
| 11 | + - (Eventually) Gee-whiz technology like HTML5 drag & drop from the desktop |
| 12 | + |
| 13 | +http://usability.wikimedia.org/wiki/Multimedia:NewUpload |
| 14 | + |
| 15 | + |
| 16 | +General disclaimer |
| 17 | +=================== |
| 18 | + |
| 19 | +For the moment, (March 2010) UploadWizard is all alpha quality code. |
| 20 | +It often does not conform to the MediaWiki coding standards. This will have to be fixed before it gets merged into trunk. |
| 21 | + |
| 22 | +It uses MwEmbed because it was found to be very difficult to separate it from the framework. |
| 23 | +Furthermore, it is somewhat redundant with the AddMedia module. AddMedia has a lot of useful code but |
| 24 | +not factored in the way that would be most convenient for this module. Michael Dale (mdale) and myself (NeilK) |
| 25 | +are attempting to refactor things and meet in the middle. |
| 26 | + |
| 27 | +Presently (March 2010) we are sprinting to get a hacky interface up to conduct a usability test on our |
| 28 | +basic ideas about updating media uploads for commons. Nothing here is expected to stay here permanently. |
| 29 | + |
| 30 | +Stay tuned, or, talk to NeilK about this, if anything here bothers you. |
| 31 | + |
| 32 | + |
| 33 | +How to use it |
| 34 | +============= |
| 35 | + |
| 36 | +This is *very* complicated at present. Obviously we want to make this simpler in the future. |
| 37 | + |
| 38 | +Currently mdale tests with the live Commons site by loading in code from a JS2-enabled prototype. |
| 39 | +This is accomplished with a gadget on Commons (or other additions to the skin). |
| 40 | + |
| 41 | +Here's how you might do such a thing: |
| 42 | + |
| 43 | +- Set up an instance of MediaWiki from trunk; or, if feeling brave[1], use a real site like Commons itself. |
| 44 | +- Set up an instance of MediaWiki from js-work branch, which your browser can access. |
| 45 | +- On this trunk-based Mediawiki, install a gadget which is invoked on all pages to load some code from the JS2-enabled site. Since |
| 46 | + this is all accomplished in your browser, you could use a local hostname. (See below). |
| 47 | +- Point your browser to the upload page on the trunk-based mediawiki, with a special parameter: Special:Upload?uploadWizard=1 . |
| 48 | + |
| 49 | + |
| 50 | +[1] Currently I (NeilK) use a local MediaWiki, since we are uploading stuff *without* the normal checks |
| 51 | + that the Commons people have carefully added. |
| 52 | + |
| 53 | +[2] The gadget might look like this: |
| 54 | + |
| 55 | +----- cut here ---- |
| 56 | +// Once mwEmbed is "on" we can use mw.setConfig calls: |
| 57 | +if( typeof mwAddMediaConfig == 'undefined'){ |
| 58 | +mwAddMediaConfig = {}; |
| 59 | +} |
| 60 | +mwAddMediaConfig['enabled_providers'] = [ 'wiki_commons', 'upload' ] |
| 61 | + |
| 62 | +importScriptURI('http://js2-work.yoursite.local/js/mwEmbed/remotes/mediaWiki.js?&uselang=en&debug=true' ); |
| 63 | +----- cut here ---- |
| 64 | + |
| 65 | + |
| 66 | + |
| 67 | + |
Index: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.IframeTransport.js |
— | — | @@ -0,0 +1,108 @@ |
| 2 | +/** |
| 3 | + * Represents a "transport" for files to upload; in this case an iframe. |
| 4 | + * The iframe is made to be the target of a form so that the existing page does not reload, even though it's a POST. |
| 5 | + * @param form an HTML form |
| 6 | + * @param progressCb callback to execute when we've started. (does not do float here because iframes can't |
| 7 | + * monitor fractional progress). |
| 8 | + * @param transportedCb callback to execute when we've finished the upload |
| 9 | + */ |
| 10 | +mw.IframeTransport = function( form, progressCb, transportedCb ) { |
| 11 | + var _this = this; |
| 12 | + |
| 13 | + _this.form = form; |
| 14 | + _this.progressCb = progressCb; |
| 15 | + _this.transportedCb = transportedCb; |
| 16 | + |
| 17 | + _this.iframeId = 'f_' + ( $j( 'iframe' ).length + 1 ); |
| 18 | + |
| 19 | + //IE only works if you "create element with the name" ( not jquery style ) |
| 20 | + var iframe; |
| 21 | + try { |
| 22 | + iframe = document.createElement( '<iframe name="' + _this.iframeId + '">' ); |
| 23 | + } catch ( ex ) { |
| 24 | + iframe = document.createElement( 'iframe' ); |
| 25 | + } |
| 26 | + |
| 27 | + // we configure form on load, because the first time it loads, it's blank |
| 28 | + // then we configure it to deal with an API submission |
| 29 | + $j( iframe ) |
| 30 | + .attr( { 'src' : 'javascript:false;', |
| 31 | + 'id' : _this.iframeId, |
| 32 | + 'name' : _this.iframeId } ) |
| 33 | + .load( function() { _this.configureForm() } ) |
| 34 | + .css( 'display', 'none' ); |
| 35 | + |
| 36 | + $j( "body" ).append( iframe ); |
| 37 | +}; |
| 38 | + |
| 39 | +mw.IframeTransport.prototype = { |
| 40 | + /** |
| 41 | + * Configure a form with a File Input so that it submits to the iframe |
| 42 | + * Ensure callback on completion of upload |
| 43 | + */ |
| 44 | + configureForm: function() { |
| 45 | + mw.log( "configuring form for iframe transport" ); |
| 46 | + var _this = this; |
| 47 | + // Set the form target to the iframe |
| 48 | + var $jForm = $j( _this.form ); |
| 49 | + $jForm.attr( 'target', _this.iframeId ); |
| 50 | + |
| 51 | + // attach an additional handler to the form, so, when submitted, it starts showing the progress |
| 52 | + // XXX this is lame .. there should be a generic way to indicate busy status... |
| 53 | + $jForm.submit( function() { |
| 54 | + mw.log( "submitting to iframe..." ); |
| 55 | + _this.progressCb( 1.0 ); |
| 56 | + return true; |
| 57 | + } ); |
| 58 | + |
| 59 | + // Set up the completion callback |
| 60 | + $j( '#' + _this.iframeId ).load( function() { |
| 61 | + mw.log( "received result in iframe" ); |
| 62 | + _this.processIframeResult( $j( this ).get( 0 ) ); |
| 63 | + } ); |
| 64 | + }, |
| 65 | + |
| 66 | + /** |
| 67 | + * Process the result of the form submission, returned to an iframe. |
| 68 | + * This is the iframe's onload event. |
| 69 | + * |
| 70 | + * @param {Element} iframe iframe to extract result from |
| 71 | + */ |
| 72 | + processIframeResult: function( iframe ) { |
| 73 | + var _this = this; |
| 74 | + var doc = iframe.contentDocument ? iframe.contentDocument : frames[iframe.id].document; |
| 75 | + // Fix for Opera 9.26 |
| 76 | + if ( doc.readyState && doc.readyState != 'complete' ) { |
| 77 | + mw.log( "not complete" ); |
| 78 | + return; |
| 79 | + } |
| 80 | + |
| 81 | + // Fix for Opera 9.64 |
| 82 | + if ( doc.body && doc.body.innerHTML == "false" ) { |
| 83 | + mw.log( "no innerhtml" ); |
| 84 | + return; |
| 85 | + } |
| 86 | + var response; |
| 87 | + if ( doc.XMLDocument ) { |
| 88 | + // The response is a document property in IE |
| 89 | + response = doc.XMLDocument; |
| 90 | + } else if ( doc.body ) { |
| 91 | + // Get the json string |
| 92 | + // XXX wait... why are we grepping it out of an HTML doc? We requested jsonfm, why? |
| 93 | + json = $j( doc.body ).find( 'pre' ).text(); |
| 94 | + mw.log( 'iframe:json::' + json ) |
| 95 | + if ( json ) { |
| 96 | + response = window["eval"]( "( " + json + " )" ); |
| 97 | + } else { |
| 98 | + response = {}; |
| 99 | + } |
| 100 | + } else { |
| 101 | + // Response is a xml document |
| 102 | + response = doc; |
| 103 | + } |
| 104 | + // Process the API result |
| 105 | + _this.transportedCb( response ); |
| 106 | + } |
| 107 | +}; |
| 108 | + |
| 109 | + |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule/mw.IframeTransport.js |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 110 | + native |
Property changes on: trunk/extensions/UploadWizard/UploadWizardJsModule |
___________________________________________________________________ |
Name: svn:mergeinfo |
2 | 111 | + /branches/REL1_15/phase3/js2/mwEmbed/modules/UploadWizard:51646 |
/branches/sqlite/js2/mwEmbed/modules/UploadWizard:58211-58321 |