Poprawki w motywach. Ukrywanie DC. Minimalna wysokosc pola tekstowego do edycji
authorLukasz Rekucki <lrekucki@gmail.com>
Mon, 9 Nov 2009 14:16:01 +0000 (15:16 +0100)
committerLukasz Rekucki <lrekucki@gmail.com>
Mon, 9 Nov 2009 14:16:01 +0000 (15:16 +0100)
platforma/static/css/html.css
platforma/static/js/models.js
platforma/static/js/views/html.js
platforma/static/xsl/html2wl_client.xsl
platforma/static/xsl/wl2html_client.xsl
platforma/templates/explorer/editor.html

index acbeac2..682e905 100755 (executable)
@@ -9,6 +9,10 @@
     padding-left: 55px;
 }
 
+.htmlview *[x-node='rdf:RDF'] {
+    display: none;
+}
+
 .htmlview * {
     position: relative;
 }
 }
 
 .htmlview .motyw {
-    position: absolute;
-    right: -8em;
+    /* position: absolute; */
+    
+    float: right;
+    left: auto;
+    clear: right;
+    
+    width: 10em;
     
-    min-width: 5em;
-    max-width: 8em;
-
     font-weight: normal;
     font-size: 10pt;
     font-variant: normal;
     text-decoration: none;
 
-    padding: 0.2em 0.2em 0.2em 0.5em;
+    padding: 0.2em;
+
+    background-color: #efefef;
     
     border: 1px solid gray;
     border-left: 2px dotted gray;
      padding: 3px 5px;
 
      text-decoration: none;
-     z-index: 11;
+     z-index: 1000;
  }
  
 
 }
 
 /* focused editable element */
