X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/3193141f55df20910cf8ba35f9e669d79c90d3f4..8705c980a8a35af6c4fc6e309f389bc1aae0b6d7:/platforma/static/js/views/html.js diff --git a/platforma/static/js/views/html.js b/platforma/static/js/views/html.js old mode 100644 new mode 100755 index fa52bd0f..cefd0d2b --- a/platforma/static/js/views/html.js +++ b/platforma/static/js/views/html.js @@ -12,15 +12,40 @@ var HTMLView = View.extend({ this.model .addObserver(this, 'data', this.modelDataChanged.bind(this)) .addObserver(this, 'state', this.modelStateChanged.bind(this)); - - $('.htmlview', this.element).html(this.model.get('data')); + + this.$menuTemplate = $(render_template('html-view-frag-menu-template', this)); this.modelStateChanged('state', this.model.get('state')); + this.modelDataChanged('data', this.model.get('data')); + this.model.load(); + + this.currentOpen = null; + this.currentFocused = null; + this.themeBoxes = []; }, modelDataChanged: function(property, value) { $('.htmlview', this.element).html(value); this.updatePrintLink(); + var self = this; + + /* upgrade editable elements */ + $("*[x-editable]", this.$docbase).each(function() { + $(this).append( self.$menuTemplate.clone() ); + }); + + /* mark themes */ + /* $(".theme-ref", this.$docbase).each(function() { + var id = $(this).attr('x-theme-class'); + + var end = $("span.theme-end[x-theme-class = " + id+"]"); + var begin = $("span.theme-begin[x-theme-class = " + id+"]"); + + var h = $(this).outerHeight(); + + h = Math.max(h, end.offset().top - begin.offset().top); + $(this).css('height', h); + }); */ }, updatePrintLink: function() { @@ -35,6 +60,7 @@ var HTMLView = View.extend({ if (value == 'synced' || value == 'dirty') { this.unfreeze(); } else if (value == 'unsynced') { + if(this.currentOpen) this.closeWithoutSave(this.currentOpen); this.freeze('Niezsynchronizowany...'); } else if (value == 'loading') { this.freeze('Ładowanie...'); @@ -61,14 +87,46 @@ var HTMLView = View.extend({ }, render: function() { - this.element.unbind('click'); + if(this.$docbase) + this.$docbase.unbind('click'); + + if(this.$printLink) + this.$printLink.unbind(); + + if(this.$addThemeButton) + this.$addThemeButton.unbind(); - if(this.$printLink) this.$printLink.unbind(); this._super(); - this.$printLink = $('.html-print-link', this.element); + + this.$printLink = $('.htmlview-toolbar .html-print-link', this.element); + this.$docbase = $('.htmlview', this.element); + this.$addThemeButton = $('.htmlview-toolbar .html-add-motive', this.element); + this.updatePrintLink(); + this.$docbase.bind('click', this.itemClicked.bind(this)); + this.$addThemeButton.click( this.addTheme.bind(this) ); + }, - this.element.bind('click', this.itemClicked.bind(this)); + renderPart: function($e, html) { + // exceptions aren't good, but I don't have a better idea right now + if($e.attr('x-annotation-box')) { + // replace the whole annotation + var $p = $e.parent(); + $p.html(html); + var $box = $('*[x-annotation-box]', $p); + $box.append( this.$menuTemplate.clone() ); + + if(this.currentFocused && $p[0] == this.currentFocused[0]) + { + this.currentFocused = $p; + $box.css({'display': 'block'}); + } + + return; + } + + $e.html(html); + $e.append( this.$menuTemplate.clone() ); }, reload: function() { @@ -84,69 +142,232 @@ var HTMLView = View.extend({ { var self = this; - console.log('click:', event, event.ctrlKey, event.target); - var editableContent = null; + console.log('click:', event, event.ctrlKey, event.target); var $e = $(event.target); - var n = 0; + if($e.hasClass('annotation')) + { + if(this.currentOpen) return false; + + var $p = $e.parent(); + if(this.currentFocused) + { + console.log(this.currentFocused, $p); + if($p[0] == this.currentFocused[0]) { + console.log('unfocus of current'); + this.unfocusAnnotation(); + return false; + } + + console.log('switch unfocus'); + this.unfocusAnnotation(); + } - while( ($e[0] != this.element[0]) && !($e.attr('wl2o:editable')) - && n < 50) + this.focusAnnotation($p); + return false; + } + + /* + * Clicking outside of focused area doesn't unfocus by default + * - this greatly simplifies the whole click check + */ + + if( $e.hasClass('theme-ref') ) + { + console.log($e); + this.selectTheme($e.attr('x-theme-class')); + } + + /* other buttons */ + if($e.hasClass('edit-button')) + this.openForEdit( this.editableFor($e) ); + + if($e.hasClass('accept-button')) + this.closeWithSave( this.editableFor($e) ); + + if($e.hasClass('reject-button')) + this.closeWithoutSave( this.editableFor($e) ); + }, + + unfocusAnnotation: function() + { + if(!this.currentFocused) + { + console.log('Redundant unfocus'); + return false; + } + + if(this.currentOpen + && this.currentOpen.is("*[x-annotation-box]") + && this.currentOpen.parent()[0] == this.currentFocused[0]) + { + console.log("Can't unfocus open box"); + return false; + } + + var $box = $("*[x-annotation-box]", this.currentFocused); + $box.css({'display': 'none'}); + // this.currentFocused.removeAttr('x-focused'); + // this.currentFocused.hide(); + this.currentFocused = null; + }, + + focusAnnotation: function($e) { + this.currentFocused = $e; + var $box = $("*[x-annotation-box]", $e); + $box.css({'display': 'block'}); + + // $e.attr('x-focused', 'focused'); + }, + + closeWithSave: function($e) { + var $edit = $e.data('edit-overlay'); + var newText = $('textarea', $edit).val(); + + this.model.putXMLPart($e, newText, function($e, html) { + this.renderPart($e, html); + $edit.remove(); + $e.removeAttr('x-open'); + }.bind(this) ); + this.currentOpen = null; + }, + + closeWithoutSave: function($e) { + var $edit = $e.data('edit-overlay'); + $edit.remove(); + $e.removeAttr('x-open'); + this.currentOpen = null; + }, + + editableFor: function($button) + { + var $e = $button; + var n = 0; + + while( ($e[0] != this.element[0]) && !($e.attr('x-editable')) && n < 50) { // console.log($e, $e.parent(), this.element); $e = $e.parent(); n += 1; } - - if(!$e.attr('wl2o:editable')) - return true; - + + if(!$e.attr('x-editable')) + throw Exception("Click outside of editable") + + console.log("Trigger", $button, " yields editable: ", $e); + return $e; + }, + + openForEdit: function($origin) + { + if(this.currentOpen && this.currentOpen != $origin) { + this.closeWithSave(this.currentOpen); + } + + var x = $origin[0].offsetLeft; + var y = $origin[0].offsetTop; + var w = $origin.outerWidth(); + var h = $origin.innerHeight(); + + console.log("Editable:", $origin, " offsetParent:", $origin[0].offsetParent); + console.log("Dimensions: ", x, y, w , h); + // start edition on this node + var $overlay = $('
'); + $overlay.css({position: 'absolute', height: h, left: x, top: y, width: '95%'}); + $($origin[0].offsetParent).append($overlay); + $origin.data('edit-overlay', $overlay); - var $overlay = $( - '
\n\ -

\n\ - \n\ - \n\ -

\n\ - \n\ -
'); - - var x = $e[0].offsetLeft; - var y = $e[0].offsetTop; - var w = $e.outerWidth(); - var h = $e.innerHeight(); - $overlay.css({position: 'absolute', height: h, left: "5%", top: y, width: "90%"}); - $e.offsetParent().append($overlay); - - // load the original XML content - console.log($e, $e.offsetParent(), $overlay); - - $('.html-editarea-cancel-button', $overlay).click(function() { - $overlay.remove(); - }); + this.model.getXMLPart($origin, function(path, data) { + $('textarea', $overlay).val(data); + }); - $('.html-editarea-save-button', $overlay).click(function() { - $overlay.remove(); + if($origin.is("*[x-annotation-box]")) + { + var $b = $origin.parent(); + if(this.currentFocused) { + // if some other is focused + if($b[0] != this.currentFocused[0]) { + this.unfocusAnnotation(); + this.focusAnnotation($b); + } + // already focues + } + else { // nothing was focused + this.focusAnnotation($b); + } + } + else { // this item is not focusable + if(this.currentFocused) this.unfocusAnnotation(); + } - // put the part back to the model - self.model.putXMLPart($e, $('textarea', $overlay).val()); - }); + this.currentOpen = $origin; + $origin.attr('x-open', 'open'); + + return false; + }, - $('textarea', $overlay).focus(function() { - $overlay.css('z-index', 3000); - }).blur(function() { - $overlay.css('z-index', 2000); - }); + addTheme: function() + { + var selection = document.getSelection(); + var n = selection.rangeCount; + + if(n == 0) + window.alert("Nie zaznaczono żadnego obszaru"); - this.model.getXMLPart($e, function(path, data) { - $('textarea', $overlay).val(data); - }); + // for now allow only 1 range + if(n > 1) + window.alert("Zaznacz jeden obszar"); + + // 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); + var date = Date.now(); + var random = Math.floor(4000000000*Math.random()); + var id = (''+date) + '-' + (''+random); + + var ipoint = document.createRange(); + + // Firefox alters the later node when inserting, so + // insert from end + ipoint.setStart(range.endContainer, range.endOffset); + elem = $('')[0]; + ipoint.insertNode(elem); + + // insert theme-ref + ipoint.setStart(range.startContainer, range.startOffset); + var elem = $('Nowy motyw')[0]; + ipoint.insertNode(elem); + ipoint.setStartBefore(elem); + + // insert theme-begin + elem = $('')[0]; + ipoint.insertNode(elem); + } + + selection.removeAllRanges(); + }, + + selectTheme: function(themeId) + { + var selection = document.getSelection(); - return false; + // remove current selection + selection.removeAllRanges(); + + var range = document.createRange(); + var s = $('#m'+themeId)[0]; + var e = $('#e'+themeId)[0]; + console.log('Selecting range:', themeId, range, s, e); + + if(s && e) { + range.setStartAfter(s); + range.setEndBefore(e); + selection.addRange(range); + } } - }); // Register view