Commit 899c2c75511d9167407ddd7bcecfb1604cf87bdb

Add README and example json

  Intermediate commit to save the current work progress.
Added better README. Added a example.json file. Should make that more
comprehensive.
  • README.md 89 --+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • example.json 42 ++++++++++++++++++++++++++++++++++++++++++
  • img/mouchak.jpg 0 
  • index.html 15 ------+++++++++
  • js/framework.js 493 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  • js/main.js 1 -
  • js/mouchak.js 489 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • Diff rendering mode:
  • inline
  • side by side

README.md

1mouchak
1Mouchak
2=======2=======
33
4A framework for building websites.
4A Javascript framework for building single page websites or web apps.
5
6It takes the content of the website as a JSON. The JSON file can contain
7text or multimedia content(images, audio, video).
8
9Mouchak can also load external JS/CSS files through the JSON. This gives the website to load
10plugins to enhance/customize the website either through JS or CSS.
11
12
13How to use it
14=============
15
16Either download this codebase or git clone the repo.
17
18Once you have downloaded or cloned the repo, load the index.html file in your browser.
19This loads the example content from example.json.
20
21Use the index.html file as the boilerplate file of your index.html file.
22Modify the code in the script tag, which loads the example.json, and change
23the URL to point to your JSON file.
24Remember the JSON files is loaded in the client side. Hence your JSON file should
25be from the same domain where this app is loaded.
26See cross-domain policies for details.
27
28The global object for this framework is exposed as the variable M. This can be
29inspected in the console.
30
31The M.filterTags() method takes an array of strings representing the tags to filter.
32It returns an array of the content objects with the matched tags.
33
34
35How it works
36============
37
38It takes the content of the site in the form of JSON. The JSON should describe the content
39(in terms of type like text, audio or image) and also provide the content (in case of text
40the content is as it is, in case images urls are given). The JSON should also describe the
41content semantically by giving it tags.
42More details about the JSON format in example.json file.
43
44The framework provides an easy way to pull up related content by just specifying the tags.
45
46Backbone models are used to model the content. Different content has different types.
47Again all types are derived from one base type.
48Each model has a corresponding view as well. The views manage how the content are rendered.
49
50Each page is made of multiple content (content objects).
51Pages are also modeled in Backbone models and their view is managed via Backbone views.
52
53The framework also use Backbone router to handle client side routing.
54
55
56What it uses
57============
58
59Mouchak uses HTML5 Boilerplate and Bootstrap project as a boilerplate code for the website.
60Mouchak also leverages powerful libraries like Backbone.js and Underscore.js to manage and render
61content. This gives more flexibility to the content of the website.
62
63The main code resides in js/mouchak.js. The HTML markup it uses is in index.html.
64
65Javascript libary files are in js/lib. We use backbone.js, underscore.js and jquery in this
66framework.
67
68Boilerplate code/files:
69404.html - error template
70crossdomain.xml - cross-domain policies to be obeyed by the client
71css/bootstrap.css - boilerplate css
72css/normalize.css - boilerplate css
73css/main.css - boilerplate css
74humans.txt - write your own credits
75img/ - directory for images
76robots.txt - crawl spider rules
77
78
79Support
80=======
81
82Email to rayanon at janastu dot org for any kind of feedback.
83
84
85Issues
86======
87
88Report issues at http://bugzilla.pantoto.org/bugzilla3/

example.json

1[
2 {
3 "name": "index",
4 "children": ["Motivation", "The JSON file"],
5 "title": "Mouchak",
6 "content": [
7 {
8 "type": "image",
9 "src": "img/mouchak.jpg"
10 },
11 {
12 "type": "text",
13 "title": "What's this?",
14 "data": "<p> Mouchak is a Javascript framework for building websites quickly.</p><p>It takes the components and the content of the website in a JSON format. The content in the JSON can be of type text or multimedia like images and videos.</p> It also has the capabilities to load external JS and CSS files to customize the website."
15 }
16 ]
17 },
18 {
19 "name": "Motivation",
20 "title": "Motivation",
21 "children": [],
22 "content": [
23 {
24 "type": "text",
25 "title": "Why Mouchak?",
26 "data": "Explanation of why we built this framework..."
27 }
28 ]
29 },
30 {
31 "name": "The JSON file",
32 "title": "The JSON format",
33 "children": [],
34 "content": [
35 {
36 "type": "text",
37 "title": "",
38 "data": "{ } Explain the JSON format here.."
39 }
40 ]
41 }
42]

img/mouchak.jpg