-/* .htmlview *[x-editable]:hover
-{    
-    
-} */
+.htmlview *[x-editable]:hover
+{
+    z-index: 900;
+} 
 
 .htmlview *[x-editable][x-open]
 {
     padding: 2px;
     cursor: pointer;
     border-bottom: 1px solid black;
+
 }
 
 .context-menu *:last-child {
 }
 
 .context-menu *:hover {
-    background-color: orange;
+    background-color: orange;    
 }
 
 /*
 
 .default-menu:hover {
     opacity: 1;
+    z-index: 5000;
 }
 
 .htmlview *[x-annotation-box] > .default-menu {
index 59a285a..dae64d8 100755 (executable)
@@ -98,7 +98,7 @@ Editor.HTMLModel = Editor.Model.extend({
         return false;
     },
 
-    asWLML: function(element
+    asWLML: function(element, inner)
     {
         console.log("Source", element);
         var doc = this.parser.parseFromString(this.serializer.serializeToString(element), 'text/xml');
@@ -111,30 +111,81 @@ Editor.HTMLModel = Editor.Model.extend({
         }
         
         console.log("Transformed", doc, " to: ", result.documentElement);
-        return this.serializer.serializeToString(result.documentElement);
+        if(inner) {
+            var children = result.documentElement.childNodes;
+            var buf = '';
+            
+            for(var i=0; i < children.length; i++)
+                buf += this.serializer.serializeToString(children.item(i));
+            
+            return buf;
+         }
+          
+         return this.serializer.serializeToString(result.documentElement);
+    },
+
+    innerAsWLML: function(elem)
+    {
+        return this.asWLML(elem, true);
+    },
+
+    updateInnerWithWLML: function($element, innerML)
+    {
+        var e = $element.clone().html('<span x-node="out-of-flow-text" x-content="%"></span>')[0];
+        var s = this.asWLML(e);
+        // hurray for dirty hacks :P
+        s = s.replace(/>%<\//, '>'+innerML+'</');
+        return this.updateWithWLML($element, s);
     },
 
     updateWithWLML: function($element, text)
     {
         // filter the string
         text = text.replace(/\/\s+/g, '<br />');
-        var chunk = this.parser.parseFromString("<chunk>"+text+"</chunk>", "text/xml");
-
-        var errors = $('parsererror', chunk);
+        try {
+            var chunk = this.parser.parseFromString("<chunk>"+text+"</chunk>", "text/xml");
+        } catch(e) {
+            console.log('Caught parse exception.');
+            return "<p>Źle sformatowana zawartość:" + e.toString() + "</p>";
+        }
 
-        // check if chunk is parsable
-        if(errors.length > 0)
-            throw {text: errors.text(), html: errors.html()};
+        var parseError = chunk.getElementsByTagName('parsererror');
+        console.log("Errors:", parseError);
         
-        var result = this.htmlXSL.transformToFragment(chunk, document);
+        if(parseError.length > 0)
+        {
+            console.log("Parse errors.")
+            return this.serializer.serializeToString(parseError.item(0));
+        }
 
-        console.log("RESULT", this.serializer.serializeToString(result));
+        console.log("Transforming to HTML");        
+        var result = this.htmlXSL.transformToFragment(chunk, $element[0].ownerDocument).firstChild;
 
-        if(!result)
-            throw "WLML->HTML transformation failed.";
-        
-        $element.replaceWith(result);
-        this.set('state', 'dirty');
+        if(!result) {
+            return "Błąd aplikacji - nie udało się wygenerować nowego widoku HTML.";
+        }
+
+        var errors = result.getElementsByTagName('error');
+        if(errors.length > 0)
+        {
+            var errorMessage = 'Wystąpiły błędy:<ul>';
+            for(var i=0; i < errors.length; i++)
+            {
+                var estr = this.serializer.serializeToString(errors.item(i));
+                console.log("XFRM error:", estr);
+                errorMessage += "<li>"+estr+"</li>";
+            }
+            errorMessage += "</ul>";
+            return errorMessage;
+        }
+
+        try {
+            $element.replaceWith(result);
+            this.set('state', 'dirty');
+            return false;
+        } catch(e) {
+            return "Błąd podczas wstawiania tekstu: '" + e.toString() + "'";
+        }
     },
 
     createXSLT: function(xslt_doc) {
@@ -151,7 +202,9 @@ Editor.HTMLModel = Editor.Model.extend({
             if(this.wlmlXSL && this.htmlXSL && this.rawText)
                 this.loadSuccess();
         } catch(e) {
-            this.loadingFailed();
+            console.log(e);
+            this.set('error', e.toString() );
+            this.set('state', 'error');
         }
     },
 
@@ -163,7 +216,9 @@ Editor.HTMLModel = Editor.Model.extend({
             if(this.wlmlXSL && this.htmlXSL && this.rawText)
                 this.loadSuccess();
         } catch(e) {
-            this.loadingFailed();
+            console.log(e);
+            this.set('error', e.toString() );
+            this.set('state', 'error');
         }
     },
 
index 0b1234c..cc42be2 100755 (executable)
@@ -105,19 +105,18 @@ var HTMLView = View.extend({
         this.$printLink = $('.htmlview-toolbar .html-print-link', this.element);
         this.$docbase = $('.htmlview', this.element);
         this.$addThemeButton = $('.htmlview-toolbar .html-add-motive', this.element);
-
-        this.$debugButton = $('.htmlview-toolbar .html-serialize', this.element);
+        // this.$debugButton = $('.htmlview-toolbar .html-serialize', this.element);
 
         this.updatePrintLink();
         this.$docbase.bind('click', this.itemClicked.bind(this));
         this.$addThemeButton.click( this.addTheme.bind(this) );
-        this.$debugButton.click( this.serialized.bind(this) );
+        // this.$debugButton.click( this.serialized.bind(this) );
     },
 
-    serialized: function() {
+    /* serialized: function() {
         this.model.set('state', 'dirty');
         console.log( this.model.serializer.serializeToString(this.model.get('data')) );        
-    },
+    }, */
 
     renderPart: function($e, html) {
         // exceptions aren't good, but I don't have a better idea right now
@@ -186,15 +185,18 @@ var HTMLView = View.extend({
          *  - this greatly simplifies the whole click check
          */
 
-        if( $e.hasClass('theme-ref') )
+        if( $e.hasClass('motyw') )
         {
             console.log($e);
-            this.selectTheme($e.attr('x-theme-class'));
+            this.selectTheme($e.attr('theme-class'));
             return false;
         }
 
         /* other buttons */
         try {
+            if($e.hasClass('delete-button'))
+                this.deleteElement( this.editableFor($e) );
+
             if($e.hasClass('edit-button'))
                 this.openForEdit( this.editableFor($e) );
 
@@ -204,7 +206,7 @@ var HTMLView = View.extend({
             if($e.hasClass('reject-button'))
                 this.closeWithoutSave( this.editableFor($e) );
         } catch(e) {
-            messageCenter.addMessage('error', "wlsave", 'Błąd:' + e.text);
+            messageCenter.addMessage('error', "wlsave", 'Błąd:' + e.toString());
         }
         
         return false;
@@ -248,10 +250,16 @@ var HTMLView = View.extend({
     closeWithSave: function($e) {
         var $edit = $e.data('edit-overlay');
         var newText = $('textarea', $edit).val();
-
-        this.model.updateWithWLML($e, newText);
-        $edit.remove();
-        this.currentOpen = null;        
+        var errors = null;
+        
+        errors = this.model.updateInnerWithWLML($e, newText);
+        
+        if(errors)
+            messageCenter.addMessage('error', 'render', errors);
+        else {
+            $edit.remove();
+            this.currentOpen = null;
+        }
     },
 
     closeWithoutSave: function($e) {
@@ -305,6 +313,8 @@ var HTMLView = View.extend({
 
         // start edition on this node
         var $overlay = $('<div class="html-editarea"><textarea></textarea></div>');
+
+        h = Math.max(h, 2*parseInt($box.css('line-height')));
         
         $overlay.css({
             position: 'absolute',
@@ -315,7 +325,7 @@ var HTMLView = View.extend({
         });
         
         try {
-            $('textarea', $overlay).val( this.model.asWLML($origin[0]) );
+            $('textarea', $overlay).val( this.model.innerAsWLML($origin[0]) );
 
             if($origin.is(".annotation-inline-box"))
             {                
@@ -347,6 +357,62 @@ var HTMLView = View.extend({
                 
         return false;
     },
+    
+    deleteElement: function($editable)
+    {
+        var relatedThemes = $("*[x-node='begin'], *[x-node='end']", $editable);
+
+        var themeMarks = relatedThemes.map(function() {
+            return $(".motyw[theme-class='"+$(this).attr('theme-class')+"']");
+        });
+
+        if($editable.is("*.motyw"))
+        {
+            console.log($editable);
+            var selector = "[theme-class='"+$editable.attr('theme-class')+"']";
+            relatedThemes = relatedThemes.add("*[x-node='begin']"+selector+", *[x-node='end']"+selector);
+        }
+        
+        console.log(relatedThemes, themeMarks);
+
+        var del = confirm("Usunięcie elementu jest nieodwracalne.\n"
+        +" Czy na pewno chcesz usunąć ten element, wraz z zawartymi motywami ?\n");
+        
+        if(del) {
+            relatedThemes.remove();
+            themeMarks.remove();
+            $editable.remove();
+        }
+    },
+
+    // Theme related stuff
+    verifyThemeInsertPoint: function(node) {
+
+        if(node.nodeType == 3) { // Text Node
+            node = node.parentNode;
+        }
+
+        if(node.nodeType != 1) return false;
+
+        console.log('Selection point:', node);
+        
+        node = $(node);
+        var xtype = node.attr('x-node');
+
+        if(!xtype || (xtype.search(':') >= 0) ||
+                xtype == 'motyw' || xtype == 'begin' || xtype == 'end')
+            return false;
+
+        // this is hopefully redundant
+        //if(! node.is('*.utwor *') )
+        //    return false;
+        
+        // don't allow themes inside annotations
+        if( node.is('*[x-annotation-box] *') )
+            return false;
+
+        return true;
+    },
 
     addTheme: function() 
     {
@@ -355,19 +421,37 @@ var HTMLView = View.extend({
 
         console.log("Range count:", n);
         
-        if(n == 0)
+        if(n == 0) {
             window.alert("Nie zaznaczono żadnego obszaru");
+            return false;
+        }
 
         // for now allow only 1 range
-        if(n > 1)
+        if(n > 1) {
             window.alert("Zaznacz jeden obszar");
-
+            return false;
+        }
 
         // from this point, we will assume that the ranges are disjoint
         for(var i=0; i < n; i++) 
         {
             var range = selection.getRangeAt(i);
             console.log(i, range.startContainer, range.endContainer);
+
+            // verify if the start/end points make even sense -
+            // they must be inside a x-node (otherwise they will be discarded)
+            // and the x-node must be a main text
+            if(! this.verifyThemeInsertPoint(range.startContainer) ) {
+                window.alert("Motyw nie może się zaczynać w tym miejscu.");
+                return false;
+            }
+
+            if(! this.verifyThemeInsertPoint(range.endContainer) ) {
+                window.alert("Motyw nie może się kończyć w tym miejscu.");
+                return false;
+            }
+
+            
             var date = (new Date()).getTime();
             var random = Math.floor(4000000000*Math.random());
             var id = (''+date) + '-' + (''+random);
@@ -378,38 +462,52 @@ var HTMLView = View.extend({
             spoint.setStart(range.startContainer, range.startOffset);
             epoint.setStart(range.endContainer, range.endOffset);
 
-            // insert theme-ref
-            
-            var elem = $('<span x-editable="true" x-node="motyw" class="motyw">Nowy motyw</span>');
-            elem.attr('x-attr-qname-'+id, 'id');
-            elem.attr('x-attr-value-'+id, 'm'+id);
-            spoint.insertNode(elem[0]);
+            var mtag, btag, etag, errors;
+
+            // insert theme-ref            
+            mtag = $('<span></span>');
+            spoint.insertNode(mtag[0]);
+            errors = this.model.updateWithWLML(mtag, '<motyw id="m'+id+'">Nowy Motyw</motyw>');
+            if(errors) {
+                messageCenter.addMessage('error', null, 'Błąd przy dodawaniu motywu :' + errors);
+                return false;
+            }
 
             // insert theme-begin
-            elem = $('<span x-node="begin" class="begin"></span>');
-            elem.attr('x-attr-qname-'+id, 'id');
-            elem.attr('x-attr-value-'+id, 'b'+id);
-            spoint.insertNode(elem[0]);
+            btag = $('<span></span>');
+            spoint.insertNode(btag[0]);
+            errors = this.model.updateWithWLML(btag, '<begin id="b'+id+'" />');
+            if(errors) {
+                mtag.remove();
+                messageCenter.addMessage('error', null, 'Błąd przy dodawaniu motywu :' + errors);
+                return false;
+            }
+
             
-            elem = $('<span x-node="end" class="end"></span>');
-            elem.attr('x-attr-qname-'+id, 'id');
-            elem.attr('x-attr-value-'+id, 'e'+id);
-            epoint.insertNode(elem[0]);
+            etag = $('<span></span>');
+            epoint.insertNode(etag[0]);
+            result = this.model.updateWithWLML(etag, '<end id="e'+id+'" />');
+            if(errors) {
+                btag.remove();
+                mtag.remove();
+                messageCenter.addMessage('error', null, 'Błąd przy dodawaniu motywu :' + errors);
+                return false;
+            }
         }
 
-    //selection.removeAllRanges();
+        selection.removeAllRanges();
     },
 
     selectTheme: function(themeId)
     {
-        var selection = document.getSelection();
+        var selection = window.getSelection();
         
         // remove current selection
         selection.removeAllRanges();
 
         var range = document.createRange();
-        var s = $('#m'+themeId)[0];
-        var e = $('#e'+themeId)[0];
+        var s = $(".motyw[theme-class='"+themeId+"']")[0];
+        var e = $(".end[theme-class='"+themeId+"']")[0];
         console.log('Selecting range:', themeId, range, s, e);
 
         if(s && e) {
index 726a404..3ee2f9e 100755 (executable)
@@ -7,12 +7,11 @@
     xmlns:dc="http://purl.org/dc/elements/1.1/"\r
 >\r
 \r
-    <xsl:output method="xml" encoding="utf-8" indent="yes"/>\r
+    <xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" />\r
     <!--\r
         Ten dokument definiuję przekształcenie odwrotne do wl2html\r
     -->\r
 \r
-\r
     <xsl:template match="comment()"><xsl:copy /></xsl:template>\r
 \r
     <!-- libxslt has fuck-ed prorities -->\r
@@ -86,8 +85,8 @@
         <xsl:element name="{@X-NODE}" namespace="{@X-NS}"><xsl:apply-templates select="@*|node()" /></xsl:element>\r
         <xsl:if test="count(following-sibling::*[starts-with(@X-NODE, 'wers')]) > 0"><xsl:text>/&#x000a;</xsl:text></xsl:if>\r
     </xsl:template>\r
-\r
-    \r
-    \r
+        \r
     <xsl:template match="*" />\r
+        \r
+    \r
 </xsl:stylesheet>
\ No newline at end of file
index 234ea5c..72ac777 100755 (executable)
@@ -5,7 +5,7 @@
     <!--
         Dokument ten opisuje jednoznaczne przekształcenie WLML 0.1 -> XHTML.
     -->        
-    <xsl:output method="xml" encoding="utf-8" indent="no" />
+    <xsl:output method="xml" encoding="utf-8" indent="no" omit-xml-declaration="yes" />
 
     <xsl:template match="/">
         <xsl:apply-templates select="chunk|utwor" />
     <xsl:template match="begin">        
         <span>
             <xsl:call-template name="standard-attributes" />
+            <xsl:attribute name="theme-class">
+                <xsl:value-of select="substring-after(@id, 'b')" />
+            </xsl:attribute>
         </span>
     </xsl:template>
 
     <xsl:template match="motyw">
         <span x-editable="true">
             <xsl:call-template name="standard-attributes" />
+            <xsl:attribute name="theme-class">
+                <xsl:value-of select="substring-after(@id, 'm')" />
+            </xsl:attribute>
             <xsl:call-template name="context-menu" />
             <xsl:value-of select="." />
         </span>
 
     <xsl:template match="end">
         <span>
-            <xsl:call-template name="standard-attributes" />           
+            <xsl:call-template name="standard-attributes" />
+            <xsl:attribute name="theme-class">
+                <xsl:value-of select="substring-after(@id, 'e')" />
+            </xsl:attribute>
         </span>
     </xsl:template>
 
     </xsl:template>
 
     <xsl:template match="*">
-        <xsl:message terminate="no">
-        Unmatched tag <xsl:value-of select="name()" />
-        </xsl:message>
+        <error>Unknown tag: <xsl:value-of select="name()" /></error>
     </xsl:template>
 
     <xsl:template name="context-menu">
         <span class="default-menu context-menu">
             <span class="edit-button">Edit</span>
+            <span class="delete-button">Delete</span>
         </span>
         <span class="edit-menu context-menu">
-            <span class="accept-button">OK</span>
-            <span class="reject-button">Cancel</span>
+            <span class="accept-button">Accept</span>
+            <span class="reject-button">Close</span>
         </span>
     </xsl:template>
 
index 2e8a6b4..a3ee045 100755 (executable)
@@ -64,8 +64,7 @@
        <script type="text/html" charset="utf-8" id="html-view-template">
                 <div class="htmlview-toolbar">
                     <a class="html-print-link" href="print" ui:baseref="{% url file_print fileid %}" target="_new">{% trans "Print version"  %}</a>
-                    <button class="html-add-motive" type="button">Dodaj motyw</button>
-                    <button class="html-serialize" type="button">Serializuj</button>
+                    <button class="html-add-motive" type="button">Dodaj motyw</button>                    
                 </div>
                 
                <div class="htmlview">