Changeset 3138 for trunk


Ignore:
Timestamp:
01/16/11 22:07:22 (9 years ago)
Author:
jukka
Message:

Added a better support for editing tables.

Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LeMillTool.py

    r3137 r3138  
    4747 
    4848 
    49 good_sites=["http://www.youtube.com/", 
    50     "http://video.google.com/", 
    51     "http://s3.amazonaws.com/slideshare/", 
    52     "http://www.macromedia.com/go/", 
    53     "http://odeo.com/", 
    54     "http://fpdownload.macromedia.com/", 
    55     "http://www.schooltube.com/", 
    56     "http://maps.google.com/",  
    57     "http://ourmedia.org/players/1pixelout/audio-player.js",  
    58     "http://channels.ourmedia.org/",  
    59     "http://www.archive.org/", 
    60     "http://www.metacafe.com/", 
    61     "http://dotsub.com/", 
    62     "http://www.wiris.ee"] 
    63  
    64 urlfinder=re.compile(r"(http://\S*)", re.IGNORECASE) 
    65  
    66  
    67 html_strip=re.compile(r""" 
     49HTML_STRIP=re.compile(r""" 
    6850    (?P<paragraph><\s*?/p\s*?>\n?) # closing paragraph tag, </p> < /p >, </p   >  
    6951    |(?P<linebreak><\s?br[\s/]*?>\n?) # <br>-tag or <br /> or < br/>... 
    7052    |(?P<lonetag><.*?>\s*?) 
    7153    """,  re.IGNORECASE | re.VERBOSE | re.MULTILINE | re.DOTALL) 
    72  
    73 bad_link_chars=re.compile(r"""["'?=*[)(}{|]""")   # This is used to check if first word in  
    74 # a bracket link contains special characters; if so, then word cannot be a legit id. 
    75 #http://127.0.0.1:8081/lemill-server/content/webpages/fluxogramas 
    7654 
    7755 
     
    9270 
    9371# whitelist   
    94 attribs={"p":[],"a":["href","target","name", "title"],"br":[],"strong":[],"b":[],"i":[],"em":[],"h2":[],"code":["class"],"pre":[],"li":[],"ul":[],"ol":[],"table":["width", "height", "border", "padding", "margin"],"tbody":["width", "height", "border", "padding", "margin"], "tr":["width","height","rowspan"],"th":["width","height","colspan","rowspan"],"td":["width","height","colspan"],"sub":[],"sup":[],"dt":[],"dd":[]} 
    95 good_tags=attribs.keys() 
     72GOOD_TAG_DICT={"p":[],"a":["href","target","name", "title"],"br":[],"strong":[],"b":[],"i":[],"em":[],"h2":[],"code":["class"],"pre":[],"li":[],"ul":[],"ol":[],"table":["width", "height", "border", "padding", "margin"],"tbody":["width", "height", "border", "padding", "margin"], "tr":["width","height","rowspan"],"th":["width","height","colspan","rowspan"],"td":["width","height","colspan"],"sub":[],"sup":[],"dt":[],"dd":[]} 
     73GOOD_TAGS=GOOD_TAG_DICT.keys() 
    9674 
    9775# block tags -- when one of these is encountered, don't change linebreaks to </br>:s anymore. 
    98 block_tags=['p','table','ul','ol','h2']  
     76BLOCK_TAGS=['p','table','ul','ol','h2']  
    9977 
    10078# search pattern 
    101 parser_pattern=re.compile(r""" 
     79PARSER_PATTERN=re.compile(r""" 
    10280    (?P<bracket>\[.*?\]) # everything that is put inside brackets 
    10381    |(?P<url>https?://[^\s]*) # http://something, where http is not preceded with " or ' 
     
    10684    """, re.IGNORECASE | re.VERBOSE | re.MULTILINE | re.DOTALL) 
    10785 
    108 linebreak_pattern=re.compile(r"\r\n|\n|\r")          
     86LINEBREAK_PATTERN=re.compile(r"\r\n|\n|\r")          
     87 
     88BAD_LINK_CHARS=re.compile(r"""["'?=*[)(}{|]""")   # This is used to check if first word in  
     89# a bracket link contains special characters; if so, then word cannot be a legit id. 
    10990 
    11091 
     
    127108            return 
    128109        if self.no_tags: 
    129             if tag in block_tags: 
     110            if tag in BLOCK_TAGS: 
    130111                self.no_tags=False 
    131         if tag in good_tags: 
     112        if tag in GOOD_TAGS: 
    132113            self.open_tags.append(tag) 
    133114            if tag=='code':  
     
    138119                else: 
    139120                    self.pre=True 
    140             good_attrs=attribs[tag] 
     121            good_attrs=GOOD_TAG_DICT[tag] 
    141122            adats=[] 
    142123            for key,value in attrs: 
     
    176157        if link[0].startswith("http://"): # external links won't need any processing 
    177158            link=link[0] 
    178         elif link[0] and not bad_link_chars.search(link[0]): 
     159        elif link[0] and not BAD_LINK_CHARS.search(link[0]): 
    179160            matches=self.pc({'getId':link[0].lower()}) # find things with this id 
    180161            if matches: 
     
    243224        if data: 
    244225            if self.no_tags:            
    245                 data=linebreak_pattern.sub(lambda x: '<br/>', data)                 
    246             data=parser_pattern.sub(self.replaceMethod, data) 
     226                data=LINEBREAK_PATTERN.sub(lambda x: '<br/>', data)                 
     227            data=PARSER_PATTERN.sub(self.replaceMethod, data) 
    247228            self.resdata.append(data) 
    248229 
     
    313294         
    314295 
    315     def isGoodEmbed(self, code): 
    316         """ Check if sent code is compatible with known nice sources """         
    317          
    318         #1. find urls 
    319         founds = urlfinder.findall(code) 
    320  
    321         #2. check if urls fit to profiles 
    322          
    323         if not founds:  
    324             if code.startswith("<script"): 
    325                 return False # We don't want javascript trickery, even if it is local 
    326                  
    327         for match in founds: 
    328             permitted=False 
    329             for nice_site in good_sites: 
    330                 if match.startswith(nice_site): 
    331                     permitted=True 
    332             if not permitted: 
    333                 return False 
    334         return True 
    335  
    336      
    337     def parse_embed_content(self, text): 
    338         """ for showing only first embed tag """ 
    339          
    340         embed_tags = re.compile(r""" 
    341             (?P<img><img.*?>) 
    342             |(?P<object><object.*?>) 
    343             |(?P<embed><embed.*?>) 
    344             |(?P<iframe><iframe.*?>) 
    345             |(?P<applet><applet.*?>) 
    346             """, re.IGNORECASE | re.VERBOSE | re.DOTALL | re.MULTILINE) 
    347          
    348         # find starttags 
    349         starttags = re.finditer(embed_tags, text) 
    350         for starttag in starttags: 
    351             start = starttag.start() 
    352             tagtext = text[start:] 
    353              
    354             # if tag don't have ending tag '>' 
    355             starttag_text = starttag.group() 
    356             if (starttag_text.count('<') > 1): 
    357                 continue 
    358  
    359             tag_end = re.compile(r"""(?P<enclosed_tag>/\s*?>)|(?P<not_closed>>)""") 
    360             tagending = re.search(tag_end, tagtext) 
    361             if (tagending.group('enclosed_tag')): 
    362                 return starttag.group() 
    363              
    364             if starttag.group('img'): 
    365                 tag = re.search('<img.*?>\s*?</img\s*?>', tagtext) 
    366                 if tag: 
    367                     return tag.group() 
    368                 else: 
    369                     return starttag.group() 
    370             if starttag.group('object'): 
    371                 # because in one object tag can also be another object tag, then we need to find enclosing tag for first object tag 
    372                 allobjectsend = re.finditer('</\s*?object\s*?>', tagtext) 
    373                 i = 1 
    374                 for o in allobjectsend: 
    375                     end = o.end() 
    376                     content = tagtext[:end] 
    377                     object_beginnings = re.findall('<\s*?object', content) 
    378                     count = len(object_beginnings) 
    379                     if count == i: 
    380                         tagtext = content 
    381                         break 
    382                     i += 1 
    383                 return tagtext 
    384             endtag='' 
    385             if starttag.group('embed'): 
    386                 endtag = re.search('</embed\s*?>', tagtext) 
    387                 if not endtag: 
    388                     return starttag.group() + '</embed>' 
    389             if starttag.group('iframe'): 
    390                 endtag = re.search('</iframe\s*?>', tagtext) 
    391                 if not endtag: 
    392                     return starttag.group() + '</iframe>' 
    393             if starttag.group('applet'): 
    394                 endtag = re.search('</applet\s*?>', tagtext) 
    395                 if not endtag: 
    396                     return starttag.group() + '</applet>' 
    397             if endtag: 
    398                 end = endtag.end() 
    399                 return tagtext[:end] 
    400              
    401         return '' 
    402      
    403  
    404296    def html_to_text(self, html): 
    405297        """ Simple parse, change <p> to \n\n and <br/> to \n and remove all other html-tags """ 
     
    414306            else: return '' 
    415307     
    416         return html_strip.sub(replacements, html)          
     308        return HTML_STRIP.sub(replacements, html)          
    417309             
    418310    def shorten_url(self, url, linebreak='<br/>'): 
  • trunk/skins/lemill/getSectionColors.py

    r3052 r3138  
    3636.defaultSkin table.mceLayout {background:%(l)s; border:1px solid %(d)s;} 
    3737.defaultSkin a.mceButtonEnabled:hover {border:1px solid %(d)s; background-color:%(d)s} 
    38 .defaultSkin .mceButton {display:block; border:1px solid %(d)s; width:20px; height:20px} 
    3938""" % {'d':dark,'l':light} 
  • trunk/skins/lemill/lemill_grid.css

    r3137 r3138  
    958958 
    959959/* Button */ 
    960 /*.defaultSkin .mceButton {display:block; border:1px solid #F0F0EE; width:20px; height:20px} */ 
     960.defaultSkin .mceButton {display:block; border:1px solid #F0F0EE; width:20px; height:20px}  
    961961/*.defaultSkin a.mceButtonEnabled:hover {border:1px solid #0A246A; background-color:#B2BBD0} */ 
    962962.defaultSkin a.mceButtonActive {border:1px solid #0A246A; background-color:#C2CBE0} 
    963 .defaultSkin .mceButtonDisabled span {color:#cccccc} 
     963.defaultSkin .mceButtonDisabled {border:1px solid color:#ccc; opacity:0.2; } 
     964 
    964965/*opacity:0.3; */ 
    965966/* Separator */ 
     
    984985.defaultSkin span.mce_charmap {background-position:-300px 0} 
    985986.defaultSkin span.mce_latex {background-position:-320px 0} 
     987.defaultSkin span.mce_delete_table {background-position:-340px 0px} 
     988.defaultSkin span.mce_delete_col {background-position:-360px 0px} 
     989.defaultSkin span.mce_delete_row {background-position:-380px 0px} 
     990.defaultSkin span.mce_col_after {background-position:-400px 0px} 
     991.defaultSkin span.mce_col_before {background-position:-420px 0px} 
     992.defaultSkin span.mce_row_after {background-position:-440px 0px} 
     993.defaultSkin span.mce_row_before {background-position:-460px 0px} 
    986994 
    987995/* ListBox */ 
     
    10141022.defaultSkin .mceMenuItemTitle a {border:0; background:#EEE; border-bottom:1px solid #DDD} 
    10151023.defaultSkin .mceMenuItemTitle span.mceText {color:#000; font-weight:bold; padding-left:4px} 
    1016 .defaultSkin .mceMenuItemDisabled .mceText {color:#888} 
     1024.defaultSkin .mceMenuItemDisabled .mceText {color:#888;} 
    10171025.defaultSkin .mceMenuItemSelected .mceIcon {background:url(img/menu_check.gif)} 
    10181026.defaultSkin .mceNoIcons .mceMenuItemSelected a {background:url(img/menu_arrow.gif) no-repeat -6px center} 
  • trunk/skins/lemill/tiny_mce/themes/lemill/editor_template.js

    r3137 r3138  
    114114            tb.add(cf.createButton('link', {title : 'lemill.link_desc', cmd : 'mceLink'})); 
    115115            tb.add(cf.createButton('unlink', {title : 'lemill.unlink_desc', cmd : 'unlink'})); 
    116             tb.add(cf.createButton('table', {title : 'lemill.table_desc', cmd : 'mceInsertTable'})); 
     116            tb.add(cf.createSeparator()); 
    117117            tb.add(cf.createButton('charmap', {title : 'lemill.charmap_desc', cmd : 'mceCharMap'})); 
    118118            tb.add(cf.createButton('latex', {title : 'lemill.latex_desc', cmd : 'mceInsertLatex'})); 
     119            tb.add(cf.createSeparator()); 
     120 
     121            tinymce.each([ 
     122                ['table', 'table.desc', 'mceInsertTable', true], 
     123                ['delete_table', 'table.del', 'mceTableDelete'], 
     124                ['delete_col', 'table.delete_col_desc', 'mceTableDeleteCol'], 
     125                ['delete_row', 'table.delete_row_desc', 'mceTableDeleteRow'], 
     126                ['col_after', 'table.col_after_desc', 'mceTableInsertColAfter'], 
     127                ['col_before', 'table.col_before_desc', 'mceTableInsertColBefore'], 
     128                ['row_after', 'table.row_after_desc', 'mceTableInsertRowAfter'], 
     129                ['row_before', 'table.row_before_desc', 'mceTableInsertRowBefore'], 
     130                //['row_props', 'table.row_desc', 'mceTableRowProps', true], 
     131                //['cell_props', 'table.cell_desc', 'mceTableCellProps', true], 
     132                //['split_cells', 'table.split_cells_desc', 'mceTableSplitCells', true], 
     133                //['merge_cells', 'table.merge_cells_desc', 'mceTableMergeCells', true] 
     134            ], function(c) { 
     135                tb.add(cf.createButton(c[0], {title : c[1], cmd : c[2]})); 
     136            }); 
     137           //["table","|","row_props","cell_props","|","row_before","row_after","delete_row","|","col_before","col_after","delete_col","|","split_cells","merge_cells"],  
     138 
     139            //tb.add(cf.createButton('table', {title : 'lemill.table_desc', cmd : 'mceInsertTable'})); 
    119140             
    120141            //tb.add(cf.createButton('html', {title : 'lemill.html_desc', cmd : 'html'})); 
  • trunk/skins/lemill/tiny_mce/themes/lemill/js/latex.js

    r3137 r3138  
    4444        tinyMCEPopup.execCommand("mceInsertContent", false, '<code class="latex">begin{equation}'+code+'\\end{equation}<\code>{$caret}'); 
    4545    } 
    46     tinyMCEPopup.restoreSelection(); 
    47     // this is kind of cleanup. I don't know where that empty code-node comes from.  
    48     sel=tinyMCEPopup.editor.selection.getNode(); 
     46    // this is kind of cleanup. I don't know where that empty code-node comes from. 
     47    ed=tinyMCEPopup.editor  
     48    sel=ed.selection.getNode(); 
    4949    tinyMCEPopup.execCommand("mceRemoveNode", false, sel); 
     50    ed.focus(); 
     51    //ed.selection.select(sel); 
     52    ed.selection.collapse(0); 
     53    tinyMCEPopup.storeSelection(); 
     54 
    5055    tinyMCEPopup.close(); 
    5156        
  • trunk/skins/lemill/tiny_mce/themes/lemill/langs/en.js

    r3137 r3138  
    2424link_is_external:"The URL you entered seems to external link, do you want to add the required http:// prefix?", 
    2525insert_latex_desc : 'Insert latex formula', 
    26 insert_latex_explain : 'Enter only your latex mathmode code. Example : \\sqrt{a+b} <br /><a href="http://www.andy-roberts.net/misc/latex/latextutorial9.html" target="_blank">More information</a>, <a href="http://detexify.kirelabs.org/classify.html" target="_blank">LaTeX symbol finder</a>', 
     26insert_latex_explain : 'Enter only your latex mathmode code. Example : \\sqrt{a+b} <br /><a href="http://www.andy-roberts.net/misc/latex/latextutorial9.html" target="_blank">LaTeX tutorial</a>, <a href="http://detexify.kirelabs.org/classify.html" target="_blank">LaTeX symbol finder</a>', 
    2727insert_latex_formula : 'Latex formula', 
    2828insert_latex_preview_verb : 'Preview', 
  • trunk/skins/lemill/tiny_mce/themes/lemill/latex.html

    r3137 r3138  
    1010 
    1111    <div id="general_panel" style="width:90%"> 
    12         <div class="discreet">{#lemill.insert_latex_explain}</div> 
     12        <p>{#lemill.insert_latex_explain}</p> 
    1313        <textarea style="width:100%;padding:0.5em;" id="formula" name="formula" cols="60" rows="4"></textarea> 
    1414        <input type="button" class="updateButton" value="{#lemill.insert_latex_preview_verb}" onclick="updatePreview();" /> 
  • trunk/skins/lemill/tiny_mce/themes/lemill/link.html

    r3015 r3138  
    99    <script type="text/javascript" src="js/link.js"></script> 
    1010</head> 
    11 <body id="link" style="display: none"> 
     11<body id="link" style="display: none;width:300px;padding:0.5em;"> 
    1212<form onsubmit="LinkDialog.update();return false;" action="#"> 
    13     <div class="tabs"> 
    14         <ul> 
    15             <li id="general_tab" class="current"><span><a href="javascript:mcTabs.displayTab('general_tab','general_panel');" onmousedown="return false;">{#lemill.link_title}</a></span></li> 
    16         </ul> 
     13    <p>{#lemill.link_title}</p> 
     14    <table border="0" cellpadding="4" cellspacing="0"> 
     15        <tr> 
     16        <td class="nowrap"><label for="href">{#lemill.link_url}</label></td> 
     17        <td><table border="0" cellspacing="0" cellpadding="0">  
     18              <tr>  
     19                <td><input id="href" name="href" type="text" class="mceFocus" value="" style="width: 200px" onchange="LinkDialog.checkPrefix(this);" /></td>  
     20                <td id="hrefbrowsercontainer">&nbsp;</td> 
     21              </tr>  
     22            </table></td> 
     23        </tr> 
     24        <tr> 
     25        <td class="nowrap"><label for="linktitle">{#lemill.link_titlefield}</label></td> 
     26        <td><input id="linktitle" name="linktitle" type="text" value="" style="width: 200px" /></td> 
     27        </tr> 
     28    </table> 
     29    <div class="clear" style="height:10px"></div> 
     30    <div class="form_submit" style="float: left"> 
     31        <input class="save" type="button" id="insert" name="insert" value="{#insert}" /> 
    1732    </div> 
    1833 
    19     <div class="panel_wrapper"> 
    20         <div id="general_panel" class="panel current"> 
    21  
    22         <table border="0" cellpadding="4" cellspacing="0"> 
    23           <tr> 
    24             <td class="nowrap"><label for="href">{#lemill.link_url}</label></td> 
    25             <td><table border="0" cellspacing="0" cellpadding="0">  
    26                   <tr>  
    27                     <td><input id="href" name="href" type="text" class="mceFocus" value="" style="width: 200px" onchange="LinkDialog.checkPrefix(this);" /></td>  
    28                     <td id="hrefbrowsercontainer">&nbsp;</td> 
    29                   </tr>  
    30                 </table></td> 
    31           </tr> 
    32           <tr> 
    33             <td class="nowrap"><label for="linktitle">{#lemill.link_titlefield}</label></td> 
    34             <td><input id="linktitle" name="linktitle" type="text" value="" style="width: 200px" /></td> 
    35           </tr> 
    36         </table> 
    37         </div> 
    38     </div> 
    39  
    40     <div class="mceActionPanel"> 
    41         <input type="submit" id="insert" name="insert" value="{#insert}" /> 
    42         <input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" /> 
     34    <div class="form_submit" style="float: right"> 
     35        <input class="cancel" type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" /> 
    4336    </div> 
    4437</form> 
  • trunk/skins/lemill/wysiwyg_support.pt

    r3137 r3138  
    33   <div xmlns:metal="http://xml.zope.org/namespaces/metal" xmlns:tal="http://xml.zope.org/namespaces/tal" metal:define-macro="wysiwygEditorBox" tal:define="area_id fieldName | inputname"> 
    44<input type="hidden" id="portal_url" name="portal_url" tal:attributes="value portal_url" /> 
    5 <textarea id="content1" name="content1" tal:attributes="id area_id; name area_id" tal:content="inputvalue" class="mceTextarea" cols="85" rows="40">This will be the editor, since it has a selector class.</textarea> 
     5<textarea id="content1" name="content1" tal:attributes="id area_id; name area_id" tal:content="inputvalue" class="mceTextarea" cols="85" rows="30">This will be the editor, since it has a selector class.</textarea> 
    66    <a href="javascript:void(0);" class="visual_toggle" onclick="$(this).hide();$('textarea.mceTextarea').tinymce().show();$('.html_toggle').show();" style="display:none">[Visual]</a> 
    77    <a href="javascript:void(0);" class="html_toggle" onclick="$(this).hide();$('textarea.mceTextarea').tinymce().hide();$('.visual_toggle').show();">[HTML]</a>     
Note: See TracChangeset for help on using the changeset viewer.