1JFIFXX XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv
2#(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y
3
4'
5=
6T
7j
8
9
10
11
12
13 " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""#
14#8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<'<e<<="=a==> >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G
15k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4
16uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.!
17zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmC   
18
19
20
21
22
23
24
25
26C
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77d|
78 }!1AQa"q2#BR$3br
79%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
80 w!1AQaq"2B #3Rbr
81$4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?
82(
83(
84(
85(
86lEQi%OU.Ĺu+|5Փ^^5~Q/m?;7o^n?g>IuxRaHw'ZVُsr˰U`G4
87(
88(
89(
90(
91(
92(
93(
94(
95(;[8뻔#?Zi7KsPZe-ˆpTh-{߉:ċ+HeZN]׊<Ax}Ll*"M)I,6df>sT@(
96(G,6e>q@C[ծԸ)NKz#d]"8iBlJSICٔӴ eMc ԺrFii
97)}@OP[)E (
98(j-GVbOkW _X @ԧ?:ʭt0w@+7ļ]ü!UZꚗµrbݺV8RvB|bvo
994k?5O]-|OxZrv$bvj\E 88߆8y^!NQPiiwvmyCsA
100(
101(
102(
103(("tUMD;~5QJq/|DԮL[
1044U0nno%3]<z'RHɶ#A@P@P@P@A8# k~%;ҥ,RqmiƳtѻCp9>VzjB[3B<
105Uj_?S+^xëj.S2;rSc^~
106y.u똿k̗TpwSHNfB* +2|ԕTԾZm6Sgo??+=W@%_iJH|V,VPA׃ٖqfP̲8ԤujN}ٽ/"7G7῏<ScuᾔꏙQƓʉdnqf`~RF]x#\m _ [G^Mu(J}p
107(
108(
109(
110(+M:^αƽY=M4vBmE]쵶 1OoM-iocwy#brYI5P@P@P@P@P@PsIx^1,GR\jN'KD.5kEt bkyUцU ei|%WEYI̸߲.wq^V|K"Ūo^Gaݟ7F@?Z?rĖ?͜د׾)GXN؟G= aycʛu6WB*4߲sQjaTߦ1EZ_۶{8[ZINM|xWY=?Q$,+_VE''zFtӒyi{E)9L겂
111(
112(
113(
114,|?!S
115uVu j7n?ƒQt(YIݕiP@P@P@P@P@P@f}'U H˻ =g$\$#?^X*~3"𥔗Tz6fP8z=υnҿi
116N1iyrr" \t9Pfy5^9\ִjJ+7-5su]^M$i.q,mH,Y<'xAOx]J|9wꦫ#}OltP]6]zoӵ1+zB*\Vt+_)&Iy|GuiO鷗.ǖ[X݉X;6:(
117(
118(
119_!Т-ׁ
1202QCE\\Ow;\rǒk$Fwc(
121(R8cgcTd<C"]|u<ZB旨c,Cx{P@P@P@P@/]McKӸ3dW5H򳮔cNq MǗ>Hmtnbrp[̲2F2A8zXyÈ0T?ߒv~J}3)<G㈏gYRXuKl}v3ƿ2ͺaFڬjSc
122/D_~h3➱–Z @PS*dYUl׏p|ݣVZU}Ei#|>೿I؆-;ój:lzmuȌ1RǦ?/+&+:r89M.DmsqgƺڬV;Vmڤ
123?ސVTq|Eԭ٧ϲ<H𽗎>K״]F.hwVQ3#+`5(
124(
125( x=1BGU<Τ5qq!wv3Iqۻ@P@cJg#rOEIUq.N쵭cxƞ%4-Kk[\oc~M! sJNLcg?%͗7^7%+ۿ MN'@B?ڍObke{K^Ҍ6Zu?K_/|l~- ~'h7bN#21<Y c)L=H=ԗޛ>`kE@2F!AzWt'}V.S
126(
127(
128(
129: f>v3?_RҒQ</
1307g_~phj?|% SkFE ?.|`=0َjut^ϣRSf<5 &;^>Φ^}F9c#xHݹJȃq ͸s
131
132u*A:qpەs3<OsߋĿhxK/P<CR M@X* PQϥ?da,DJҼ!)ʮZjKf43uWί~]bxw:/@Ek'4d5>w<yB?1A9K֍^EըRλ؟Pj<|_Mx#NtueunE" c_S*j*Z҄;5Vh8xLkۇ ,־d:_rB.73Z[EQFk /iE?i
133FeFOH?zmm'zg?ijy~Xn$MmdUuk/Ue`yT2:(
134(+k`GUYN)ؙIE]w}W^^i<LWRI+#ݕënF>@P@|2Wa"$!YUz$oAjO뷿U/)?ewC~O<Wq (A3=$V<?R؜E.mzM/tpRmdv^-~m/>t-OC}EQb:ʥIjms;#>Z0x_ž0M7ִ'"K3/̒|*F3W쳾YJ*M^<zSI%B_g? !TWjN^*gm i4ÓpCD՞x-̭dZ*[S[-$HKUciJgR{~WwW<GMiP@P@P@u@qh?Tn@xwÏٟᶯč7,aD% 0U5$ /y?_
135m%Uwvi,0kZjt)hXv?ye~ζ?#xfƟ -@̣9ܖmGv&+W'N_OV*gl>I/Mj*h+#/5[QERd#>blz0a)h֚hkNsUfVWVLnc@Vъ3n??]_VÚn+QN?)
136);azlw;X(VR\鮭IjK\ Qө}yx#?=OPV,5 A><8oFn>ʤ0
137(hej&(V,tcݿjyQR|1/e܍h//(
138(>u*3Uԯ,?i?HԘQr1oi|CM ؿJ_掉JyGƏ9]i5`ik.kI!d\/K9"5reK FMn}4It[=+|]:~_>xGB".fM.Xy#;22ǘ  QR JrM{WN[$Nz_3}^,wgeaYi?b#? kG%i*G )^W yWeeJJ-|~LQJ2Ks//7"4_7&K fݫ-_j 
139(
140(
141(Ҁ&_6%MOZ˰gs¯rQܨRz:ݿV/ =ǀ.$g伳a;O10vw#lyuF)Kmqplm*5GVyDkm/Rf%l4ɗZGE>K] %%q"DT`ӰKMLgͦJ˟o nK/j*CK;vKV1șa[
142:RѦOsEVZ-xCBS' &d4:'? an{;9Y繗὜n++'N<F?n" 5/9Y'F?,15?&NZ߻e[Kys9yX=N}f-gǺ'/)?zn-t2< #> ]r~ȸ}K;n[~WwvPv?RHaEHQ@P+,}P@P4'R}iN7Yr] P@_/(˹/,^^[r7oP@PY^/ '?JƯCS3Oߴw?7
143M:mг\r:Ϗ9c Λ̿T{VߴG_?kj-J$4Cq 2@ȯ<D,?=b((SroIʯ)[^_#Q՟WCVvV[K!T*"UTrm;ovޭ6dJNR<&Up~jovՇ6発J2QU6T2;7Y,ZT'ՔW&YAZ/gJ𿄴l" .q8vPGhT|l
144(
145(
146: 3D_I?,RxK[[S[_/K5ZE Ch Fޯ#"[X@y769U0fJ[F_~$|2|s/| X-PyGP;"Z J2;f4Q?^͞,%J`ӻ1`$6LxzWw?gbմZ/v榭?{
147Ƭ*/tyIAwZ] *K4tӴV(mE
148*(Vт:eZ F_"nғin5-'/R/3*xDS _cW5zYr+QU]s#J2i򳫬N
149FeA`2@=_'i~ŃLgRhqQ+G2C.s'5,ngR)mG yגg7mn|WEWr?2oA^OJ>$ |7$GO?71쫶Vx FاkϽ7=OJ?ҵmj(S7a(Wx/c?˸]NόI? 7h{{;A>ME$#W qOvBfȿUz:tCG'g#PO5æG=D/eL|m&eU_?bO9In#bun-2)#@ Z_z'ԡaz4/a.y>x>[?k}$/駆ve~U;I_EO=؅Mf?EO=%t[kʅK۟AzҎZO_R@Ix/ǭk yF-\3(:ZnqU~ދM+WΗ&~ӿ6|O?iI^?:eƋ O xR+e0NF.",_01*Y4g 䧣k;%xK'.[~=ǟP^hv<1mF]I_tJlei ~ a9Wc% =mt䕮S?Sgn g{ hVN,ɒ[%L7Wfl3}:{_b*TIZ&nsP@P@P sUhbIQw5qq9;~?U_^'x\a%¯3kڸpVdVI;Y[hoQ;{_;xT,<+<K]ZFOr8+`0 -_cW.o?xxi_5y&7GE<vM| #4mV(lyqf_+WN}ڝҥFߖ[-o>:<[Uk7_-x/὞eЅ]Ψ(oa0pݽ؉)T.PZs#szA^I+#mݐٯC~l<%[=gQ5;aR4 *N-!MWT~_|85k ݾ-a˖YX@o_{,&/^:IE/4[;=F~&D_S3.?F~0NzV(7&q2f 20!HUc^ xٽ<4<45MOz%/v.7z_U}-EJ<w?N~|pkP|og_Q{%M"PLR'lIG\7~VaQ}IcYclTj
150K
151WwU>:j P!Vxc HjqN6pP~dXhʴC郧JҼu} DgP<,+hb1ٜџGSԟFl1k3 3ewye)M}Ji7 7Qno戤u!͡K'mgKK~:Y5?xnGRlqNa1'g<#ٵT]OgVӆskcMkY5a.٩2<EK~w|?y|+j;z T8 zY,ٚw_g]_>ęqi'Ξˏºlr^s#d`&aKm_Nw}_QoP\\WTs|_0SW/-E< zFS=8De U8RT4XXdw/ط
152^[/YE}ٯm9OlWTkPԇĚK}eZtu4cA&ǩ3#QW ϕL*,\MyZW [ R&.rKdc-@e^>Oh.ijq~tcBԿٮIo1o&n 9Atr߳AC>6_Q)_5/ ݍCpDD]֗ReG mA8itDev}s*8t$(Q2T'_1uOz1^QkEc)T3
153(
154(
155:Ʃc_I+r%-?f3h06b8ݱҲs3u{?ϖ o#?aj/Z*3f U ԰kQ)?c_+7dE._{*?f|; ]fGVasbkzlծU[=Fd?\|1+a^HW2rAw9=k3VNQK.S֤ԥyo oW9?<+Q` m[eɇ?*E /fx4bKF/Ļ{v#<=dߊH~?h?:xm|M\pG3nt?1n
156
157G*tϭg97y_mj ד+HtU`\xՕuSp}Zi?vJK-URa+Hٯ ٲֲf O 1s8BgoϨxQ.#4RB($$@O~:*/]/MPCkS5em<Eun)O{ n0NTbO7ž?*K 6}5SZS)'y5IjS8߯B_w|>wǾ þGm>Ɋ/'˷Q ʀ0w2rʘa[MXjSnRg !1?~<8=B3A(I8'0I]tZGn rSg?F^&?~xK}U<դ-PK ѐ\#B;W-jv}[{gcE1 1Jj
158NFAP@D.gRväZ$Y͍~uĭ#rDz+=q4HIfcMgkoa}8Nc!ٱN)k>#|?jU 7G&6XZIZ(yv?\F
159(.7COqnN3Xv?$V.TȔy[<Q.@]Y͓mqmv)Xw ֎uYN5!FM%sCMH`oD rr|9> YL`ޚ蟶9|NT?&M߭vSϳZ?Sطf⯇߷}b2w:UԚ)u F|b(p6:B"1xliWjMO<(J$.Oe|i'|Ku6gkëuCG-b|TWK>iOxßؗ}LsZVMj 
160($mH}#B 4ҳO?aT:ES}
161O?</'<cX? S<pꢡodhwg˚7յ |K 2A閒7:ȎG 9novh?,Wǵu/uyϠsg|p#?z7ca5,y/nr?n>߈~)@ǿIxtn.<Zn,129;KE.auTײ_#o~
162~WZZ\xzQ%ޑb&Ҭӻ6ǖ*_*'+;wMW}ibN^{{5][~ܿߍ"P.4
163Gڠ:gn8cO]  M+u~=\N"nl}? wóۀQ7]:<s]|g35oN|-n(n*QO 1]ao(sm:~ǿ$RZ|f+8"DWK"c\E)>N!~/ >)֞ _⋝2kY^(Y
164?AG4<
165 |>?˟sek<; O}og{oЕy](e4'~p~ʿſDu۾Bގׇ6)˧}X24rߺ?˸GRʥ:zլjMSIE]cs[/ F|? ZG=(9!~N׀[
16683|81\xfj7Wy?a
167s]Tg[c߀uo
168~ɾ`v/iyb o *K$nHTU{'xW_7i䚌V{볛ZiWIyy#?M|e({00ӰީÕjp՟3i9m.ܟ>𨪵4֎7
169(*u-6 6=KE0-<;7`)"ck8.zI[4}VCUK*}S |RƤEe08)r0xqXn⟎R9܆`d9z(fQ'uv&_u@oڕk_Fmb8Fzqs5$rI?Rh$y/4=&2MP<Uixސ;} SvX `;ŏC)륺츠.˰|iqqٿ#@Зz~K Gqi>͸+te;Nr 'ˣ2$[kn.F&NFuF\de~dI8rL#SGR|E(cT:̾cPԠ5)m/e| <S )->Y￵'?߅&tsͻ[:gL9{3;E+2'YNc
170i%>0qs%Ef/? cwM;>|1Bq9~9rqOXHS[JѯZ0Wñ9Eّ_^;9e? T6I;od٬@@?AKZ7пoS?xuWbՓ KY("]ܖڲ{E;f In~
171i=G8 7)Q쩣o?_&%'iN :o/z#?VI ''J0[!Q*6?tgo él;t<s+98A^NJNG|E?_yk,G>E߃e }Z U'<cb3ܫ ?3;XfO'KxxTCqu]sV[A?**<[-S_zrj7E/WXkS7|(Ѽ˦Nѭ1O8:?j'EO쬵|)?~ %5^%1<p+zުA㵺ܕlp״wqY*|#7<O?FqSį]jډ',n5 ɖmݰpҾ4dxx^%`-%?c*fEw2Jޑ?ZIlf{P@^I ;?KeQm%5[6̿QNogQzT~9ƟH_?q zq'O2il7Jg:^|S# 7x>4k7կ|E,N*Ⱥ)2<8hP%($u`K/S쐠PtDILȫ3:abQЫN:
172~?|x+]L Mk8A\bct>热{cy`ҵk;R/Xdy"s-il}2x_)?~7:#ߝ|_4x{PFR2̀5J-h6zӯqUp黽4~
1737KP/>"-crk=>Nm6H*稬'bZ$m^7sL;9]W~v=ZOյWPN[ 0yeL'?1< י -j8!kݿ3Uqzo髜(
174(
175е268VO}A$!>](j wBl>askt Ќ ']Veͣ7 m"1F-׏~3VveJ~ǿxikzs>z7pݘz~]U˱
1766껯c->q\7őtc0tu}&>? V9c(͇y%xGATcѩ5f>/H?~3~Ѽ9M_zY9g2pwst1j/sܟ/'ow#۩j,v<BpOj9{MӼ7n>u]Gq 'ҏu5lٝS|[?:NHq%&Kn>סB<e•ZM$y_C>j?>$u mmĭwX79>kqYGE;/~'K'T65|5eDN<0kZ,^q.ay}ב_zR'/M+%<5-?F6Cx[K8QC߅x4W;QǀF(xg<IBP{A搐b%zr~n|)ҢoY9x^)_G<'Y5R( q!HbUHp>a, [_ז.#0ayJKV}>qxM7/?= 7rPw#_e
177Ԫ^{/_[H{~?Fd8x2__W}ܥ%;RNQ},iiJc [Q.Y<GUIlc)IQ֨
178(
179(
180(dӮ2β HP@[VzM/\me7@'O ؒ~kK[ztJ
181wad|?6kk 5]5301n( * 2fNԥt?U`d9l!Ӿ+E,K#kX ^.;T 6GG$xS
1825It>ɚ9\EcWfdmGOIJٗ52.W̥ "?/x=>OH*^Cu~٣=3=mV_ɿM/a!#qm@ٗ 0=xGߚ?}0?_[ g/So_#>Q&a"1|%ѢLjCu\l,m;?,S?!e?><^8Ho(?S??F_+OeO*]6"vz]NԓNU}e%M,*iR_{2^cnV&9Ǎi)~)|1uw)xSZEEVwkeSs 2ahC^T[_Ⱥ8VWy~gr_hdxLӶ@yҿ5FQZKsTX)ϯdښz,zrleͱ 0=֦[_]`_L m->%|߱7gs£\y'֩wl݅sl&E_eW2-&ߢM6k2iQ#uħo0XݟW_ O3A/y?;ۛʽtkwn #! g#N~U?ԣhwWnuB"swpH ˆbI^<:曻;h{XyMqO*GyߍoM?-cXҴ k؈
183b MkJZ܊ME2Vwe4 _o7~%x4Ğޞ5Ynn!'Br:gp;B5UwNǝz[.g\Mi2[~>|&p<!4SHK9QMT K):0?I%o |Q8Y?ݮ~߰w7x74hwS"StTz]˙d\0V5$@8PrG*io+R>sq}j$ ;8@*{bh
184(
185(
186(7J?Zʮކk(
187 d-OT缎Ve?JI%cRnW#]ORON>\=um>t?ƎX</@5BirGsϸo/(aI<gaUo)rC{IxbQ~}8@9
188=;ܑ<E1){8#b?m2}7Ş./VVn"Qܜ擄"${f[|U~:i҉)o>17 Ƀ.f$cŻt-ī3M|]ƕ}Ǻŏ-/}͑=A( |.m_tY95+?[(N[#ɿlۿf?I+k.{ ~-:[5J 0>No׭*' -4| ~^-i=Q:[xRvR;V
189GɹԗY#U!-Mw\v> х.~?gԼ3Jh,MѸ1ci(ècW+(Mݫm{1Jv.s?l|1<aOڶcNI/>_5/X6sj>#'(ҧ JNY~yW8vM仞DZ-ׂdVs >o,Ah5x7Xk}ҕ?$oWF.- gYV5UMV+xἝq^cQN#>9!sڤrC)ِ!3Հl >Y\T'kEmI{Q>O0|R'S.~4iqO)'ā03o|ɴZiwoCyKMo?g5cॗ#>9okI X]H>G  @QП=(%+Zb+ԇ,_^ ?d>vDx{Hge3.,ǻT9VK5u=gR%u ~~U*Ln-5?ʍbh
190(
191(
192(: F?*ƯCSN
193(nâ]\a!O~*D g]GP@P@PF?oO 0bOu}FCmkZ-m^vLD~b1҄RIF)-[~U(7|9o_=߶/ƫKqj$s49#b99(gs`iEhکuizF>w:VV~l-yO&"PY$2M| C0ǚM[VoWuJG
194өbl?b35E]hm%ӣfYd[&0B 9G; T򪲖N%+Kd<K^d};|LN? ?|6MAjv2[;pKm<<b!G-UͨFה)QV}zE l|Z~˟
195<Q95Oh{O&֙~w%Dl,lifB,x
196ARC#ђ2*FMRm\8[]/W bJe
197vi$=1 o~"+C>B}/EA5_I.YcQ #pq<'*abcԬJnzʤ4al\dMiyӹÿïXx|%Gu7whۺ B/,ی.y9ͳ "%Kŵ*-JkFzS^F;rɓ~ߴ0?4E8o6xJeeaŢ OZP\5_Ru蜽ɿ-saI%&*ʰt"V)NkFQ黧c̿i2Rm>&|5Hy"~k~YDH o
1983N3̸du>X&oFכt ܺ N&O~0xi{Cմq-+4m8 *rvf`䮌9CJAMp؇Z(
199(
200(
201(;Q\}T~CXU46gOYP@n mi9`VcY8j9B
202(
203(>rɟhٗF/vǾ_VxkX4;,s2ʹC˖U NQi_]|n3~>OƍPPK{pqe(ʧֿp&y<~j}[/TEI;GDyۗ4AGOaeLPK6y0<^UWTOIzV_ʣ'<WL+SzA~xz8$M'2GHBk'BTm1`T-7ęUnM:5i?v.ͦ>,|5OOѽislڛU/W⥯
204xNb6 ,exr^m</ÔXL+ũT][MFZ]Ft|PUjNN[^z>~߱߄
205|5 i^)4RC%Bp3aTG;vS\wwծ9&exEr^+Ae忢><;? :V^uqgsѼGFA5FUrJ5Ѧ_LB\*4~m? ')M;R<=CE񾑧ۙ5_OQ||вHʼ A~,1kRqN&NjΜjIލZ/II >+7_
206<mk= CL&{cqQe< ~#,6255dLYs$$
207Il#k _!2 9$o/ u@Hi]@5 Ry3){,jNR!Kޕ۵ex^:nRkens~_Oo߄?Pzv$[_B,`HRZQC%1 &Ei^2\zj%fmgLls<J>_jsN9-5?ʵbh
208(
209(
210(
211(q #ܱ0GU+3`
212(s9SlHS
213(
214(*(D>T3z7cRNU>>O _x7za~_/x^j}Ib| O@k4n>ca?ςW-񎙢ڋFRf/4p ƀp70&ZQuӼ?ˡRlSI++}U|Lд[;XAAIڪI?~S9)Iտúq卒:}O_h4_θ*5mcF+}MxOÞ,CCsY_غ~kyP;2\15ؘb)ɩũ'4ɗR:9ū5>-~(^ Oco
2153[|+u9x~6"( mUe aajftogu5JQIJ]۽K[,J_4z|S~h%[T+5ّ3A8+fV
216uySW<9[ڻ>ui~||O/_~AJ Qv P-8c*Ʊ,Um*Tr 82|
217ruyݓnQ\[{MY5<zQImm''5??3yh6:A2q"--NЬN TWp8 Rzsqm{NT(MEJ鵮W>JJϛގ ީk;[C3mYx7Sx]}AIo8A%v]fsyų%N*6ovn;tH#Ȱ%9_i[]+-/ya/?_x~2"{̃QiYXǶ"Ҳm.r1_8|!O|;J+i^rX Rժ{Nz_# .5z>⇄|!uܖ]YtݰZM=;^:%-bJ{G%Xr=61ĵ
218(
219(
220(
221(A4^Wmv?ѮjI{ר4
222(<W?i$DJ?,Ԛ馭ُVfP@P@5 sJooQNp9b}1@ aG2: \n5 >qǽKE(;|?\F^겴mrWm-_ݛ`甜(#|,j?1݅ 1xi| *pU#ͳL6O*bmK كT#J_n<"?wվRK}ί'ܻXͺ;BN )\M-<LU*^WeWYcpq`*Oބ^_q_k:w9|Em/7 p5Ӄ9~E8dO11_>_y8 URk_$6,>> ^&W1,R{=_xϓacN?H|gCG.ZTQk^>0YK X@"oUv>I[﵏R}E~?eW[ǚVXDWan-A<9dX!LJ-m$Qz5ZcnqEӪF|שxKm>
223lN]A@㫮0N+c>ϧ<UV[_vb87/;QohO'{qkzio^5^*%k8#U'j.s5לd|oХNUڔ 4}[՞[ጂ*r{)'4_?U1H;qҾyNEϜf)?NXhW. R^r\O~V<vO joizC!bs,#>vcq<8XV`狓U;pK|AUp^4RAsKz/XI~OB|u߯x^kgXbAo:1睹77s*XW~8. FϚ=\5΃JZoK[zGGW!<shpi׿ѼR 99qMZU O^YtZ$jXK/˳]~OF?mo+Kx O7 W4yT8 $.d5H.xB=N/#vC#R5p(
224֓ap$q4hFAwHԼGRPG4S.eWU@
225(
226(=+p}"0|$}FrL톐E€
227(/ծ~٪\]g'fL]qVV8$&P@P(,.|QhP(s/H8)nJZ7<7rl1)|AvlSr:VZ,'2~U9]O|K?n)WkO 7"_t~n5G [-Ľ^XTSMk|77'D,$"eWD`ڡ9 5'>%S泲M>n\72yWm/h< u}.-ll
228 ~'_%laG ko}Y _s}{|umdZ&+=ѻLtnO'#φ/V--:_օǞг>m~ M[ExLϦvpGu`@9^AkW=:eޭyce Vd<Cq%e/ЉY0[ew1A`5x^< ]Xa2tݹ_ؚ?Q:Y'<G{럲5,`|Y )FTV!`
229/H8}[8eʚmv<&䟵çxs xbm?eOPjN5/ I̎sU̸
230E6U$anťו?xៃ]&j_!K1'U1}+V;3jJN_;ϡW{ZQQ^I/u_P@>$:<GF\(jZ#fX;>ap|yLdG *Yc]
231 Jv0u#KVRvJV,ӵ37r^ꡈ\r3pJxyN*2{;hNsQ?߳l.Ox-źo|d}=;Tp::zK~\Օ'I/D|׆iwRO7п_焴5><i-ZdռmEbG1J{#ӱh#_HyRI>g&M~vH㒼jfgPbhfl,N&D[_ikI|ۃ\a#IY'U]ʥ\x?i=ƖZE5{9M &h5N2#'}* Z6r \X6n\33JMO纵١E{H H'4Нb
232(
233(m4YS@nzQ1,H8U}quP@jwdn..aM+I-]]gP@P@36k_KS"C+SV4zֶFζ͵f0(Ili&KieiřG'ʪ:(;e|ow__1wR:i3ݔҮ,--6 wVD#ey/q<yN-,F:*єW)^x/.'Q3dO=Nz}=a1s*o=[}޿SSP,wUYΗ*4GqGc^HT+Os.-hkC^!y}
234O8|KVL<LT>`ו3MA,6)^[&¿X7:L\ی-Cג3HFzc,(2PQ[V t~CD#E!HZZ1*#LsRlPonن7LGǶ+ {',{h]zT|V#`27g
235>ZV|T]i6N29~SZx5}O앏Kе7LmYmjaI0߼
236=q۞1_ԥҌSq[]l|k,Lܕz:xj)m3#j^ b&yoB:WeY.W~?3^)WAo5Ȟ2I}cóXoQD4[2oа)_$Y֍ 5))C޼IkI/6Dך<~M,]ROK(3vI{a+j&+ƿ<u}[?ğ K׆<A64&}5GP2Tbƭ)EMԧR&)~;WڲJH$G+d8q6yk39'Oh|i-"c^WF$~Wc>x~; he[+~Q?6;o Jܧi1K\y@6cS.؏4$$Vz7I}$-SF\j_'"?҇-M<X@d$
237(
238(/b^ cT,jG)P@dH隺j3yta@P@٭ O-L^GOiaKs~|=վ*C/HgԵNce fYesUAXT3:iC]{>3~Nx-hq6+v̗rvc:PFF|Mj|<YmnE(NDЃGj~?>#|5guީҧ݈f-$o-j81Ϊ8SqWDTiq Jᄅum</#uk ^m\hij(vpw0p3_c<%UB87te_2#pKBtgM8/O[-m{_'u=1VMxО"c>`"JUJkClFtOug5ǥD<WOoTQc$pkz9XxSRVN݌`*t.ϨNM*_ CX# =6pU2h0\9:5F-qхªIw/x<m͵Q7g==ifX+_ 4eNz_IMf֙qKT8rY9c^s,RuSk$^+|u^vP%B1̠&ю95a٧!yIJvRa=wgbľ ]뗊И5zuZV R䞄ɯ|1fuien2kYTJ ei(O]#?Ov yc~t/~OAk#,>8z4 \, c?s(c,RPR(~mVh(VqU,MKT#m[^KS(w^ @M:A%>h!Y`ZxV7dWtc_긼F>3KHHUk>З/.t -J9l!ڮp$L犕Lu1bݺn`\,cB\}?5x+MrĶϦ]h <rG_}b*gqrk}4eI/ .=Ɯ=J9zQ^4__IS??dTOSMw~[}͜;LUٟh
239+?)>t/h^)09-j]& $ccV9{h 5hh^9Č@c۶{SU.[_]r6fyJ >l֢NŨMm}J~28:WzQ-5$=oíM f :
240b
241(
242(
243wmkr8`}5WWW9P@se٢E<kI{5ߺqP@P@.ԴG- [f?-; 4M|][hɝ!ޒ&vH?bʊGZN5&Ok J-?SxOIzhr GY\t}k imXAYmJ0 \ W+6+QN|#k/FO.t^<ƞ8ɧ[g wYycϸ8{ >?TF^Ҧx x7Jt?XktWP$R1x RPx3իBxrUk<H'5'-f;úK7z#}TX~Hj~XR=(Կ?jEV\ʊsIO >ht w[`b.{23~:|?i^iU~ffqO/%uO_:EO|6jSߋn-VO%u!|X%G j߷J[?8-?ݲ<3' Z_"KMcĿW'ZIg1⹫5 KG8S%\ӥRq~D"7?^/Z;$I$9w5*r=6_R=_WuȞ ׶̷ӡ;`k2FKa-m544eݎIQuj=XS(GdixkZ߈|AjIcy6?Jqr҆6JqZ3^ m$ެ_;SM<|Ek:I iyniGa޿J3<Ϫ~!!zwך𜕾3
2442
245(
246(
247(
248[b+:IbGE:
249(9_;8A5.=w8P@P@|Oe~@v/#,& s~?o~&k}ӣ60^J1灹AaSG[Ov`O(Q6@?iKW?ʡT~*.1< >( 8$v@M{c- <͹x}oo % v~~]6zgNk@
250(M=(;8o? ~:?k6~*Ooo$)0#mH݉r#?K\L5?idQgtMyUUͰt*(՗-ݵu%n~Ҟ_*~cO~gT5b{^ݡb AcIA@^
251M/Xt?iVSgocd,@=p+*ѱIQ!@P@P@P@~܌]xB;뢭f5
252(8߉gQoKcĎf0
253(
254(&ӯoD=E&4]7?^?hXr0I!S,.XVHXtfFkC;UmƿԼIɩ .]NYaG=ƶѣk8"eЭxn^{t^-Z>3eSJ_[?s}s!/ᧄ~%XǦxxm|=-QOqy$lǻ>G`<P8;7f/|Ssm(nkk\S;(vO_#جc Z*OiNb^X\D7*}K s\eIH'dT۲ [/A4W>?'[rҿ~~I9'5ۻ:@'*-}j}aʴ,(ٙy<v#tC:G'W?MGS_ԣ>Tl/0ЄM&vsb1pIF=۲>/ߵſ I5q\6zEuXiW^;jWb O U\=Pg87(K9i]M=46M^)?y==9u;k %cßRowoGm/Eە*7"?>/2ޥ~FjJ5E>Zk&18̱N*Q>my?[Z~ W|tG'tυ1:;-:ڄfQdp7Hi %WTӴɦ/熩_޷OZ3l@WJVv<Ms]nsP@P@P@zk61ª~s\ww; (
255#}?V?ֺ)|'-o B
256(
257(< CW?ZOeƯ_ |0$2H2ZR9 c54GQ7ٻG_-EKL:4|. b"a͹ܟۉk80nn3.y4`*_prXE\Uh1\^[OCixO'-f[ZuwU3<?0
2589Yc1T暔jEk|IJ6i>ڥgܞSçJQN/~eV
259WaK^&"U̶'p3׎s s_PI*jV2^0HV_Nj7 V́KF}G#nl[Gs"愼2k/6̨`j/8hݿΟ ov<#g
260 &Q}[ºUwjOOMve)>prz~~[[`d) [A&rw %"k{ ΥXK_b_+G)oDW  ׌_G|Q_?w0Yҳj2zv}٫xa)^X>Uӏ$f#Z]!oog_ o][tPկ?ZS4j 7؞KIhj\2ğYii{T3}Y/51_f9vf#J&%VjY~_TO~r_\g>רbWD<+|MYm*USy6Z8|Li.wKoT 7𗈡M_Xy]{ƻ&8nz(SIC ITeyڝX~iU+'Z}>oooKҴNGt,-Z±@*T7:=տV} cEF*rUCHOm{X1e:"1?;dYYYA/QBޖs>>Y'׉ߛ98|H<ں 
261(
262(
263(߭ڑr@jdʂ=2N
264(ĒƃtJVdP@P@XҭojÉ'E?BFi7eq^I5YQ5?O'%s-ٟs-d%"N$'z1,GJڝ8E|/yR?CkN=&ݐ?.OSL#ŷ.+q$z^[m<(fQ2~Qa8[xOѿl@h8O/EPtUmv]H
265(BL$H?zB+E\M#D^!N"8̎>g Ӱ溪]
266<z59dbK|KWo>0ҵω|7 +QK x./QHfHk¬Y_%g(nK%hqK䞉)'~UJnk\rOo+/ZV"X:o]((
267(
268(:^~% gU6+sA@P@u?]4VfP@P@^Ӭz?O(*e?ൿ+|V?ބگ_~>>&/H;9,,ew5x'\|'>FV0'DH} \FK+ĈTHOŮ1_RFMi7fҽd{l5QK~^[6;{iO$RA>\Wu0OUֺU跑M~G!$ oNBYRI?+ooPNI=I%P3* ԓEoFG/%+Z_x^B%ȝH.Y#V!]'BK|uPE4W [G.K#'P RbSs~g+
269S7Zl?_>vɾ++2e= VA!liMwJ^vyWQ}䖯5w)?F\7
270v[ wmV]*egL<!IHL\p\̱WQ>[I:OktH,s*;=וٽ/v}~/x
271T?kɫ{m/ú֗3ZCI,Ai\;` mӔRҍjЊI%%dGsMN?D]JV{˓Qv$<Iݐ/e܍i%ënF>(
272(
273(ՏFe%$Gsw*ѹYP@p>>R%krVZ 8?'ٿÚ_~4xt];Y.iOi4&{0##l ^P42|Q]|5UIŚ=έxvOᡰ]Hk1#J$qrxgk?m57.M^/ttL/.$7
274M+5,p$#?ug2ϑ~V0%H$hfBVZa7Fe9R;^n[<N{gsJ,H[O%<> կ<EYLj<$Z/h>%yt馑cWsqfvSQ~1[jRKm5u~i&i'D4߱߈'&В [^Ѯ =D8ۂJsX)cf5.Rq+Iٯ3X{ /cm\5Z}/S
2756r*x_>"ޭ&"`n|BW}xIb#ZM2yg}{[S+|Ij$ڳv=MGލau_6zd>cb+VY[ +b(EGKN9ne17+F}f''Z TQ[E}Uy;Z$s'*/:OYo{ Wv-q9-H¦+ GKդy?Cw\>iZ_ůs_AN1Sދcͭ9%s(y{~!gÛl} Kr!%e+Z7(6eĸZU cNUOC"Y?n=M2sW/2!-
276VZ/X% kI_%~,Oڷ
277 (dO | b!_Tk.fCЃrTt9/y4yȳLW2VZiAZɞ><cيk k|Oj:Yr^U$Bߊx!nܑ]gȲQ/ݟE|&ԗEk^s]HV(Qr}Zx.?̹[Ȟ"X}%O/Y-Ҿ)i$9iJJO]*ۘ9_w[TjL<t~5Ҷ8e2
278b^Qr7_X:o@ 
279(
280(7E51@h QӬO:E\uFwrRP@P-Ie?I-oC~$G$GDƶ6LJ>͞=/-k󵾥yxVStIh]]96ֳx?Ofx;PO~T5KA P*!dMI
281.5 ß> CO~Kߌ^ $ɨLmN0X#:+;ZWm&yo5f&;OhxQuMC~"(5KhYܛݤciKfuǪ7<19/Agطҵkۀ'wHѪ(ڵ+3~]Uu/ [
2825XxZMKQդ9 ͓K:CGwo2Rs"eJ}u~պ?EhOkv:437SY]#3[ $# $tݽkGZKA >=EGhRW>*h[x?nhV~ /q"J`ڱByv2qըK+Q>hq Ok&CXNI=Iwg |q]8Zht'MR'mVx/#66UI+3  HNm$9F-ݜX6)ON2I6z/T<Qt |9Y1d[{*Gsx ;`m8|'#VN*ɹM-]vntxGU ;JI}==7s^F~S þ |Ot8"rto@Al~gIV-' B;=<G
283fqzq>Mozuxw_<W"vWud@B $!#F`S0 ZSN1N 66벒Kٕp
284񓔩i9h_w녷 —Zρ|GG"G> j+2>ӵدxP?*TsW2Wg'GM&bgA[?_95|OR_|M\+/كlX'_ ?rie2Fq}X/ӓP˲7F>KGu^9P38POS֋0>~|gޓ-C,.%.pb5o%3I"GխbchSi߬|}b~miYu'J^Ilo_Dg_~߲_f| kM+A^<oNK#Y˒$Uq򌻑_շ#}ZeP@P@Ӿ߯HʢM)F=
285(80g{{h_ jJ]nŶ9bz^4m:KM>/_v:_]4zz -vs;??Ti\63_NE#H9ҴmR1efϯ{X>6okXK kh?z浱;[ J~ Hq\'Oq-Bs{| mO>#QN
286>S[~ߟu%}H5V `N!i
287}V AHXzT}v}oᇁ|4zzg%i|mEH'8PKXI$vs0PDo~ -x-IKA -
288>P
289泄;#%8|
290xSz5o8{n~./xQ58 73qX=TFyl<<5oS^?~#:~
291J. (.t{3:@I6E.qM7mR?YQq?D7 u"o;UO^jZsL5u9ԣ*n u4Ȯ8dU<r9)E=骿vϦz:^nn#nxL8TM^&ڌG6i\< [kzgn|~f3 =Y"^]lq$aC2dψxk}=ܣҎ%+u.y?hMW >ζ 8 fGQRpti]Z2R٭S<5|X?c\6> X<H&c%$w2*$FU!Q> 8<yEJUuԩtԢwiEj՝<\V*X
292TSc.,i]p͛gx۪M ePK)Sױʣ]MރӌMz.S*0Qkdž+>.;;y.f"vc a;
293bk⦧VWvۇPAŒySm͘_.->Klm2H??ےrh28BMg/{~'G 3X/>.sc 2Q0-cY3,FuGInM gOJ3F7>GV_?yʫ/:y<u? _++žesUK ]/i[^-JGho#>u`dj_
294iai.t˿_qGʟ?~/M'Gψ|OÚ BO*/ (~l[m/x;ŒӈBx]zNitz맾Wә|~w<*/Z)ankm?xmڀt}&"j(
295or%<&
296V/~OҭZW}Y/<G//()y5<76eՐP@_/(˹/,^^[r7oP@P@w?tEܶGMsw1o߬͂
297(
298ŁW- &8€>[Կd{\ sT5oxzBg_F (V*T)EA'-Oc/c_go'uⴷ-w?d. Υu,y6mAUاެA\<Q-2ѷlNj,Ow-_/7!''|zR=^ :Fq4V,]=ᗀ<wT|{y K:̒XnuM"ʌFu4;2FٷIkcUZ/s_uOW3ú?ػ\2 @*>{4qNptQtu(Է;h~*Mb](N߮
299 ~wY<+ 4gyjdi]I 4<;%'pw9*MrSAM9!sϭu:ot1X]чIPu=AeK]VIZG>cs>?Wִ|g j&,7Zl>k7Fa՗b׉dNc%[Y;^)}1ZPK}̟m/~(|B mGF{yxʀhQFv~_f)p8 YPGQnu$%goNgch*>"x{-zY_ 迴wcK[OlѼ]lcS 1K[r@֎c9^"W[0 $fjQwwmZQOi;Ԅ1pK'
300N񕺯ԔxC6.\ΙTMdۺ4>~'|$Ԍ_j+a*A}F?gڢw> |^*^xG]V-GW7M#̭!B 0`qp2lgAb&97/q+ROeMy12bʶIMmu}oKkhšƟ<ᾦr>"J`1IН+ìS&QM4ܭʽ􊼮|M8F0Щ 4|ZY^fum}gxUI R\0͜@)Z8wZvdm)Ț("JVقio.ج J|!GYPIyդ|UHO4(xy;|b'{.B=ـ*p,/^38YZ/?(ga J1[Ɩ(Սp޸O7ךV
301rO>Xa9zK%Wg7u?k-NLnU&m{4,l.l~FUPU뵉<k|7NRrjjkIJJ_f\
3026o99Ko {?avr||ӡ5^)m:(|DLؖ}6-L׆5LŹqҍیisrߣVּS8fOwk۪M~MeA|a?JS૵9*
303(
304|RyF]yր bۑO2
305(,tY'er)Xa
306UG`:W&jI+a@P@$R.U q@nyIsigo'?u+2,VHP@Pg<iYhzM5^y$!T}Mg<Cy|Ka)E7P-C6W)^3
307uf?_nbo/܏k
308p]Yxt/B/GI<1> W/i-ZxIcmd=3w3ynx^_+dI4F3$d6!dSڹ]#/7?!d ~ѭG--h6MnO-gi,Ev)<2peȒnθ[o>_lڝfvjxG3Ԕk%Cד&z͆|ctkdOAooof4mTE\P1sMsjն3cIrZK|O@SK>F8?pTKX;FĿ \"s\Wښe񯅘dk1~ Y}S{Hw#ǾdꡏFSX<K"4x,gJ9uW4u~&yK4OB3]PQũ7sw{yWM#Xjadmyix$P|H᪏/Px~[rvtB2I23(B~f|[+KU<wߧY)ʎ5_?/yO#4%55:L^x\5dEprٿJL<0x( F)l#Vw{@5[gP\Wy򓔮ʴ
309(
310(Kw#}Z%ënF>(;Z/nKC'Ve.c€
311(
312(onXLĠ#Zӕ+B]nsP@*ן{_?_'5-1u/j'4 -|Md;wvj|+]$|CckExG];JnV
313+6{MܚC'Ht:C--e q/{
314d(7Ѥ:}WmmQ*=;?o7zm;]CJ/kG\ikttN7%r᱘yaSdMvikTQT&iٯ G^>loؗV-]>ŷuOF/$s}OUd+_q9ab^ԧN-+oz]]dcj,=j^V5;y=$s?5Th?O?fiw̾#GzWs|M?i<DX}O%><KEK(6vwעY4+?&WU3?^Un Ϩ;OYےwSjA3Qmq''*7-θW6n W"k/
315?aQٛͩЯX8JE]:8I~-#
316IKO8/ce.} -־Q4I ^\FK41I/+١wԔUJ*6pW~o<W7
317vߖ2kweػV+e7MbFhIHehALdAp 3|*qvpT)ٓO8̳>Wڊ{+w
318~?|)|ycZⷈTjGgaxj_Roeő@$PIOx_L0S/˛EkZRc%ڻZwL~+p')%ϲN9Ϳb$t8UdL>e̸f&[v03_ч2?3Uz}noZFl[NN삘
319(
320(/e܍h//( .cB\
321J*B<ұ0C)oDPC
322(
323(Uԣ { >y@Js #9VfPץ~<A tc:Yuz {Ptd)pLcrG^Fa@P@[]Yʳ[U I6/)_
324vWvWiSŚ,N}Z>br)>t#uiʕXFIi]S;UjpvkT韚)NZ~.טYo:m+6v.C6jon4[, 㻥{J?~|ˣG+SX|k=G4`ůxĦ9Em Eh
325(s_K+pxMjN/4%w}Uz_#'Z*sRd}ǓS(|2/v ޥquojx$ 78Xk^*q.cV5*Pi%hJPMw'pO)&m_vNd?hZ }M&/mg|76LdhӢ,ƣ:9ΪƥJ6Q[_iMK/r캛:IݶI~^/٣ ρ~/>kz" 2 ֑ܼE̍$ǩ>6ƕqjӄbӓnJW]їRF.vyo#q[f;xoo U?uY
326yJN@ ;xW84ۼ~[9>KǛW}jU-_d}uyk}jگŖ>.>%AYS-ht=47)i :l1]ù]<Zp)>}~d?,Nc')}tKZwu5̗wb}}VV<fwdt(
327(
328(/e܍h//UY")$$GBӿ|a>:朹N֨4
329(
330(
331(++mF ?4wBiIY}? ~k9} tJHT@u@qh?Tn7~Jؓ8,ix^l[BYCM+"-1b OWL^!$[jQ;*}̕Qda&?hK<ZZl-ҋ:g׾oXlXo Aq`E 7K?i/YcTq!o#Ycbtb3>:>ŧūp}A*h{Im__WO[—mk[KO+QXz`OtIsDR?o^K?~i/B^^GbcGdmѠg!eߊ~߄4
332YkhZ XJ$TYH#= )-D(=QOm= mq 2:0!AV`Xʛ[ŏ7>'ʞ&_ICXX\ӜIhc8#42ꗲ0s;z7G~O U;}gj?I+ ? ohďd^i>Iα
333.NP:_/js4U8+^'gS꿴ɥBx7_ˉ 4 y!+~Nq /3֬nH>$(۟
334-[x6E g#z82|jt0QYޣJM<|ogظԮG?,~Ww| <1oq^YB]j3.-qt'Glg_Nld՝N&rzza[攜1P@P@P@_/(˹/[
335,5ݑeaV5'uP@P@P@Eygmn֗M4wBiIYG|y]Y }ƷE-:N:-5?ʴbh
336(
337
338Aja>Eu*~jYU_>x\ ֱ
339R) wnCB&Q6\bޯc?ljb7oG^C!xIs,Q壍heQN@Фsݨ$trIoFR:84j=\b=bO9Ƨ=x]az Z9c9ܫ,敝Yjch
340(
341(
342(:?A]WS)QX}~β6OVv0`uP@P@P@|Q_gc?"Ն|
343R!6A&ai[c+Iube$+2|QH|E?YxN1X,瘁Iiqo
344chP=u~#~̱~ٺWwOMW-6LW4B[`?+3R):FrNt' Пf/\^x|K&xnL)>Ts!\GjI=$;dPџi$YsGțk1G_FZy~E5<o*Ək'gXewI}GRcW+-ُ~T2ݠV /v6|94,XTYrI_c'94 هঝ^#ռGrw#!w1iYK|X"0yU$.N[LA@P@P@P@P@SpWQ֢+Xa{R#zt*X!@P@P@PWYt7%'i|qs4|2<QiQk_|OOx{ZmԴe[tb/,IJ+.!MR\u#J<='74
345A+7mCß~|*x^ q+mH޻[XUGt˕gZe/[o }?hyukJX,.2:@8c%ʭo̓<goW7
346ٶ𥞱R^jNҤՁH<u!C =(c?Iu9|C!xUgv4,gH/6*S)A<ԦkS^o^|Cź}`gt>5j_s k sjǃf<{^W^k)v-vV;0ykte%H}|3<3^vo#k[dsFh.ðhߍMo
347WŖ4Vz u<}FZpǿdGΟM//PMg7-VG<C>T9,o̗'vPHP@P@P@P@P@={\ [ve=6j.N7M$VkPzzU073`
348(
349(
350(?=_O#Ǐ
351ٲAg<j(;̬6ө(= SEf~7~_/??ߌ?[sw-#Ahz:+kj7 Aj֐90Vt8 juF7I/zRf1M˫%y_m׈,?fo>*x1#o:oEA|S6qi²A.ՖA`#f5/c^
352p\喩UΪ'*R.'~|y7?s =_'\Y[—SKkYPt276Wbڳ#DG~3\σg1ENA|)7<#|OOd?\ch9XT0s0WxKt*6Z}p¾>m0i=í.2i5?:Uks)QxN%;NhEʔe21GR<9fbP@P@P@P@P@>{Dд$toC~O|?ZUR[Oltƃ'ܞmNEhOHa@P@P@P@~OxwNwdE<jHd' 9$BAIpDo|$ #a~zg5wY[]&]J*|e+ <ˆ8o8ctSR0MI&Ҳh6JnPMfl}?aS.Ek֣yj__jsfBv$E2}z(ahB(B F1IF)Y$%KDrmꕨ
353(+Znoӑka8[΍q[sgrGִUd2tb2o>k0dO ݴ~j^nU߇̠MU)E$E28"
354(
355(
356(@,B$Pj8hE?7?^ORZ9t7φ&zd?OYAu: /O"l-!hOSY61P@P@P@P@P@P@P@P7:}ݔRHtka8
357'N}cr?LT!҃Qᮐ'
358jt#ѕ&c0WV?4U؇AeY~s?b^d/'ݳFve>dMJoD?֏iv%|/e>¯N4dAi{Hwe>ı įޙ@c2? 9ğKj˖ Psw#5.dRݚ I1OK&ZjYzv1ec^61["zC
359(
360(
361(
362(
363(
364(
365(
366(
367(
368(
369(
370(
371(
372(
373(
374(?

index.html

2424
25 <div class="container" id="container">25 <div class="container" id="container">
26 <div id="header"></div>26 <div id="header"></div>
27 <div id="nav">
28 <ul class="nav nav-pills"></ul>
29 </div>
30 <div class="clearfix"></div>
31 <div id="content-container"></div>27 <div id="content-container"></div>
32 <div id="footer"></div>28 <div id="footer"></div>
33 </div>29 </div>
32 //Code to initialize the framework32 //Code to initialize the framework
33 window.M = window.M || {};33 window.M = window.M || {};
34 window.onload = function() {34 window.onload = function() {
35 M.load("/maraa/data/maraa2.json");
35 M.load('example.json');
36 };36 };
37 </script>37 </script>
38 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>38 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
41 <script src="js/lib/backbone.js"></script>41 <script src="js/lib/backbone.js"></script>
42 <script src="js/lib/bootstrap.js"></script>42 <script src="js/lib/bootstrap.js"></script>
43 <script src="js/plugins.js"></script>43 <script src="js/plugins.js"></script>
44 <script src="js/framework.js"></script>
44 <script src="js/mouchak.js"></script>
4545
46 <!-- Underscore templates -->46 <!-- Underscore templates -->
47 <script type="text/template" id="news-template">47 <script type="text/template" id="news-template">
55 <a href="<%= link %>">Read More</a>55 <a href="<%= link %>">Read More</a>
56 </div>56 </div>
57 </script>57 </script>
58
59 <script type="text/template" id="nav-template">
60 <div class="navigation" id="nav-<%= page %>">
61 <ul class="nav nav-pills"></ul>
62 </div>
63 </script>
58 <script type="text/template" id="nav-dropdown-template">64 <script type="text/template" id="nav-dropdown-template">
59 <li class="dropdown">65 <li class="dropdown">
60 <a class="dropdown-toggle" data-toggle="dropdown" href="#/<%= M.sanitize(name) %>"66 <a class="dropdown-toggle" data-toggle="dropdown" href="#/<%= M.sanitize(name) %>"
78 </ul>78 </ul>
79 </li>79 </li>
80 </script>80 </script>
81
81 <script type="text/template" id="image-view-template">82 <script type="text/template" id="image-view-template">
82 <div class="img-wrapper">83 <div class="img-wrapper">
83 <img src="<%= src %>">84 <img src="<%= src %>">

js/framework.js

1(function(M) {
2/* Defining Backbone models, collections and views */
3
4var BaseType = Backbone.Model.extend({
5 defaults: {
6 tags: [],
7 title: "",
8 attr: {}
9 },
10 initialize: function() {
11 }
12});
13
14var Text = BaseType.extend({
15 defaults: {
16 data: "",
17 },
18 initialize: function() {
19 }
20});
21
22var TextView = Backbone.View.extend({
23 tagName: 'div',
24 className: '',
25 initialize: function() {
26 _.bindAll(this);
27 _.bind(this.render, this);
28 },
29 render: function(el) {
30 $(el).append(this.el);
31 var str = '<h4>'+ this.model.get('title') +'</h4> <p>' +
32 this.model.get('data') + '</p>';
33 $(this.el).html(str);
34 M.appendAttrs(this.model, this.el);
35 }
36});
37
38var Table = BaseType.extend({
39 defaults: {
40 data : {
41 th: [],
42 tr:[]
43 }
44 },
45 initialize: function() {
46 }
47});
48
49var TableView = Backbone.View.extend({
50 tagName: 'table',
51 className: 'table',
52 initialize: function() {
53 _.bindAll(this);
54 _.bind(this.render, this);
55 },
56 render: function(el) {
57 var heading = this.model.get('data').th;
58 var str = '<tr>';
59
60 for(var i = 0; i < heading.length; i++) {
61 str += '<th>' + heading[i] + '</th>';
62 }
63 str += '</tr>';
64
65 _.each(this.model.get('data').tr, function(row) {
66 str += '<tr>';
67 for(var i = 0; i < row.length; i++) {
68 if(row[i].match(/http.?:/)) {
69 //console.log(row[i].match(/http:/))
70 }
71 str += '<td>'+ row[i] + '</td>';
72 }
73 str += '</tr>';
74 });
75
76 $(el).append(this.el);
77 $(this.el).html(str);
78 M.appendAttrs(this.model, this.el);
79 }
80});
81
82var Image = BaseType.extend({
83 defaults: {
84 src: ""
85 },
86 initialize:function() {
87 }
88});
89
90var ImageView = Backbone.View.extend({
91 tagName: 'image',
92 className: '',
93
94 initialize: function() {
95 _.bindAll(this);
96 _.bind(this.render, this);
97 },
98 render: function(el) {
99 $(el).append(this.el);
100 $(this.el).attr('src', this.model.get('src'));
101 M.appendAttrs(this.model, this.el);
102 }
103});
104
105
106var Video = BaseType.extend({
107 defaults: {
108 src: ""
109 },
110 initialize:function() {
111 }
112});
113
114var VideoView = Backbone.View.extend({
115 initialize: function() {
116 _.bindAll(this);
117 _.bind(this.render, this);
118 // assuming cross-domain urls will have http in the src,
119 // so also assuming they are embedded flash urls,
120 // hence iframe
121 if(this.model.get('src').match('http')) {
122 this.tagName = 'iframe';
123 }
124 // otherwise, use html5 video tag, if the video is served
125 // from the same domain
126 else {
127 this.tagName = 'video';
128 }
129 },
130 render: function(el) {
131 $(el).append(this.el);
132 $(this.el).attr('src', this.model.get('src'));
133 M.appendAttrs(this.model, this.el);
134 }
135});
136
137var RSS = BaseType.extend({
138 defaults: {
139 src: ""
140 },
141 initialize:function() {
142 }
143});
144
145var RSSView = Backbone.View.extend({
146 el: '#feeds',
147 initialize: function() {
148 _.bindAll(this);
149 _.bind(this.render, this);
150 },
151 render: function() {
152 M.populateFeeds(this.model.get('src'));
153 }
154});
155
156// Plugin model can be used to load dynamic components
157// to the website by loading external JS files.
158// Also the website can be styled by using external CSS files,
159// which can also be loaded via this plugin model.
160var Plugin = BaseType.extend({
161 defaults: {
162 src: "",
163 data: {},
164 callback: ""
165 },
166 initialize: function() {
167 if(this.get('src').match(/\.js/)) {
168 var script = document.createElement('script');
169 var callback = this.get('callback');
170 script.src = this.get('src');
171 document.body.appendChild(script);
172 script.onload = function() {
173 eval(callback);
174 };
175 }
176 else if(this.get('src').match(/\.css/)) {
177 var link = document.createElement('link');
178 link.rel = 'stylesheet';
179 link.href = this.get('src');
180 link.type = 'text/css';
181 document.body.appendChild(link);
182 }
183 }
184});
185
186var PluginView = Backbone.View.extend({
187 initialize: function() {
188 return;
189 },
190 render: function(el) {
191 return;
192 }
193});
194
195var type_map;
196M.type_map = type_map = {
197 model : {
198 'text': Text,
199 'image': Image,
200 'video': Video,
201 'rss': RSS,
202 'table': Table,
203 'plugin': Plugin
204 },
205 view: {
206 'text': TextView,
207 'image': ImageView,
208 'video': VideoView,
209 'rss': RSSView,
210 'table': TableView,
211 'plugin': PluginView
212 }
213};
214
215// var Media = Backbone.Model.extend({
216// defaults:{
217// "audio": new Audio() ,
218// "video": new Video(),
219// "image": new Image()
220// }
221
222// });
223
224var Texts = Backbone.Collection.extend({model: Text});
225var Images = Backbone.Collection.extend({model: Image});
226var Videos = Backbone.Collection.extend({model: Video});
227var RSSs = Backbone.Collection.extend({model: RSS});
228
229
230var Page = Backbone.Model.extend({
231 defaults: {
232 name: "index",
233 title: "",
234 children: [],
235 content: []
236 },
237 initialize: function() {
238 // adding the name of the model as its id.
239 // look up of this model through the collection
240 // is faster this way.
241 this.set({id: M.sanitize(this.get('name'))});
242 }
243});
244
245
246var Pages = Backbone.Collection.extend({
247 model: Page
248});
249
250var PageView = Backbone.View.extend({
251 tagName: 'div',
252 className: 'page',
253 initialize: function() {
254 _.bindAll(this);
255 _.bind(this.render, this);
256 this.render();
257 $(this.el).hide();
258 },
259 render: function() {
260 $('#content-container').append(this.el);
261 $(this.el).append('<h3>'+this.model.get('title')+'</h3>');
262 var self = this;
263 _.each(this.model.get('content'), function(item) {
264 var view = type_map.view[item.get('type')];
265 if(!view) {
266 console.log('Error initing view', item);
267 return;
268 }
269 if(item.get('type') === 'rss') {
270 M.rss_view = new view({model: item});
271 $(self.el).append(_.template($('#news-template').html()));
272 }
273 else {
274 var item_view = new view({model: item});
275 item_view.render(self.el);
276 }
277 });
278 }
279});
280
281var AppView = Backbone.View.extend({
282 el: 'body',
283 events: {
284 'click .nav li a' : 'navClicked'
285 },
286 initialize: function() {
287 _.bindAll(this);
288 },
289 render: function() {
290 M.createNavigation();
291 $('#index').show();
292 },
293 navClicked: function(event) {
294 $('.nav li').removeClass('active');
295 $(event.currentTarget).parent().addClass('active');
296 }
297});
298
299var AppRouter = Backbone.Router.extend({
300 routes : {
301 'index' : 'index',
302 ':page' : 'showPage'
303 },
304 index: function() {
305 $('.page').hide();
306 $('#index').show();
307 },
308 showPage: function(page) {
309 $('.page').hide();
310 //news pages are rendered on the fly,
311 //as feeds have to be fetched.
312 if(page === 'news') {
313 M.rss_view.render();
314 }
315 $('#'+page).show();
316 $('.'+page).show();
317 }
318});
319
320/* Defining other necessary functions */
321M.init = function() {
322 M.tags = {}; //global tag cache
323 M.pages = new Pages(); //global collection of all pages
324
325 // iterate through the JSON to intialize models and views
326 _.each(M.site_content, function(page) {
327 var new_page = new Page(page);
328 var contents = [];
329 _.each(page.content, function(content) {
330 var Item = type_map.model[content.type];
331 if(!Item) {
332 console.log('Error initing item: ', content);
333 return;
334 }
335 var item = new Item(content);
336 contents.push(item);
337 });
338 new_page.set({content: contents});
339 var new_page_view = new PageView({model: new_page,
340 id: new_page.get('id')});
341 M.pages.add(new_page);
342 });
343
344 M.appView = new AppView();
345 M.appView.render();
346 var app_router = new AppRouter();
347 Backbone.history.start();
348 // start with index page
349 //app_router.index();
350 M.simHeir();
351};
352
353// hack to simulate heirarchy among the page views
354// basically add the parent id as class in all of its children
355// elements.
356M.simHeir = function() {
357 _.each(M.pages.models, function(page) {
358 if(page.id == 'index') return;
359 _.each(page.get('children'), function(child) {
360 child = M.sanitize(child);
361 $('#'+child).addClass(page.id);
362 });
363 });
364};
365
366
367// append attributes to elements from the model
368M.appendAttrs = function(model, el) {
369 _.each(model.get('attr'), function(val, key) {
370 $(el).attr(key, val);
371 });
372}
373
374//Helper method for making a list of id associated to tag
375 M.createTagList = function(item,x)
376 {
377
378 for(var i in item['tags'])
379 {
380 if( M.tags[item['tags'][i]] === undefined)
381 {
382 M.tags[item['tags'][i]] = [];
383 M.tags[item['tags'][i]].push(x);
384 }
385 else
386 M.tags[item['tags'][i]].push(x);
387 }
388 };
389
390//create navigational links
391M.createNavigation = function() {
392 var top_level = M.pages.get('index').get('children');
393 $('<li class="active"><a href="#/index">Home</a></li>').appendTo('.nav');;
394 _.each(top_level, function(child) {
395 child = M.sanitize(child);
396 var children = M.pages.get(child).get('children');
397 var page = M.pages.get(child);
398 var dropdown_template = _.template($('#nav-dropdown-template').html());
399 if(_.isEmpty(children)) {
400 li = '<li><a href="#/' + child + '">'+ M.humanReadable(child) +'</a></li>';
401 }
402 else {
403 li = dropdown_template({
404 //no: page.cid,
405 name: M.humanReadable(page.get('name')),
406 list: _.map(children, M.humanReadable)
407 });
408 }
409 $(li).appendTo('.nav');
410 });
411};
412
413// populate with news feeds in the news section
414// gets the feeds from server side script 'feed.py'
415M.populateFeeds = function(rss_url) {
416 $('#feeds-loader').show();
417 $('.news-item-wrapper').remove();
418 jQuery.getFeed({
419 url: 'feeds',
420 type: 'GET',
421 data: "rss_url="+encodeURIComponent(rss_url),
422 success: function(feed) {
423 $('#feeds-loader').hide();
424 var template = _.template($('#news-item-template').html());
425 _.each(feed.items, function(item) {
426 x = $('#feeds').append(template({
427 title: item.title,
428 link: item.link
429 }));
430 });
431 },
432 error: function(err) {
433 $('#feeds-loader').hide();
434 $('#feeds').append('Oops, something went wrong! <br/> Please try again.');
435 }
436 });
437};
438
439/* Other helper functions */
440
441M.contentList = []; //A list to hold out filtered content objects.
442
443//Check for the tags and return only those "content" objects which match a given tag.
444M.checkTags = function(tags){
445 if(_.isArray(tags))
446 {
447 var list = [];
448 _.each(tags,function(item){
449 list.push(M.tags[item]);
450 });
451 return _.uniq(list);
452 }
453 else
454 return false; //Failure code, the function will only accept a list as input
455};
456
457// change all '-' to spaces and capitalize first letter of
458// every word
459M.humanReadable = function(str) {
460 if(typeof str !== "string") {
461 str = '';
462 }
463 return '' + str.replace(/[-]+/g, ' ').replace(/[^\s]+/g, function(str) {
464 //return '' + str.replace(/[-]+/g, ' ').replace(/([A-Z])/g, function(s) { return ' '+s;}).replace(/[^\s]+/g, function(str) {
465 return str.substr(0,1).toUpperCase() + str.substr(1).toLowerCase();
466 });
467};
468
469// change all spaces to '-' and everything to lowercase
470M.sanitize = function(str) {
471 if(typeof str !== "string") {
472 str = '';
473 }
474 return '' + str.replace(/[\s]+/g,'-').replace(/[^\s]+/g, function(str) {
475 //TODO: handle special characters!
476 return str.replace('&', 'and').toLowerCase();
477 });
478};
479
480
481// Loader
482M.load = function(content_url) {
483 if(typeof content_url !== 'string') {
484 console.error('URL to load has to be of type string!!');//TODO: raise custom exception
485 return;
486 }
487 $.getJSON(content_url, function(data) {
488 M.site_content = data;
489 M.init();
490 });
491};
492
493})(M);

js/mouchak.js

1(function(M) {
2/* Defining Backbone models, collections and views */
3
4var BaseType = Backbone.Model.extend({
5 defaults: {
6 tags: [],
7 title: "",
8 attr: {}
9 },
10 initialize: function() {
11 }
12});
13
14var Text = BaseType.extend({
15 defaults: {
16 data: "",
17 },
18 initialize: function() {
19 }
20});
21
22var TextView = Backbone.View.extend({
23 tagName: 'div',
24 className: '',
25 initialize: function() {
26 _.bindAll(this);
27 _.bind(this.render, this);
28 },
29 render: function(el) {
30 $(el).append(this.el);
31 var str = '<h4>'+ this.model.get('title') +'</h4> <p>' +
32 this.model.get('data') + '</p>';
33 $(this.el).html(str);
34 M.appendAttrs(this.model, this.el);
35 }
36});
37
38var Table = BaseType.extend({
39 defaults: {
40 data : {
41 th: [],
42 tr:[]
43 }
44 },
45 initialize: function() {
46 }
47});
48
49var TableView = Backbone.View.extend({
50 tagName: 'table',
51 className: 'table',
52 initialize: function() {
53 _.bindAll(this);
54 _.bind(this.render, this);
55 },
56 render: function(el) {
57 var heading = this.model.get('data').th;
58 var str = '<tr>';
59
60 for(var i = 0; i < heading.length; i++) {
61 str += '<th>' + heading[i] + '</th>';
62 }
63 str += '</tr>';
64
65 _.each(this.model.get('data').tr, function(row) {
66 str += '<tr>';
67 for(var i = 0; i < row.length; i++) {
68 if(row[i].match(/http.?:/)) {
69 //console.log(row[i].match(/http:/))
70 }
71 str += '<td>'+ row[i] + '</td>';
72 }
73 str += '</tr>';
74 });
75
76 $(el).append(this.el);
77 $(this.el).html(str);
78 M.appendAttrs(this.model, this.el);
79 }
80});
81
82var Image = BaseType.extend({
83 defaults: {
84 src: ""
85 },
86 initialize:function() {
87 }
88});
89
90var ImageView = Backbone.View.extend({
91 tagName: 'image',
92 className: '',
93
94 initialize: function() {
95 _.bindAll(this);
96 _.bind(this.render, this);
97 },
98 render: function(el) {
99 $(el).append(this.el);
100 $(this.el).attr('src', this.model.get('src'));
101 M.appendAttrs(this.model, this.el);
102 }
103});
104
105
106var Video = BaseType.extend({
107 defaults: {
108 src: ""
109 },
110 initialize:function() {
111 }
112});
113
114var VideoView = Backbone.View.extend({
115 initialize: function() {
116 _.bindAll(this);
117 _.bind(this.render, this);
118 // assuming cross-domain urls will have http in the src,
119 // so also assuming they are embedded flash urls,
120 // hence iframe
121 if(this.model.get('src').match('http')) {
122 this.tagName = 'iframe';
123 }
124 // otherwise, use html5 video tag, if the video is served
125 // from the same domain
126 else {
127 this.tagName = 'video';
128 }
129 },
130 render: function(el) {
131 $(el).append(this.el);
132 $(this.el).attr('src', this.model.get('src'));
133 M.appendAttrs(this.model, this.el);
134 }
135});
136
137var RSS = BaseType.extend({
138 defaults: {
139 src: ""
140 },
141 initialize:function() {
142 }
143});
144
145var RSSView = Backbone.View.extend({
146 el: '#feeds',
147 initialize: function() {
148 _.bindAll(this);
149 _.bind(this.render, this);
150 },
151 render: function() {
152 M.populateFeeds(this.model.get('src'));
153 }
154});
155
156// Plugin model can be used to load dynamic components
157// to the website by loading external JS files.
158// Also the website can be styled by using external CSS files,
159// which can also be loaded via this plugin model.
160var Plugin = BaseType.extend({
161 defaults: {
162 src: "",
163 data: {},
164 callback: ""
165 },
166 initialize: function() {
167 if(this.get('src').match(/\.js/)) {
168 var script = document.createElement('script');
169 var callback = this.get('callback');
170 script.src = this.get('src');
171 document.body.appendChild(script);
172 script.onload = function() {
173 eval(callback);
174 };
175 }
176 else if(this.get('src').match(/\.css/)) {
177 var link = document.createElement('link');
178 link.rel = 'stylesheet';
179 link.href = this.get('src');
180 link.type = 'text/css';
181 document.body.appendChild(link);
182 }
183 }
184});
185
186var PluginView = Backbone.View.extend({
187 initialize: function() {
188 return;
189 },
190 render: function(el) {
191 return;
192 }
193});
194
195var type_map;
196M.type_map = type_map = {
197 model : {
198 'text': Text,
199 'image': Image,
200 'video': Video,
201 'rss': RSS,
202 'table': Table,
203 'plugin': Plugin
204 },
205 view: {
206 'text': TextView,
207 'image': ImageView,
208 'video': VideoView,
209 'rss': RSSView,
210 'table': TableView,
211 'plugin': PluginView
212 }
213};
214
215// model for each Page
216var Page = Backbone.Model.extend({
217 defaults: {
218 name: "index",
219 title: "",
220 children: [],
221 content: []
222 },
223 initialize: function() {
224 // adding the name of the model as its id.
225 // look up of this model through the collection
226 // is faster this way.
227 this.set({id: M.sanitize(this.get('name'))});
228 }
229});
230
231var Pages = Backbone.Collection.extend({
232 model: Page
233});
234
235var PageView = Backbone.View.extend({
236 tagName: 'div',
237 className: 'page',
238 initialize: function() {
239 _.bindAll(this);
240 _.bind(this.render, this);
241 this.render();
242 $(this.el).hide();
243 },
244 render: function() {
245 $('#content-container').append(this.el);
246 this.appendNavTemplate();
247 $(this.el).append('<h3>'+this.model.get('title')+'</h3>');
248 var self = this;
249 _.each(this.model.get('content'), function(item) {
250 var view = type_map.view[item.get('type')];
251 if(!view) {
252 console.log('Error initing view', item);
253 return;
254 }
255 if(item.get('type') === 'rss') {
256 M.rss_view = new view({model: item});
257 $(self.el).append(_.template($('#news-template').html()));
258 }
259 else {
260 var item_view = new view({model: item});
261 item_view.render(self.el);
262 }
263 });
264 },
265 appendNavTemplate: function() {
266 var li;
267 var nav_template = _.template($('#nav-template').html());
268 $(this.el).append(nav_template({
269 page: this.model.id
270 }));
271 }
272});
273
274var AppView = Backbone.View.extend({
275 el: 'body',
276 events: {
277 'click .nav li a' : 'navClicked'
278 },
279 initialize: function() {
280 _.bindAll(this);
281 },
282 render: function() {
283 $('#index').show();
284 _.each(M.pages.models, function(page) {
285 this.createNavigation(page.id);
286 }, this);
287 },
288 navClicked: function(event) {
289 $('.nav li').removeClass('active');
290 $(event.currentTarget).parent().addClass('active');
291 },
292 createNavigation: function(page) {
293 var li;
294 if(page === 'index') {
295 li = '<li class="active"><a href="#/index"> Home </a></li>';
296 $('#nav-index .nav').append(li);
297 }
298 var dropdown_template = _.template($('#nav-dropdown-template').html());
299 var children = M.pages.get(page).get('children');
300 _.each(children, function(child) {
301 child = M.sanitize(child);
302 var model = M.pages.get(child);
303 var children = model.get('children');
304 if(_.isEmpty(children)) {
305 li = '<li><a href="#/' + child + '">' + M.humanReadable(child) + '</a></li>';
306 }
307 else {
308 li = dropdown_template({
309 name: M.humanReadable(model.get('name')),
310 list: _.map(children, M.humanReadable)
311 });
312 }
313 $(li).appendTo('#nav-' + page + ' .nav');
314 });
315 },
316 updateBreadcrumbs: function(event) {
317 //TODO: write code to use bootstrap's breadcrumbs to render a
318 // navigational breadcrumb
319 }
320});
321
322var AppRouter = Backbone.Router.extend({
323 routes : {
324 'index' : 'index',
325 ':page' : 'showPage'
326 },
327 index: function() {
328 $('.page').hide();
329 $('#index').show();
330 },
331 showPage: function(page) {
332 $('.page').hide();
333 //news pages are rendered on the fly,
334 //as feeds have to be fetched.
335 if(page === 'news') {
336 M.rss_view.render();
337 }
338 $('#'+page).show();
339 $('.'+page).show();
340 }
341});
342
343/* Defining other necessary functions */
344M.init = function() {
345 M.tags = {}; //global tag cache
346 M.pages = new Pages(); //global collection of all pages
347
348 // iterate through the JSON to intialize models and views
349 _.each(M.site_content, function(page) {
350 var new_page = new Page(page);
351 var contents = [];
352 _.each(page.content, function(content) {
353 var Item = type_map.model[content.type];
354 if(!Item) {
355 console.log('Error initing item: ', content);
356 return;
357 }
358 var item = new Item(content);
359 contents.push(item);
360 M.createTagList(content, item);
361 });
362 new_page.set({content: contents});
363 var new_page_view = new PageView({model: new_page,
364 id: new_page.get('id')});
365 M.pages.add(new_page);
366 });
367
368 M.appView = new AppView();
369 M.appView.render();
370 var app_router = new AppRouter();
371 Backbone.history.start();
372 // start with index page
373 var location = window.location;
374 location.href = location.origin + location.pathname + '#/index';
375 M.simHeir();
376};
377
378// hack to simulate heirarchy among the page views
379// basically add the parent id as class in all of its children
380// elements.
381M.simHeir = function() {
382 _.each(M.pages.models, function(page) {
383 if(page.id == 'index') return;
384 _.each(page.get('children'), function(child) {
385 child = M.sanitize(child);
386 $('#'+child).addClass(page.id);
387 });
388 });
389};
390
391
392// append attributes to elements from the model
393M.appendAttrs = function(model, el) {
394 _.each(model.get('attr'), function(val, key) {
395 $(el).attr(key, val);
396 });
397}
398
399// create the list of tags and associate the objects with related tags
400M.createTagList = function(content, model) {
401 for(var i in content.tags) {
402 if(!M.tags[content.tags[i]]) {
403 M.tags[content.tags[i]] = [];
404 }
405 M.tags[content.tags[i]].push(model);
406 }
407};
408
409// Filter the tags and return only those "content" objects which match a given tag.
410// @tags should be an array
411M.filterTags = function(tags) {
412 if(!_.isArray(tags)) {
413 console.log('You have to pass an array'); //TODO: raise an exception
414 return false;
415 }
416 var list = [];
417 _.each(tags, function(item) {
418 if(M.tags[item]) {
419 list.push(M.tags[item]);
420 }
421 });
422 return _.uniq(_.flatten(list));
423};
424
425// populate with news feeds in the news section
426// gets the feeds from server side script 'feed.py'
427M.populateFeeds = function(rss_url) {
428 $('#feeds-loader').show();
429 $('.news-item-wrapper').remove();
430 jQuery.getFeed({
431 url: 'feeds',
432 type: 'GET',
433 data: "rss_url="+encodeURIComponent(rss_url),
434 success: function(feed) {
435 $('#feeds-loader').hide();
436 var template = _.template($('#news-item-template').html());
437 _.each(feed.items, function(item) {
438 x = $('#feeds').append(template({
439 title: item.title,
440 link: item.link
441 }));
442 });
443 },
444 error: function(err) {
445 $('#feeds-loader').hide();
446 $('#feeds').append('Oops, something went wrong! <br/> Please try again.');
447 }
448 });
449};
450
451
452/* Other helper functions */
453
454// change all '-' to spaces and capitalize first letter of
455// every word
456M.humanReadable = function(str) {
457 if(typeof str !== "string") {
458 str = '';
459 }
460 return '' + str.replace(/[-]+/g, ' ').replace(/[^\s]+/g, function(str) {
461 return str.substr(0,1).toUpperCase() + str.substr(1).toLowerCase();
462 });
463};
464
465// change all spaces to '-' and everything to lowercase
466M.sanitize = function(str) {
467 if(typeof str !== "string") {
468 str = '';
469 }
470 return '' + str.replace(/[\s]+/g,'-').replace(/[^\s]+/g, function(str) {
471 //TODO: handle special characters!
472 return str.replace('&', 'and').toLowerCase();
473 });
474};
475
476
477// Loader
478M.load = function(content_url) {
479 if(typeof content_url !== 'string') {
480 console.error('URL to load has to be of type string!!');//TODO: raise custom exception
481 return;
482 }
483 $.getJSON(content_url, function(data) {
484 M.site_content = data;
485 M.init();
486 });
487};
488
489})(M);