From 4e21547da6bf10a62791ab487edc9502a889818a Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Thu, 14 May 2009 16:20:43 -0700 Subject: [PATCH] Added a toolbar color button for setting text and background colors. Still needs more testing/fixing in IE and WebKit. --- NEWS | 1 + controller/Html_cleaner.py | 35 ++- static/css/style.css | 62 +++- static/images/toolbar/buttons.png | Bin 3529 -> 3937 bytes static/images/toolbar/color_button.xcf | Bin 0 -> 1221 bytes static/images/toolbar/small/buttons.png | Bin 1840 -> 2015 bytes static/images/toolbar/small/color_button.xcf | Bin 0 -> 1045 bytes static/js/Editor.js | 106 ++++++- static/js/Wiki.js | 288 ++++++++++++++++++- tools/tile_images.sh | 5 - view/Toolbar.py | 5 + 11 files changed, 486 insertions(+), 16 deletions(-) create mode 100644 static/images/toolbar/color_button.xcf create mode 100644 static/images/toolbar/small/color_button.xcf diff --git a/NEWS b/NEWS index 86acac5..3372a02 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,5 @@ 1.6.12: + * Added a toolbar color button for setting text and background colors. * Added a "start a new discussion" link to each discussion forum page. * Updated Luminotes Server INSTALL file with instructions for setting the http_url configuration setting. diff --git a/controller/Html_cleaner.py b/controller/Html_cleaner.py index b562083..8b89a3a 100644 --- a/controller/Html_cleaner.py +++ b/controller/Html_cleaner.py @@ -19,6 +19,8 @@ class Html_cleaner(HTMLParser): Cleans HTML of any tags not matching a whitelist. """ NOTE_LINK_URL_PATTERN = re.compile( '[^"]*/notebooks/\w+\?[^"]*note_id=\w+', re.IGNORECASE ) + COLOR_RGB_PATTERN = re.compile( "^rgb(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*)$" ) + COLOR_HEX_PATTERN = re.compile( "^#\d{6}$" ) def __init__( self, require_link_target = False ): HTMLParser.__init__( self, AbstractFormatter( NullWriter() ) ) @@ -110,6 +112,7 @@ class Html_cleaner(HTMLParser): 'caption', 'col', 'colgroup', + 'span', ] # A list of tags that require no closing tag. @@ -124,7 +127,8 @@ class Html_cleaner(HTMLParser): 'p': [ 'align' ], 'img': [ 'src', 'alt', 'border', 'title', "class" ], 'table': [ 'cellpadding', 'cellspacing', 'border', 'width', 'height' ], - 'font': [ 'color', 'size', 'face' ], + 'font': [ 'size', 'face', 'color' ], + 'span': [ 'style' ], 'td': [ 'rowspan', 'colspan', 'width', 'height' ], 'th': [ 'rowspan', 'colspan', 'width', 'height' ], } @@ -168,6 +172,12 @@ class Html_cleaner(HTMLParser): else: bt += ' %s=%s' % \ (xssescape(attribute), quoteattr(attrs[attribute])) + if attribute == 'style': + if self.style_is_acceptable( attrs[ attribute ] ): + bt += ' %s="%s"' % (attribute, attrs[attribute]) + else: + bt += ' %s=%s' % \ + (xssescape(attribute), quoteattr(attrs[attribute])) if tag == "a" and \ ( not attrs.get( 'href' ) or not self.NOTE_LINK_URL_PATTERN.search( attrs.get( 'href' ) ) ): if self.require_link_target and not attrs.get( 'target' ): @@ -209,6 +219,29 @@ class Html_cleaner(HTMLParser): return parsed[0] in self.allowed_schemes + def style_is_acceptable(self, style): + pieces = style.split( ";" ) + + for piece in pieces: + piece = piece.strip() + if piece == "": + continue + + param_and_value = piece.split( ":" ) + if len( param_and_value ) != 2: + return False + + ( param, value ) = param_and_value + value = value.strip() + + if param.strip().lower() not in ( "color", "background-color" ): + return False + if not self.COLOR_RGB_PATTERN.search( value ) and \ + not self.COLOR_HEX_PATTERN.search( value ): + return False + + return True + def strip(self, rawstring): """Returns the argument stripped of potentially harmful HTML or JavaScript code""" self.reset() diff --git a/static/css/style.css b/static/css/style.css index e222981..a436d01 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -111,11 +111,11 @@ h1 { } #toolbar .bold_large { - background-position: 440px 0; + background-position: 480px 0; } #toolbar .bold_small { - background-position: 220px -2px; + background-position: 240px -2px; } #toolbar .italic_large { @@ -142,6 +142,14 @@ h1 { background-position: 60px -2px; } +#toolbar .color_large { + background-position: 400px 0; +} + +#toolbar .color_small { + background-position: 200px -2px; +} + #toolbar .font_large { background-position: 360px 0; } @@ -159,11 +167,11 @@ h1 { } #toolbar .insertUnorderedList_large { - background-position: 400px 0; + background-position: 440px 0; } #toolbar .insertUnorderedList_small { - background-position: 200px -2px; + background-position: 220px -2px; } #toolbar .insertOrderedList_large { @@ -848,6 +856,42 @@ h1 { text-decoration: none; } +#color_table { + border-collapse: collapse; + border: 1px solid #000000; + margin-top: 1em; +} + +#color_table td { + border: 1px solid #000000; + width: 1.5em; + height: 1.5em; + text-align: center; + vertical-align: middle; + cursor: pointer; + padding: 0; + margin: 0; +} + +#color_table .color_box { + font-size: 110%; + outline: none; + font-weight: bold; + border: none; + width: 1.5em; + height: 1.5em; + padding: 0; + margin: 0; +} + +.color_box_dark_selected { + color: #ffffff; +} + +.color_box_light_selected { + color: #000000; +} + .selected_mark { vertical-align: top; } @@ -1154,6 +1198,12 @@ h1 { .radio_label { color: #000000; + border: none; + outline: none; + background-color: transparent; + padding: 0; + -moz-user-select: none; + -webkit-user-select: none; } .radio_label:hover { @@ -1161,6 +1211,10 @@ h1 { cursor: pointer; } +.small_button { + font-size: 100%; +} + .hook_action_area { -moz-border-radius: 5px; -webkit-border-radius: 5px; diff --git a/static/images/toolbar/buttons.png b/static/images/toolbar/buttons.png index fc43bc12b36e5de1fc1979b157a72b5d6e9433c4..a76353abc69dfd2c0c4d3e4711729ba7dad36e5a 100644 GIT binary patch literal 3937 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 literal 3529 zcmbW4i#wBj{Ku*1aVSgC<5<+5JyDT*5>FUqlg(sKBVi+^kmlkM%SK|3^d zNJ&Y7kO;V=l$7+SWURYeMskl#T5Obrj1S5hE=dV1utuUJDPyYa*EZ{k70-U`aXT?Tj+D5y?mS`!AvWYLuYO_ka7GxxN~gy)S0`a>M8-VxblL7 z%P)-Z^uV0rz>6+=DyeNg@86RysbNFdagtz)=4)&B{nOP$zYz$NaCtmUowj`E*IP|s zcOHVg!H#HGmUQGv|0U6IW0c$A1h?`(G>zck`)^<0fhAt&t$zt|uU`-wSSTj`c^?5$ z8muX*sAy_xie0@Ccm|#Gl%S}qs_&uOKxA*6m{iGCd+-36A4Y;~vKRK5V%zR0vpmj& z!C>kQztz~=e4YF=2hPgM(YX1~_ZfVleY$3SY-o%{;a>iD}aDeQjftM$0sJ zWNBxoZmyw~w2vF!J^Z56%<20hf5<>();eFC8{qtRKk4(wN}#`=wyXy&(|S8>KB_ve zs<7wqn{h>l&7-+8&j*BF-yontEUO7yJs{X`;!H9N899cF(bQ046DHf2mX;!W&7!*m ziz~6sUt2KZOw*p5MYVNEBV*Kcp5Dr9SpaRJpG@*8EUTvEp0p01AM3+XrWJh)K^rFD z5nNU5y^8WI4?6sN_W_#n}EBn}&BW-(qH8Npk@}RLQSq zLu3`~S=R7Y@krn3&nRIXK=C}}=6-arBoopc6}GwDrV4bBkz=E*vlwZjhpN|{UJ~Gh z%l1{{Tf{5-!|qNDc%j0BY<+TrvegrMx6iKTGJ7NdfT`%-OlK5t5YL$;AepY`4bunQ za_3afE(wpWd=BbJ@^a~*NZ!@%V?2oX>J^iAeO!CBzj!8c)w zb*HYgJ;OH${V`*^`19siW}TMf`$d`ORlzw`!HESrT)GL{uLr;OmcBHQrA%rbigX{F zyZ}#gJP}h5nR2{b8gAD1S;}s4$BAl$zbr|LOt$f{0c9k%fhZYx`A76Whe_K;5~mv` zfGTbFPwGTI6ewlIkMoR25LAY5jqQF2-N_zPH$LhgvJe@UyBgm$)4Fk!Yit?`iEAbj z``r-@3ZUd(yR6nBUi1^F$Eamk1##=a`lPC);*1;Ub$VXgRJPM>CUM@?(cDzNNSDujH_y?D(}D#gF>fDqX6)~Futu7&lPvc0e?Aeb)F$p4zk5XZ0ZM7Vs3%~IPk0@5wb`W(#tSMX-KHooH^oV<*zQ5A z+`+zp*S~kY_~61ySb2|cHVUK8=!4sJs*G5+?RL8PiIe=!2aD$G&u5@dZ7olYbr?`u@o$yUh&Sa(%QF0m z-`eFH3edHRiR{AwXI~Kkl^lMbuI0T}z|S~zl^=c~GP)V(^=X}cck&0rYka%4@uf(;Rqtj9ux?ful3yjiCn zDf8H=JvsG1@l*rHnC*TI8TkJy}=X)Z{G_0!PmvhqQV4`0QE0 z^T6xh1BV_QRJV<9{BFa|w6GH{@BN&hKBJx{waqpirCQt-zc%tgveKrjVbJ_e*EAut zG11u9)ckg$0f*DN0QXWaRV*& zqA?+R{!n%ycFN|E3LhGe=p|E;nPybN7vLH`=uzbWe<$DG!{Y*F=YH<|n(@idE9-)4 z;0GV&1CjBFcrc^?EcVt|Lg;YBuzMyBQjBB$URo@H$*k1hyCdQD>x|P@h4v8S}>Pn z=62Z7TpX?3iDf0SS;T!?`m52W0iLAgH5)>B$v@bhW;vbwZei0@88(tC6c=d}ADM6Z zP}Ox#$@ik)Ge{6)RD64eV|Om;oUwfSb?HJq@P3_a)Jjm zB(fkwn6W$|Mye7Jo|sFlBq(QuC|-(GC3_#A{}HoH<5{*_GoJq)*90MbuJ)=X zbd;)%-py#MPU=I|`Kz?mq_9dpUMuBF$1Iid!0oc>>3|wCWv7lVJ#~+3r%%aGt#g>@ zrd@)tU6q~Y10kk8i@O6db$&@p4W|65ks+-&;+<8$;rZSO1v0j-T`24fVXzbkV`M#K z(|V(r#`9^nRz8IAv0FKBT*d<*IrMOxM4A(`Q7SI;En_(dduq+0RY!{KIC7?W1jppMC~BHbzl7{=+8RWMxdqrMQkh z{7B9zfPu!d5!ADar ztC5-(wFn6EmlxLLAB1QA(W7TNfJNHoohc&)t5^1_9u4@lYsj?s*4=vG%#zE$Nh?{+ zgpOcyznRV5f>gnk{a|Q*_BebWv~?u`TC_s_wDqJD>Z%0tTtL`80w@0f4IRtuQQn2Z zhQ?2Ps#$uX9aC`=Z+orf_awg#NSHfpVXv}-i?fRh2IF$f3+tr3YoPA}+0Zv^Tb>Di zxHjk)t7#1%y~oD;lZ{%3>t7KvayM627tO?{SLZM&Bu=tS2H}H{hl}unx0N? zo2_ncV@cgFwdafk1?`egi7T+EVuC#44`|& z6nG7w?>?K!JvN}3)5be#1s8>BDL#Jum(FpGFA3=aQehyoN&q88v;Na;!y}6Y&%T-llMQ1j$1rsk*&QObS9s?> z?ka)nyS%+~LG}qq5ez5m zS&$>^+5etSprP+2f7OP3@w?IM%HwYaDX!2y;Mc7GGrDD4(wB~{76c@myd*h&Ng=K5 K;8m91xBmyVlL=1% diff --git a/static/images/toolbar/color_button.xcf b/static/images/toolbar/color_button.xcf new file mode 100644 index 0000000000000000000000000000000000000000..558beb42d62ffb69214f37bb636b7eaa02a9c0af GIT binary patch literal 1221 zcmbVL&1(};5TD(R`82jgEmT1tp3+0KJ@lx89z?KGs3%c$yLr2Ln(UU{6%#z{LA?te z6#ozr4_-W0@#9ZWi4?pkshEW4%svtejRny!(;1C&~9@-#(yB^{Kb&lF>kQ@B5ty4 zP0lV|UR@*aY#J`ww#VyUC>q3JhEc3Vaks^p@M9iE+=)ftM~mf}>p4Ly2w5#v_0Zd} z+0yByR3;e9%C(588xvBspY3l@YA4#X9pQUyS=U0cJBd_W{hlvkY~E>d>beuSZp7n% z4N~@ADr0u1(ma$HZ!*K$R>TbI;|Q8?buf77D%m zR7hZJimKG*Rp^(+tM7yUr^m;@g3-CJ@_-!u;<%6>XMf9gH%}HyXCCx$@})2GxrL+L zsk{K-@`HurAHI~gaq`2SJVk0Ci(^WWS;)Z6GXCH(&*+#D9%H~ytg&_DL_Vuw!sz_B zw0;A7=~wQ&-X89Je6#@9X|Vcf&#d!S_L$hJ9o;3DUYx!t33B^gltU_>F3>E(LZIlK f6Rjk73k_Wr`COzNip!<@PplQF%7rPBzG9BX8&-8VHk#CLSt|1AF$bMHoJY%B2g4YQJgCge*xDl?agQKuQXnmIjX> z51&78OnmwI@RcjUm+#pF@7rfgJfW_$wiXyh7dWY@Fx{t=q0t9L`TOq+w>;L{#o`yK zZPtFZ`?qSvb?6X$fA}z*oekS;Fm(mam;qN*m>aKE8Un^;5TeHImSsu$`jw`0bMx|Y zb0cjaeEX(*fT{x#?tpaZqF%45$(Z=8&g#`L-N063+cx;<(csG;KZfVbF(#h&(#0@* zI80qMdLK1?eqZ7>5aN}eKVf=cvpQBezZULd5Jf={#Xt%)e^fHm;cz+~j*^vaNQztk#S3SR5H#jd3zH!6YxZK_?gQgV)86rjb@dMtnCHVGZ#=!62!`l13y`b$nEiX4aqR9C21s*WK>`0gxA?ZhauFV*jn?cY%nNeTs z)~zsAgqt?$8%I10Qp9M2fUU@*NASCM>iOyE@afa=uwgI_ZlXbpOmM7~K?sE?-o6b# ze~vk!Dne@LIvi%ltS?ii!WS>XYu3QXu(ZVJawgQ@J$@YS-W{Gb3#QEF_3LnRvoUeT z3v@gpe|>r)A}8twLhy>eWrf#rJu96?8f!p(*+i_LG_+73@5qtCms=$TO$D#p zt?zzf4IN)9SeVG5E}Q=S;p%E5_a1oSk97%wE(SqZu>yae1|_$ZNbg6Z?px|o=-U_G zycwnnB-D2jj4EeZ+r>tnJ{_jF9IVnMXBJPNf5JnCM7I9Wq3Z1-r_BunpU>y>`KUz` z!0-3_73Kf)tu+dPqE}BR+JzPhYS~lD-s+^Jz;wf$Ib(K2@db6+)YQOUuMvBtu}oTP zmxr1Tx1K1zdNFJ4`p!ZHNKTA)$=c1GCMMBKo?R)1-csc z?+?t+$nbcysa;Ctqwg>yW5NVuNvUKun9rVmN!i`fqfp&A(>unf4?3M40=y&s63|gy>ln59b-jZsx*k#fVa|| zPN&o947}!;larH^on27ia=BbC*TaWo%+JqvI2;a#W6Bh#)9G|L0^d*2mvDVmG>C^@ z&!hv_qldn6#mgW?K|sSGH5FdB4#oxN@%;V`<2vwo(6I7&=mSJXL9eOO|NTLke*~k6 zw?T^Z!J}~Z31!&mgG!9Gp>i$wTOHPex^z#Tgz3XblzFCxK2?BSyP6wsqKU#uz_Zyf&c&j07*qoM6N<$f)ni1^#A|> delta 1821 zcmV+&2jckO53mj)iBL{Q4GJ0x0000DNk~Le0002+0000K5C#AM0OJsBMUf#Xe+JM= zL_t(|+U=Zuhzx%m$9HaL&vV3y$Uk?tJ3BLbA><($OG`Xn zRn^eY(9qcU=@aiA7YGCbfq*A+x*re>27|J^aG@kgk|fQW#~5RbEnkl5PzYYXJ`~E& z&(Htz1zYXj&3nfMZXo@4L)W)291g=bZ}N^zAZR~~S*NA66BmIXBdDsXe@3It&G6j0 z_T`$U>zY@QD-fy?ZeAaNNId^|^C!zkZpC zgD!w>Ss-W&;{TExwD)&TeY?C=Hz+JDDk?0TGKHokDKAfwfaK-Xh2U2kr7+D_MXUDLjQ_a;iGe;=${H+a=5>w4Le znYvABDSYWt>&tiTg7@t4CZ1%!W4b4#*C%wBE-;-3)WhHojQxB6oR(*LyG-nn)x5sf z{{4P!7!DqU4;_Mwi{W4prlF#lGvO;&_{Qs(hStX!Ff!gm27%1h?H$WBKYqYFcUtFD z2AAuUmBH`dx4wMre^~h1HE-i`x&Zq9PI|}?xV|24Y2gzGNaf7UZJc^g{CXT`%zzgy zf++*09vMoUjvt37OyC`HuF;ML*m~k7`|Cv_iV}&OJxkLl&f>kh1ZBXB7hC7wy~{f? zf6%on9E(}!_v{Iml)%@odmC4_bwS&q8@!qtKHqP}en!$Xe`Dz3U<@~^jBVP~`u5|; z!*Ach_VcNuU>~}wuI3$4+xYw$9z2+LB!Uc~>$0U%m{hsyp#?lrHE42jKbh zz4*3NyPy*dfAE+wFsj%Yvko04*28(v*u;sMjyyfPg|5R?d`LI)er47yn6AS|kHSlr z!sEula@tSBWCq?nRQ5RB-!A10`0uZ2{WP~{ynHiYd*(x9aho^8ojb!DH@X`~wiIDK zdDDdyfBAFFN!HNNjcX*rJ7#y8HVwXT z0bactrlBl5UZ2OwKKIx$xOZ=O_H1~^4w#0JXeh*QLpHpi<3a6$6V#qiV&Wp2rfGj> zvi3}uK(SV0zM@2se=LL>8(}JHbZ?KW_KIW;5?!FPy@8^q8|a3M7k+8*A3uhvhrkI8#RLM69;NzC zDPIWenXF+LhGG0&Ok$d*$=ILs{WS_q(OakeT1>&4{Cs%C2-ur0)Q$sn>DSf4@wgY? z>QrQ9U!H7z=k3GtO6Fsg&PYd`Jij&?vc=akwH9La_!Ryw+iK5xuX765@e(&wu ze_?x#3k|tZks>|zcni`lMe?9od>z6KQAA`q>o;*RuOT`F(gEwY{LRfFj z8rqI`{Cv568&39aWL`yBl(Eor(Pz%UbLLo=^Fq9eQs4(P4R%T-#Q!fl@&3Bfj$#nT z0)Z`Ca8cg66%A}4z?f;8W^FAxpn^eMvT4k4!2)!QQTcnWGJ5pCm-A}q=nq4gbFSau zeFGrKZS0RL(wLupIq&Dp1?l1a@0M}{9FH4DJYHQb%aSC^hJldRuEiU%R;<8UGz!(! zgmSF%@_5`Z4CDNHuXj)&5D3zvqZeTb1OkCTAP@)y0)apv5V*;2ERn14>K1cd00000 LNkvXXu0mjf2bhvl diff --git a/static/images/toolbar/small/color_button.xcf b/static/images/toolbar/small/color_button.xcf new file mode 100644 index 0000000000000000000000000000000000000000..cd41fc2d74be19b1610e14541bedb47ec5fa5061 GIT binary patch literal 1045 zcma)5K~EDw6rOfl*rJ6Rm8)~Jkd%P_13U;30ts9^F3aq8Lc2?L*Vvkv9{d9&TnPt$ z1P4vLk$CV#VhDf0L~eKi4+g()cNR)0!b`q+-+b@Anb~=>zHV>Ij_XNJx0HZAF(7{g z6bcv~1NQ8e5Cn`#C7=R~8_v9^$X|i4`dGH%hV8Zr63hjIcE#X`CbsagG8e>-UT|r;oYX58z8?z2T@?D`@$0lGj R>lf!0QNF0&13mFw`~x 0 ) + var range = selection.getRangeAt( 0 ); + else + var range = this.document.createRange(); + node = range.endContainer; + } else if ( this.document.selection ) { // browsers such as IE + var range = this.document.selection.createRange(); + node = range.parentElement(); + } + + while ( node ) { + var name = node.nodeName.toLowerCase(); + if ( name == "body" ) + break; + + if ( node.hasAttribute && node.hasAttribute( "style" ) ) { + if ( foreground == null ) { + foreground = getStyle( node, "color" ) + if ( foreground == "transparent" ) + foreground = null; + } + if ( background == null ) { + background = getStyle( node, "background-color" ) + if ( background == "transparent" ) + background = null; + } + } else if ( name == "font" && node.getAttribute( "color" ) ) { + foreground = node.getAttribute( "color" ); + } + + if ( foreground && background ) + break; + + node = node.parentNode; + } + + return [ + foreground ? Color.fromString( foreground ).toHexString() : null, + background ? Color.fromString( background ).toHexString() : null + ]; +} + +Editor.prototype.set_foreground_color = function( color_code ) { + if ( GECKO ) this.exec_command( "styleWithCSS", true ); + this.exec_command( "forecolor", Color.fromString( color_code ).toHexString() ); + if ( GECKO ) this.exec_command( "styleWithCSS", false ); +} + +Editor.prototype.set_background_color = function( color_code ) { + if ( GECKO ) this.exec_command( "styleWithCSS", true ); + if ( MSIE ) + this.exec_command( "backcolor", Color.fromString( color_code ).toHexString() ); + else + this.exec_command( "hilitecolor", Color.fromString( color_code ).toHexString() ); + if ( GECKO ) this.exec_command( "styleWithCSS", false ); +} + Editor.prototype.shutdown = function( event ) { signal( this, "title_changed", this, this.title, null ); this.closed = true; diff --git a/static/js/Wiki.js b/static/js/Wiki.js index f52c15c..0e522a5 100644 --- a/static/js/Wiki.js +++ b/static/js/Wiki.js @@ -344,6 +344,7 @@ Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_wri connect( "italic", "onclick", function ( event ) { self.toggle_button( event, "italic" ); } ); connect( "underline", "onclick", function ( event ) { self.toggle_button( event, "underline" ); } ); connect( "strikethrough", "onclick", function ( event ) { self.toggle_button( event, "strikethrough" ); } ); + connect( "color", "onclick", this, "toggle_color_button" ); connect( "font", "onclick", this, "toggle_font_button" ); connect( "title", "onclick", function ( event ) { self.toggle_button( event, "title" ); } ); connect( "insertUnorderedList", "onclick", function ( event ) { self.toggle_button( event, "insertUnorderedList" ); } ); @@ -357,6 +358,7 @@ Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_wri this.make_image_button( "italic" ); this.make_image_button( "underline" ); this.make_image_button( "strikethrough" ); + this.make_image_button( "color" ); this.make_image_button( "font" ); this.make_image_button( "title" ); this.make_image_button( "insertUnorderedList" ); @@ -1498,7 +1500,8 @@ Wiki.prototype.update_toolbar = function() { this.update_button( "italic", "i", node_names ); this.update_button( "underline", "u", node_names ); this.update_button( "strikethrough", "strike", node_names ); - this.update_button( "font", "font", node_names ); + this.update_button( "color", "color", node_names ); + this.update_button( "font", "fontface", node_names ); this.update_button( "title", "h3", node_names ); this.update_button( "insertUnorderedList", "ul", node_names ); this.update_button( "insertOrderedList", "ol", node_names ); @@ -1574,6 +1577,30 @@ Wiki.prototype.toggle_attach_button = function ( event ) { event.stop(); } +Wiki.prototype.toggle_color_button = function ( event ) { + if ( this.focused_editor && this.focused_editor.read_write ) { + this.focused_editor.focus(); + + // if a pulldown is already open, then just close it + var existing_div = getElement( "color_pulldown" ); + + if ( existing_div ) { + this.up_image_button( "color" ); + existing_div.pulldown.shutdown(); + existing_div.pulldown = null; + return; + } + + this.down_image_button( "color" ); + this.clear_messages(); + this.clear_pulldowns(); + + new Color_pulldown( this, this.notebook.object_id, this.invoker, event.target(), this.focused_editor ); + } + + event.stop(); +} + Wiki.prototype.toggle_font_button = function ( event ) { if ( this.focused_editor && this.focused_editor.read_write ) { this.focused_editor.focus(); @@ -4463,6 +4490,265 @@ Suggest_pulldown.prototype.shutdown = function () { } +NAMED_COLORS = [ + [ "#000000", "black" ], + [ "#333333", "steel gray" ], + [ "#696969", "dim gray" ], + [ "#808080", "gray" ], + [ "#a9a9a9", "dark gray" ], + [ "#d3d3d3", "light gray" ], + [ "#f5f5f5", "white smoke" ], + [ "#ffffff", "white" ], + + [ "#800000", "maroon" ], + [ "#8b0000", "dark red" ], + [ "#b22222", "fire brick" ], + [ "#dc143c", "crimson" ], + [ "#ff0000", "red" ], + [ "#ff4500", "orange red" ], + [ "#ff6347", "tomato" ], + [ "#ffa07a", "light salmon" ], + + [ "#8b4513", "saddle brown" ], + [ "#a52a2a", "brown" ], + [ "#a0522d", "sienna" ], + [ "#d2691e", "chocolate" ], + [ "#ff8c00", "dark orange" ], + [ "#ffa500", "orange" ], + [ "#ffd700", "gold" ], + [ "#ffff00", "yellow" ], + + [ "#556b2f", "dark olive green" ], + [ "#006400", "dark green" ], + [ "#008000", "green" ], + [ "#2e8b57", "sea green" ], + [ "#32cd32", "lime green" ], + [ "#00ff00", "lime" ], + [ "#7cfc00", "lawn green" ], + [ "#98fb98", "pale green" ], + + [ "#008b8b", "dark cyan" ], + [ "#20b2aa", "light sea green" ], + [ "#00ced1", "dark turquoise" ], + [ "#66cdaa", "medium aquamarine" ], + [ "#40e0d0", "turquoise" ], + [ "#00ffff", "cyan" ], + [ "#7fffd4", "aquamarine" ], + [ "#afeeee", "pale turquoise" ], + + [ "#191970", "midnight blue" ], + [ "#000080", "navy" ], + [ "#0000ff", "blue" ], + [ "#4169e1", "royal blue" ], + [ "#4682b4", "steel blue" ], + [ "#6495ed", "cornflower blue" ], + [ "#87ceeb", "sky blue" ], + [ "#add8e6", "light blue" ], + + [ "#4b0082", "indigo" ], + [ "#800080", "purple" ], + [ "#9400d3", "dark violet" ], + [ "#8a2be2", "blue violet" ], + [ "#ba55d3", "medium orchid" ], + [ "#da70d6", "orchid" ], + [ "#ee82ee", "violet" ], + [ "#dda0dd", "plum" ], + + [ "#c71585", "medium violet red" ], + [ "#ff1493", "deep pink" ], + [ "#db7093", "pale violet red" ], + [ "#ff69b4", "hot pink" ], + [ "#ffb6c1", "light pink" ], + [ "#ffc0cb", "pink" ], + [ "#ffdab9", "peach puff" ], + [ "#ffe4e1", "misty rose" ], +] + + +function Color_pulldown( wiki, notebook_id, invoker, anchor, editor ) { + anchor.pulldown = this; + this.anchor = anchor; + this.editor = editor; + this.initial_selected_mark = null; + this.selected_color_box = null; + + Pulldown.call( this, wiki, notebook_id, "color_pulldown", anchor ); + + this.invoker = invoker; + + var DEFAULT_FOREGROUND_CODE = "#000000"; + var DEFAULT_BACKGROUND_CODE = "#ffffff"; + var current_colors = editor.current_colors(); + + this.foreground_code = current_colors[ 0 ]; + if ( this.foreground_code == DEFAULT_FOREGROUND_CODE ) + this.foreground_code = null; + + this.background_code = current_colors[ 1 ]; + if ( this.background_code == DEFAULT_BACKGROUND_CODE ) + this.background_code = null; + + var foreground_attributes = { "type": "radio", "id": "foreground_color_radio", "name": "color_type", "value": "foreground" }; + var background_attributes = { "type": "radio", "id": "background_color_radio", "name": "color_type", "value": "background" }; + + if ( this.foreground_code || !this.background_code ) { + foreground_attributes[ "checked" ] = true; + } else { + background_attributes[ "checked" ] = true; + } + + this.foreground_radio = createDOM( "input", foreground_attributes ); + + // using a button here instead of a