Merge i commit dzialaja.
authorŁukasz Rekucki <lrekucki@gmail.com>
Tue, 8 Sep 2009 10:34:39 +0000 (12:34 +0200)
committerŁukasz Rekucki <lrekucki@gmail.com>
Tue, 8 Sep 2009 10:34:39 +0000 (12:34 +0200)
Mozliwosc uaktualnienia swojej galezi dla danego pliku.
Poprawki zeby lepiej dzialalo na Ie

12 files changed:
apps/explorer/forms.py
apps/explorer/models.py
apps/explorer/views.py
fixtures/przyciski.xml
lib/hg.py
project/static/css/filelist.css
project/static/css/master.css
project/static/js/codemirror/select.js
project/static/js/editor.js
project/static/js/jquery.logging.js
project/templates/explorer/editor.html
project/urls.py

index 0e1ec86..da8a117 100644 (file)
@@ -55,8 +55,13 @@ class BookForm(forms.Form):
     content = forms.CharField(widget=forms.Textarea)
     commit_message = forms.CharField(required=False)
 
+class MergeForm(forms.Form):
+    message = forms.CharField(error_messages={'required': 'Please write a merge description.'})
+
 class BookUploadForm(forms.Form):
-    file = forms.FileField()
+    file = forms.FileField(label='Source OCR file')
+    bookname = forms.RegexField(regex='[\w-]+',  \
+        label='Publication name', help_text='Example: slowacki-beniowski')
 
 class ImageFoldersForm(forms.Form):
     folders = forms.ChoiceField(required=False)
index e47c26f..5fc31e7 100644 (file)
@@ -52,5 +52,3 @@ def get_images_from_folder(folder):
             in os.listdir(os.path.join(settings.MEDIA_ROOT, settings.IMAGE_DIR, folder))
             if not fn.startswith('.'))
 
-def user_branch(user):
-    return 'personal_'+user.username
index 82c9d39..19cc10b 100644 (file)
@@ -16,7 +16,11 @@ from toolbar import models as toolbar_models
 
 #
 # Some useful decorators
-#
+
+def file_branch(path, user=None):
+    return ('personal_'+user.username + '_' if user is not None else '') \
+        + 'file_' + path
+
 def with_repo(view):
     """Open a repository for this view"""
     def view_with_repo(request, *args, **kwargs):          
@@ -32,7 +36,7 @@ def ajax_login_required(view):
         if request.user.is_authenticated():
             return view(request, *args, **kwargs)
         # not authenticated
-        return HttpResponse( json.dumps({'result': 'access_denied'}) );
+        return HttpResponse( json.dumps({'result': 'access_denied', 'errors': ['Brak dostępu.']}) );
     return view_with_auth
 
 #
@@ -40,7 +44,8 @@ def ajax_login_required(view):
 #
 @with_repo
 def file_list(request, repo):
-    latest_default = repo.repo.branchtags()['default']
+    #
+    latest_default = repo.get_branch_tip('default')
     files = list( repo.repo[latest_default] )
     bookform = forms.BookUploadForm()
 
@@ -59,34 +64,29 @@ def file_upload(request, repo):
                 # prepare the data
                 f = request.FILES['file']
                 decoded = f.read().decode('utf-8')
-
+                path = form.cleaned_data['bookname']
+                
                 def upload_action():
-                    print 'Adding file: %s' % f.name
-                    repo._add_file(f.name, decoded.encode('utf-8') )
-                    repo._commit(
-                        message="File %s uploaded from platform by %s" %\
-                            (f.name, request.user.username), \
-                        user=request.user.username \
-                    )
-                    
-                    # end of upload
+                    repo._add_file(path ,decoded.encode('utf-8') )
+                    repo._commit(message="File %s uploaded by user %s" % \
+                        (path, request.user.username), user=request.user.username)
 
                 repo.in_branch(upload_action, 'default')
 
                 # if everything is ok, redirect to the editor
                 return HttpResponseRedirect( reverse('editor_view',
-                        kwargs={'path': f.name}) )
+                        kwargs={'path': path}) )
 
             except hg.RepositoryException, e:
                 other_errors.append(u'Błąd repozytorium: ' + unicode(e) )
-            except UnicodeDecodeError, e:
-                other_errors.append(u'Niepoprawne kodowanie pliku: ' + e.reason \
-                 + u'. Żądane kodowanie: ' + e.encoding)
+            #except UnicodeDecodeError, e:
+            #    other_errors.append(u'Niepoprawne kodowanie pliku: ' + e.reason \
+            #     + u'. Żądane kodowanie: ' + e.encoding)
         # invalid form
 
     # get
     form = forms.BookUploadForm()
-    return direct_to_template(request, 'explorer/file_upload.html',
+    return direct_to_template(request, 'explorer/file_upload.html',\
         extra_context = {'form' : form, 'other_errors': other_errors})
    
 #
@@ -118,10 +118,10 @@ def file_xml(request, repo, path):
                     warnings = [u'Niepoprawny dokument XML: ' + unicode(e.message)]
 
                 #  save to user's branch
-                repo.in_branch(save_action, models.user_branch(request.user) );
+                repo.in_branch(save_action, file_branch(path, request.user) );
             except UnicodeDecodeError, e:
                 errors = [u'Błąd kodowania danych przed zapisem: ' + unicode(e.message)]
-            except RepositoryException, e:
+            except hg.RepositoryException, e:
                 errors = [u'Błąd repozytorium: ' + unicode(e.message)]            
 
         if not errors:
@@ -131,9 +131,154 @@ def file_xml(request, repo, path):
             'errors': errors, 'warnings': warnings}) );
 
     form = forms.BookForm()
-    data = repo.get_file(path, models.user_branch(request.user))
+    data = repo.get_file(path, file_branch(path, request.user))
     form.fields['content'].initial = data
-    return HttpResponse( json.dumps({'result': 'ok', 'content': data}) ) 
+    return HttpResponse( json.dumps({'result': 'ok', 'content': data}) )
+
+@ajax_login_required
+@with_repo
+def file_update_local(request, path, repo):
+    result = None
+    errors = None
+    
+    wlock = repo.write_lock()
+    try:
+        tipA = repo.get_branch_tip('default')
+        tipB = repo.get_branch_tip( file_branch(path, request.user) )
+
+        nodeA = repo.getnode(tipA)
+        nodeB = repo.getnode(tipB)
+        
+        # do some wild checks - see file_commit() for more info
+        if (repo.common_ancestor(tipA, tipB) == nodeA) \
+        or (nodeB in nodeA.parents()):
+            result = 'nothing-to-do'
+        else:
+            # Case 2+
+            repo.merge_revisions(tipB, tipA, \
+                request.user.username, 'Personal branch update.')
+            result = 'done'
+    except hg.UncleanMerge, e:
+        errors = [e.message]
+        result = 'fatal-error'
+    except hg.RepositoryException, e:
+        errors = [e.message]
+        result = 'fatal-error'
+    finally:
+        wlock.release()
+
+    if result is None:
+        raise Exception("Ouch, this shouldn't happen!")
+    
+    return HttpResponse( json.dumps({'result': result, 'errors': errors}) );
+
+@ajax_login_required
+@with_repo
+def file_commit(request, path, repo):
+    result = None
+    errors = None
+    local_modified = False
+    if request.method == 'POST':
+        form = forms.MergeForm(request.POST)
+
+        if form.is_valid():           
+            wlock = repo.write_lock()
+            try:
+                tipA = repo.get_branch_tip('default')
+                tipB = repo.get_branch_tip( file_branch(path, request.user) )
+
+                nodeA = repo.getnode(tipA)
+                nodeB = repo.getnode(tipB)
+
+                print repr(nodeA), repr(nodeB), repo.common_ancestor(tipA, tipB), repo.common_ancestor(tipB, tipA)
+
+                if repo.common_ancestor(tipB, tipA) == nodeA:
+                    # Case 1:
+                    #         * tipB
+                    #         |
+                    #         * <- can also be here!
+                    #        /|
+                    #       / |
+                    # tipA *  *
+                    #      |  |
+                    # The local branch has been recently updated,
+                    # so we don't need to update yet again, but we need to
+                    # merge down to default branch, even if there was
+                    # no commit's since last update
+                    repo.merge_revisions(tipA, tipB, \
+                        request.user.username, form.cleaned_data['message'])
+                    result = 'done'
+                elif any( p.branch()==nodeB.branch() for p in nodeA.parents()):
+                    # Case 2:
+                    #
+                    # tipA *  * tipB
+                    #      |\ |
+                    #      | \|
+                    #      |  * 
+                    #      |  |
+                    # Default has no changes, to update from this branch
+                    # since the last merge of local to default.
+                    if nodeB not in nodeA.parents():
+                        repo.merge_revisions(tipA, tipB, \
+                            request.user.username, form.cleaned_data['message'])
+                        result = 'done'
+                    else:
+                        result = 'nothing-to-do'
+                elif repo.common_ancestor(tipA, tipB) == nodeB:
+                    # Case 3:
+                    # tipA * 
+                    #      |
+                    #      * <- this case overlaps with previos one
+                    #      |\
+                    #      | \
+                    #      |  * tipB
+                    #      |  |
+                    #
+                    # There was a recent merge to the defaul branch and
+                    # no changes to local branch recently.
+                    # 
+                    # Use the fact, that user is prepared to see changes, to
+                    # update his branch if there are any
+                    if nodeB not in nodeA.parents():
+                        repo.merge_revisions(tipB, tipA, \
+                            request.user.username, 'Personal branch update during merge.')
+                        local_modified = True
+                        result = 'done'
+                    else:
+                        result = 'nothing-to-do'
+                else:
+                    # both branches have changes made to them, so
+                    # first do an update
+                    repo.merge_revisions(tipB, tipA, \
+                        request.user.username, 'Personal branch update during merge.')
+
+                    local_modified = True
+
+                    # fetch the new tip
+                    tipB = repo.get_branch_tip( file_branch(path, request.user) )
+
+                    # and merge back to the default
+                    repo.merge_revisions(tipA, tipB, \
+                        request.user.username, form.cleaned_data['message'])
+                    result = 'done'
+            except hg.UncleanMerge, e:
+                errors = [e.message]
+                result = 'fatal-error'
+            except hg.RepositoryException, e:
+                errors = [e.message]
+                result = 'fatal-error'
+            finally:
+                wlock.release()
+                
+        if result is None:
+            errors = [ form.errors['message'].as_text() ]
+            if len(errors) > 0:
+                result = 'fatal-error'
+
+        return HttpResponse( json.dumps({'result': result, 'errors': errors, 'localmodified': local_modified}) );
+
+    return HttpResponse( json.dumps({'result': 'fatal-error', 'errors': ['No data posted']}) )
+    
 
 @ajax_login_required
 @with_repo
@@ -144,23 +289,24 @@ def file_dc(request, path, repo):
         form = forms.DublinCoreForm(request.POST)
         
         if form.is_valid():
+            
             def save_action():
                 file_contents = repo._get_file(path)
 
                 # wczytaj dokument z repozytorium
                 document = parser.WLDocument.from_string(file_contents)                    
-                document.book_info.update(form.cleaned_data)
-                
-                print "SAVING DC"
+                document.book_info.update(form.cleaned_data)             
 
                 # zapisz
-                repo._write_file(path, document.serialize())
+                repo._write_file(path, document.serialize().encode('utf-8'))
                 repo._commit( \
                     message=(form.cleaned_data['commit_message'] or 'Lokalny zapis platformy.'), \
                     user=request.user.username )
                 
             try:
-                repo.in_branch(save_action, models.user_branch(request.user) )
+                repo.in_branch(save_action, file_branch(path, request.user) )
+            except UnicodeEncodeError, e:
+                errors = ['Bład wewnętrzny: nie można zakodować pliku do utf-8']
             except (ParseError, ValidationError), e:
                 errors = [e.message]
 
@@ -173,7 +319,7 @@ def file_dc(request, path, repo):
     content = []
     
     try:
-        fulltext = repo.get_file(path, models.user_branch(request.user))
+        fulltext = repo.get_file(path, file_branch(path, request.user))
         bookinfo = dcparser.BookInfo.from_string(fulltext)
         content = bookinfo.to_dict()
     except (ParseError, ValidationError), e:
@@ -186,28 +332,25 @@ def file_dc(request, path, repo):
 
 @login_required
 @with_repo
-def display_editor(request, path, repo):
-    
-    if not repo.file_exists(path, models.user_branch(request.user)):
-        try:
-            data = repo.get_file(path, 'default')
-            print type(data)
-
-            def new_file():
-                repo._add_file(path, data)
-                repo._commit(message='File import from default branch',
-                    user=request.user.username)
-                
-            repo.in_branch(new_file, models.user_branch(request.user) )
-        except hg.RepositoryException, e:
-            return direct_to_template(request, 'explorer/file_unavailble.html',\
-                extra_context = { 'path': path, 'error': e })
-
-    return direct_to_template(request, 'explorer/editor.html', extra_context={
-        'hash': path,
-        'panel_list': ['lewy', 'prawy'],
-        'scriptlets': toolbar_models.Scriptlet.objects.all()
-    })
+def display_editor(request, path, repo):    
+
+    # this is the only entry point where we create an autobranch for the user
+    # if it doesn't exists. All other views SHOULD fail.
+    def ensure_branch_exists():
+        parent = repo.get_branch_tip('default')
+        repo._create_branch(file_branch(path, request.user), parent)
+        
+    try:
+        repo.with_wlock(ensure_branch_exists)
+        
+        return direct_to_template(request, 'explorer/editor.html', extra_context={
+            'hash': path,
+            'panel_list': ['lewy', 'prawy'],
+            'scriptlets': toolbar_models.Scriptlet.objects.all()
+        })
+    except KeyError:
+        return direct_to_template(request, 'explorer/nofile.html', \
+            extra_context = { 'path': path })
 
 # ===============
 # = Panel views =
@@ -216,7 +359,7 @@ def display_editor(request, path, repo):
 @ajax_login_required
 @with_repo
 def xmleditor_panel(request, path, repo):
-    text = repo.get_file(path, models.user_branch(request.user))
+    text = repo.get_file(path, file_branch(path, request.user))
     
     return direct_to_template(request, 'explorer/panels/xmleditor.html', extra_context={
         'fpath': path,
@@ -234,7 +377,7 @@ def gallery_panel(request, path):
 @ajax_login_required
 @with_repo
 def htmleditor_panel(request, path, repo):
-    user_branch = models.user_branch(request.user)
+    user_branch = file_branch(path, request.user)
     try:
         return direct_to_template(request, 'explorer/panels/htmleditor.html', extra_context={
             'fpath': path,
@@ -247,7 +390,7 @@ def htmleditor_panel(request, path, repo):
 @ajax_login_required
 @with_repo
 def dceditor_panel(request, path, repo):
-    user_branch = models.user_branch(request.user)
+    user_branch = file_branch(path, request.user)
 
     try:
         doc_text = repo.get_file(path, user_branch)
index 287a0cc..d67f3ed 100644 (file)
@@ -1,62 +1,98 @@
 <?xml version="1.0" encoding="utf-8"?>\r
-<django-objects version="1.0"><object pk="2" model="toolbar.buttongroup"><field type="CharField" name="name">Autokorekta</field><field type="SlugField" name="slug">autokorekta</field><field type="IntegerField" name="position">0</field></object><object pk="1" model="toolbar.buttongroup"><field type="CharField" name="name">Formatowanie</field><field type="SlugField" name="slug">formatowanie</field><field type="IntegerField" name="position">0</field></object><object pk="3" model="toolbar.buttongroup"><field type="CharField" name="name">Widok</field><field type="SlugField" name="slug">display_options</field><field type="IntegerField" name="position">2</field></object><object pk="5" model="toolbar.button"><field type="CharField" name="label">A&lt;sup&gt;+&lt;/sup&gt;</field><field type="SlugField" name="slug">increase_font_size</field><field type="TextField" name="params">({change: 2})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">codemirror_fontsize</field><field type="CharField" name="key">+</field><field type="CharField" name="tooltip">Zwiększ rozmiar czcionki.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="3"></object></field></object><object pk="6" model="toolbar.button"><field type="CharField" name="label">A&lt;sup&gt;-&lt;/sup&gt;</field><field type="SlugField" name="slug">descrease_font_size</field><field type="TextField" name="params">({change: -2})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">codemirror_fontsize</field><field type="CharField" name="key">-</field><field type="CharField" name="tooltip">Zmniejsz rozmiar czcionki.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="3"></object></field></object><object pk="7" model="toolbar.button"><field type="CharField" name="label">A&lt;sup&gt;=&lt;/sup&gt;</field><field type="SlugField" name="slug">reset_font_size</field><field type="TextField" name="params">({fontSize: 13})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">codemirror_fontsize</field><field type="CharField" name="key">=</field><field type="CharField" name="tooltip">Przywróć orginalny rozmiar czcionki.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="3"></object></field></object><object pk="4" model="toolbar.button"><field type="CharField" name="label">Novelpages</field><field type="SlugField" name="slug">novelpages</field><field type="TextField" name="params">({exprs: [\r\r
-  ["\\,\\.\\.|\\.\\,\\.|\\.\\.\\,", "..."],\r\r
-  ["„", ",,"] /* DOUBLE LOW-9 QUOTATION MARK */\r\r
-]})\r\r
-  </field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">lineregexp</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip">Wykonuję operację z novel-pages.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="2"></object></field></object><object pk="3" model="toolbar.button"><field type="CharField" name="label">Usuń spację</field><field type="SlugField" name="slug">strip_whitespace</field><field type="TextField" name="params">({exprs: [ ["^\\s+|\\s+$", ""], ["\\s+", " "] ]})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">lineregexp</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip">Usuwa zbędne spację z dokumentu.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="2"></object></field></object><object pk="1" model="toolbar.button"><field type="CharField" name="label">Wers</field><field type="SlugField" name="slug">insert_verse</field><field type="TextField" name="params">({tag: 'wers'})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key">w</field><field type="CharField" name="tooltip">Otacza zaznaczony tekst tagiem 'wers'.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="1"></object></field></object><object pk="2" model="toolbar.button"><field type="CharField" name="label">Zamień dywiz</field><field type="SlugField" name="slug">zamien_dywiz</field><field type="TextField" name="params">({exprs:[ ["—","---"] ]})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">lineregexp</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip">Zamienia '—' na '---'.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="2"></object></field></object><object pk="insert_tag" model="toolbar.scriptlet"><field type="TextField" name="code">$.log(editor, panel, params);\r\r
-\r\r
-var texteditor = panel.texteditor;\r\r
-var text = texteditor.selection();\r\r
-texteditor.replaceSelection('&lt;' + params.tag + '&gt;' + text + '&lt;/' + params.tag + '&gt;');\r\r
-if (text.length == 0) \r\r
-{\r\r
-    var pos = texteditor.cursorPosition();\r\r
-    texteditor.selectLines(pos.line, pos.character + params.tag.length + 2);\r\r
-}\r\r
-\r\r
-panel.fireEvent('contentChanged');</field></object><object pk="lineregexp" model="toolbar.scriptlet"><field type="TextField" name="code">// params: {exprs: list of {expr: "", repl: "" [, opts: "g"]}}\r\r
-var cm = panel.texteditor;\r\r
-\r\r
-var exprs = $.map(params.exprs, function(expr) {\r\r
-    var opts = "g";\r\r
-    if(expr.length &gt; 2)\r\r
-        opts = expr[2];\r\r
-    return {rx: new RegExp(expr[0], opts), repl: expr[1]};\r\r
-});\r\r
-\r\r
-var selection = cm.selection();\r\r
-\r\r
-if(selection) \r\r
-{\r\r
-    var lines = selection.split('\n');\r\r
-    lines = $.map(lines, function(line) { \r\r
-        $(exprs).each(function() { \r\r
-            var expr = this;\r\r
-            line = line.replace(expr.rx, expr.repl);\r\r
-        });\r\r
-        return line;\r\r
-    });\r\r
-    cm.replaceSelection( lines.join('\n') );\r\r
-}\r\r
-else {\r\r
-    var line = cm.firstLine();\r\r
-    do {\r\r
-        var content = cm.lineContent(line);\r\r
-        $.log("Swapping line: $" + content + "$");\r\r
-    \r\r
-        $(exprs).each(function() { var expr = this;\r\r
-            content = content.replace(expr.rx, expr.repl);\r\r
-        });\r\r
-        cm.setLineContent(line, content);\r\r
-        line = cm.nextLine(line);\r\r
-    } while( !(line === false) );\r\r
-}</field></object><object pk="codemirror_fontsize" model="toolbar.scriptlet"><field type="TextField" name="code">var texteditor = panel.texteditor;\r\r
-var frameBody = $('body', $(texteditor.frame).contents());\r\r
-\r\r
-if(params.fontSize) {\r\r
-    frameBody.css('font-size', params.fontSize);\r\r
-}\r\r
-else {\r\r
-    var old_size = parseInt(frameBody.css('font-size'));\r\r
-    frameBody.css('font-size', old_size + (params.change || 0) );\r\r
+<django-objects version="1.0"><object pk="14" model="toolbar.buttongroup"><field type="CharField" name="name">Akapity i długie cytaty</field><field type="SlugField" name="slug">akapity-i-dlugie-cytaty</field><field type="IntegerField" name="position">0</field></object><object pk="2" model="toolbar.buttongroup"><field type="CharField" name="name">Autokorekta</field><field type="SlugField" name="slug">autokorekta</field><field type="IntegerField" name="position">0</field></object><object pk="21" model="toolbar.buttongroup"><field type="CharField" name="name">Bloki</field><field type="SlugField" name="slug">bloki</field><field type="IntegerField" name="position">0</field></object><object pk="20" model="toolbar.buttongroup"><field type="CharField" name="name">Bloki początkowe</field><field type="SlugField" name="slug">bloki-poczatkowe</field><field type="IntegerField" name="position">0</field></object><object pk="19" model="toolbar.buttongroup"><field type="CharField" name="name">Deklaracje</field><field type="SlugField" name="slug">deklaracje</field><field type="IntegerField" name="position">0</field></object><object pk="12" model="toolbar.buttongroup"><field type="CharField" name="name">Dramat wierszowany</field><field type="SlugField" name="slug">dramat-wierszowany</field><field type="IntegerField" name="position">0</field></object><object pk="18" model="toolbar.buttongroup"><field type="CharField" name="name">Dramat współczesny</field><field type="SlugField" name="slug">dramat-wspolczesny</field><field type="IntegerField" name="position">0</field></object><object pk="13" model="toolbar.buttongroup"><field type="CharField" name="name">Elementy początkowe</field><field type="SlugField" name="slug">elementy-poczatkowe</field><field type="IntegerField" name="position">0</field></object><object pk="23" model="toolbar.buttongroup"><field type="CharField" name="name">Mastery</field><field type="SlugField" name="slug">mastery</field><field type="IntegerField" name="position">0</field></object><object pk="1" model="toolbar.buttongroup"><field type="CharField" name="name">Nagłówki</field><field type="SlugField" name="slug">naglowki</field><field type="IntegerField" name="position">0</field></object><object pk="22" model="toolbar.buttongroup"><field type="CharField" name="name">Początek dramatu</field><field type="SlugField" name="slug">poczatek-dramatu</field><field type="IntegerField" name="position">0</field></object><object pk="16" model="toolbar.buttongroup"><field type="CharField" name="name">Polecenia</field><field type="SlugField" name="slug">polecenia</field><field type="IntegerField" name="position">0</field></object><object pk="11" model="toolbar.buttongroup"><field type="CharField" name="name">Strukturalne</field><field type="SlugField" name="slug">strukturalne</field><field type="IntegerField" name="position">0</field></object><object pk="15" model="toolbar.buttongroup"><field type="CharField" name="name">Style znakowe</field><field type="SlugField" name="slug">style-znakowe</field><field type="IntegerField" name="position">0</field></object><object pk="17" model="toolbar.buttongroup"><field type="CharField" name="name">Wersy</field><field type="SlugField" name="slug">wersy</field><field type="IntegerField" name="position">0</field></object><object pk="3" model="toolbar.buttongroup"><field type="CharField" name="name">Widok</field><field type="SlugField" name="slug">display_options</field><field type="IntegerField" name="position">2</field></object><object pk="5" model="toolbar.button"><field type="CharField" name="label">A&lt;sup&gt;+&lt;/sup&gt;</field><field type="SlugField" name="slug">increase_font_size</field><field type="TextField" name="params">({change: 2})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">codemirror_fontsize</field><field type="CharField" name="key">+</field><field type="CharField" name="tooltip">Zwiększ rozmiar czcionki.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="3"></object></field></object><object pk="6" model="toolbar.button"><field type="CharField" name="label">A&lt;sup&gt;-&lt;/sup&gt;</field><field type="SlugField" name="slug">descrease_font_size</field><field type="TextField" name="params">({change: -2})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">codemirror_fontsize</field><field type="CharField" name="key">-</field><field type="CharField" name="tooltip">Zmniejsz rozmiar czcionki.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="3"></object></field></object><object pk="7" model="toolbar.button"><field type="CharField" name="label">A&lt;sup&gt;=&lt;/sup&gt;</field><field type="SlugField" name="slug">reset_font_size</field><field type="TextField" name="params">({fontSize: 13})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">codemirror_fontsize</field><field type="CharField" name="key">=</field><field type="CharField" name="tooltip">Przywróć orginalny rozmiar czcionki.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="3"></object></field></object><object pk="39" model="toolbar.button"><field type="CharField" name="label">akapit</field><field type="SlugField" name="slug">akapit</field><field type="TextField" name="params">({tag:"akap"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="14"></object></field></object><object pk="40" model="toolbar.button"><field type="CharField" name="label">akapit cd.</field><field type="SlugField" name="slug">akapit-cd</field><field type="TextField" name="params">({tag:"akap_cd"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="14"></object></field></object><object pk="41" model="toolbar.button"><field type="CharField" name="label">akapit dialogowy</field><field type="SlugField" name="slug">akapit-dialogowy</field><field type="TextField" name="params">({tag:"akap_dialog"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="14"></object></field></object><object pk="14" model="toolbar.button"><field type="CharField" name="label">akt</field><field type="SlugField" name="slug">akt</field><field type="TextField" name="params">({tag:"akt"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="1"></object></field></object><object pk="32" model="toolbar.button"><field type="CharField" name="label">autor</field><field type="SlugField" name="slug">autor</field><field type="TextField" name="params">({tag:"autor"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="13"></object></field></object><object pk="10" model="toolbar.button"><field type="CharField" name="label">część/księga</field><field type="SlugField" name="slug">czesc</field><field type="TextField" name="params">({tag:"naglowek_czesc"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="1"></object></field></object><object pk="65" model="toolbar.button"><field type="CharField" name="label">dedykacja</field><field type="SlugField" name="slug">dedykacja</field><field type="TextField" name="params">({tag:"dedykacja"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="13"></object></field></object><object pk="74" model="toolbar.button"><field type="CharField" name="label">dedykacja</field><field type="SlugField" name="slug">dedykacja</field><field type="TextField" name="params">({tag:"dedykacja"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="22"></object></field></object><object pk="31" model="toolbar.button"><field type="CharField" name="label">didaskalia</field><field type="SlugField" name="slug">didaskalia</field><field type="TextField" name="params">({tag:"didaskalia"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="12"></object></field></object><object pk="62" model="toolbar.button"><field type="CharField" name="label">didaskalia</field><field type="SlugField" name="slug">didaskalia</field><field type="TextField" name="params">({tag:"didaskalia"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="18"></object></field></object><object pk="49" model="toolbar.button"><field type="CharField" name="label">didaskalia wewn.</field><field type="SlugField" name="slug">didaskalia-wewn</field><field type="TextField" name="params">({tag:"didask_tekst"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="15"></object></field></object><object pk="63" model="toolbar.button"><field type="CharField" name="label">didaskalia wewn.</field><field type="SlugField" name="slug">didaskalia-wewn</field><field type="TextField" name="params">({tag:"didask_tekst"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="18"></object></field></object><object pk="20" model="toolbar.button"><field type="CharField" name="label">dramat wiersz.</field><field type="SlugField" name="slug">dramat-wiersz</field><field type="TextField" name="params">({tag:"dramat_wierszowany_l"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="11"></object></field></object><object pk="22" model="toolbar.button"><field type="CharField" name="label">dramat wiersz./w. łam</field><field type="SlugField" name="slug">dramat-wiersz-w-lam</field><field type="TextField" name="params">({tag:"dramat_wierszowany_lp"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="11"></object></field></object><object pk="21" model="toolbar.button"><field type="CharField" name="label">dramat współczesny</field><field type="SlugField" name="slug">dramat-wspolczesny</field><field type="TextField" name="params">({tag:"dramat_wspolczesny"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="11"></object></field></object><object pk="38" model="toolbar.button"><field type="CharField" name="label">dzieło nadrzędne</field><field type="SlugField" name="slug">dzielo-nadrzedne</field><field type="TextField" name="params">({tag:"dzielo_nadrzedne"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="13"></object></field></object><object pk="67" model="toolbar.button"><field type="CharField" name="label">długi cyt. poet.</field><field type="SlugField" name="slug">dlugi-cyt-poet</field><field type="TextField" name="params">({tag:"poezja_cyt"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="21"></object></field></object><object pk="43" model="toolbar.button"><field type="CharField" name="label">długi cyta. poet.</field><field type="SlugField" name="slug">dlugi-cyt-poet</field><field type="TextField" name="params">({tag:"poezja_cyt"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="14"></object></field></object><object pk="42" model="toolbar.button"><field type="CharField" name="label">długi cytat</field><field type="SlugField" name="slug">dlugi-cytat</field><field type="TextField" name="params">({tag:"dlugi_cyt"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="14"></object></field></object><object pk="66" model="toolbar.button"><field type="CharField" name="label">długi cytat</field><field type="SlugField" name="slug">dlugi-cytat</field><field type="TextField" name="params">dlugi_cytat</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="21"></object></field></object><object pk="52" model="toolbar.button"><field type="CharField" name="label">ekstra</field><field type="SlugField" name="slug">ekstra</field><field type="TextField" name="params">({tag:"ekstra"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="15"></object></field></object><object pk="29" model="toolbar.button"><field type="CharField" name="label">kwestia</field><field type="SlugField" name="slug">kwestia</field><field type="TextField" name="params">({tag:"kwestia"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="12"></object></field></object><object pk="61" model="toolbar.button"><field type="CharField" name="label">kwestia</field><field type="SlugField" name="slug">kwestia</field><field type="TextField" name="params">({tag:"kwestia"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="18"></object></field></object><object pk="23" model="toolbar.button"><field type="CharField" name="label">liryka</field><field type="SlugField" name="slug">liryka</field><field type="TextField" name="params">({tag:"liryka_l"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="11"></object></field></object><object pk="24" model="toolbar.button"><field type="CharField" name="label">liryka/w. łam</field><field type="SlugField" name="slug">liryka-w-lam</field><field type="TextField" name="params">({tag:"liryka_lp"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="11"></object></field></object><object pk="47" model="toolbar.button"><field type="CharField" name="label">mamtemat.</field><field type="SlugField" name="slug">matemat</field><field type="TextField" name="params">({tag:"mat"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="15"></object></field></object><object pk="36" model="toolbar.button"><field type="CharField" name="label">motto</field><field type="SlugField" name="slug">motto</field><field type="TextField" name="params">({tag:"motto"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="13"></object></field></object><object pk="75" model="toolbar.button"><field type="CharField" name="label">motto</field><field type="SlugField" name="slug">motto</field><field type="TextField" name="params">({tag:"motto"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="22"></object></field></object><object pk="37" model="toolbar.button"><field type="CharField" name="label">motto podpis</field><field type="SlugField" name="slug">motto-podpis</field><field type="TextField" name="params">({tag:"motto_podpis"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="13"></object></field></object><object pk="16" model="toolbar.button"><field type="CharField" name="label">nagłówek kwestii</field><field type="SlugField" name="slug">naglowek-kwestii</field><field type="TextField" name="params">({tag:"naglowek_osoba"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="1"></object></field></object><object pk="35" model="toolbar.button"><field type="CharField" name="label">nota</field><field type="SlugField" name="slug">nota</field><field type="TextField" name="params">({tag:"nota"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="13"></object></field></object><object pk="4" model="toolbar.button"><field type="CharField" name="label">Novelpages</field><field type="SlugField" name="slug">novelpages</field><field type="TextField" name="params">({exprs: [\r
+\r
+  ["\\,\\.\\.|\\.\\,\\.|\\.\\.\\,", "..."],\r
+\r
+  ["„", ",,"] /* DOUBLE LOW-9 QUOTATION MARK */\r
+\r
+]})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">lineregexp</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip">Wykonuję operację z novel-pages.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="2"></object></field></object><object pk="18" model="toolbar.button"><field type="CharField" name="label">opowiadanie</field><field type="SlugField" name="slug">opowiadanie</field><field type="TextField" name="params">({tag:"opowiadanie"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="11"></object></field></object><object pk="50" model="toolbar.button"><field type="CharField" name="label">osoba</field><field type="SlugField" name="slug">osoba</field><field type="TextField" name="params">({tag:"osoba"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="15"></object></field></object><object pk="64" model="toolbar.button"><field type="CharField" name="label">osoba</field><field type="SlugField" name="slug">osoba</field><field type="TextField" name="params">({tag:"osoba"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="18"></object></field></object><object pk="12" model="toolbar.button"><field type="CharField" name="label">podrozdział</field><field type="SlugField" name="slug">podrozdzial</field><field type="TextField" name="params">({tag:"naglowek_podrozdzial"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="1"></object></field></object><object pk="34" model="toolbar.button"><field type="CharField" name="label">podtytuł</field><field type="SlugField" name="slug">podtytul</field><field type="TextField" name="params">({tag:"podtytul"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="13"></object></field></object><object pk="19" model="toolbar.button"><field type="CharField" name="label">powieść</field><field type="SlugField" name="slug">powiesc</field><field type="TextField" name="params">({tag:"powiesc"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="11"></object></field></object><object pk="68" model="toolbar.button"><field type="CharField" name="label">przypis autorski</field><field type="SlugField" name="slug">przypis-autorski</field><field type="TextField" name="params">({tag:"pa"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="21"></object></field></object><object pk="71" model="toolbar.button"><field type="CharField" name="label">przypis edytorski</field><field type="SlugField" name="slug">przypis-edytorski</field><field type="TextField" name="params">({tag:"pe"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="21"></object></field></object><object pk="70" model="toolbar.button"><field type="CharField" name="label">przypis redaktorski</field><field type="SlugField" name="slug">przypis-redaktorski</field><field type="TextField" name="params">({tag:"pr"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="21"></object></field></object><object pk="69" model="toolbar.button"><field type="CharField" name="label">przypis tłumacza</field><field type="SlugField" name="slug">przypis-tlumacza</field><field type="TextField" name="params">({tag:"pt"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="21"></object></field></object><object pk="11" model="toolbar.button"><field type="CharField" name="label">rozdział</field><field type="SlugField" name="slug">rozdzial</field><field type="TextField" name="params">({tag:"naglowek_rozdzial"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="1"></object></field></object><object pk="15" model="toolbar.button"><field type="CharField" name="label">scena</field><field type="SlugField" name="slug">scena</field><field type="TextField" name="params">({tag:"naglowek_scena"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="1"></object></field></object><object pk="54" model="toolbar.button"><field type="CharField" name="label">sep. asteryks</field><field type="SlugField" name="slug">sep-asteryks</field><field type="TextField" name="params">({tag:"sekcja_asteryks"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="16"></object></field></object><object pk="55" model="toolbar.button"><field type="CharField" name="label">sep. linia</field><field type="SlugField" name="slug">sep-linia</field><field type="TextField" name="params">({tag:"separator_linia"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="16"></object></field></object><object pk="53" model="toolbar.button"><field type="CharField" name="label">sep. światło</field><field type="SlugField" name="slug">sep-swiatlo</field><field type="TextField" name="params">({tag:"sekcja_swiatlo"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="16"></object></field></object><object pk="13" model="toolbar.button"><field type="CharField" name="label">śródtytuł</field><field type="SlugField" name="slug">srodtytul</field><field type="TextField" name="params">({tag:"srodtytul"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="1"></object></field></object><object pk="30" model="toolbar.button"><field type="CharField" name="label">strofa</field><field type="SlugField" name="slug">strofa</field><field type="TextField" name="params">({tag"strofa"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="12"></object></field></object><object pk="57" model="toolbar.button"><field type="CharField" name="label">strofa</field><field type="SlugField" name="slug">strofa</field><field type="TextField" name="params">({tag:"strofa"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="17"></object></field></object><object pk="46" model="toolbar.button"><field type="CharField" name="label">słowo obce</field><field type="SlugField" name="slug">slowo-obce</field><field type="TextField" name="params">({tag:"slowo_obce"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="15"></object></field></object><object pk="17" model="toolbar.button"><field type="CharField" name="label">tagi główne</field><field type="SlugField" name="slug">tagi-glowne</field><field type="TextField" name="params">({tag:"utwor"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="11"></object></field></object><object pk="33" model="toolbar.button"><field type="CharField" name="label">tytuł</field><field type="SlugField" name="slug">tytul</field><field type="TextField" name="params">({tag:"nazwa_utworu"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="13"></object></field></object><object pk="45" model="toolbar.button"><field type="CharField" name="label">tytuł dzieła</field><field type="SlugField" name="slug">tytul-dziela</field><field type="TextField" name="params">({tag:"tytul_dziela"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="15"></object></field></object><object pk="3" model="toolbar.button"><field type="CharField" name="label">Usuń spację</field><field type="SlugField" name="slug">strip_whitespace</field><field type="TextField" name="params">({exprs: [ ["^\\s+|\\s+$", ""], ["\\s+", " "] ]})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">lineregexp</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip">Usuwa zbędne spację z dokumentu.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="2"></object></field></object><object pk="51" model="toolbar.button"><field type="CharField" name="label">uwaga</field><field type="SlugField" name="slug">uwaga</field><field type="TextField" name="params">({tag:"uwaga"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="15"></object></field></object><object pk="28" model="toolbar.button"><field type="CharField" name="label">wers akap.</field><field type="SlugField" name="slug">wers-akap</field><field type="TextField" name="params">({tag:"wers_akap"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="12"></object></field></object><object pk="58" model="toolbar.button"><field type="CharField" name="label">wers akap.</field><field type="SlugField" name="slug">wers-akap</field><field type="TextField" name="params">({tag:"wers_akap"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="17"></object></field></object><object pk="26" model="toolbar.button"><field type="CharField" name="label">wers cd.</field><field type="SlugField" name="slug">wers-cd</field><field type="TextField" name="params">({tag:"wers_cd"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="12"></object></field></object><object pk="60" model="toolbar.button"><field type="CharField" name="label">wers cd.</field><field type="SlugField" name="slug">wers-cd</field><field type="TextField" name="params">({tag:"wers_cd"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="17"></object></field></object><object pk="27" model="toolbar.button"><field type="CharField" name="label">wers wcięty</field><field type="SlugField" name="slug">wers-wciety</field><field type="TextField" name="params">({tag:"wers_wciety"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="12"></object></field></object><object pk="59" model="toolbar.button"><field type="CharField" name="label">wers wcięty</field><field type="SlugField" name="slug">wers-wciety</field><field type="TextField" name="params">({tag:"wers_wciety"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="17"></object></field></object><object pk="48" model="toolbar.button"><field type="CharField" name="label">www</field><field type="SlugField" name="slug">www</field><field type="TextField" name="params">({tag:"www"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="15"></object></field></object><object pk="44" model="toolbar.button"><field type="CharField" name="label">wyróżnienie</field><field type="SlugField" name="slug">wyroznienie</field><field type="TextField" name="params">({tag:"wyroznienie"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="15"></object></field></object><object pk="25" model="toolbar.button"><field type="CharField" name="label">wywiad</field><field type="SlugField" name="slug">wywiad</field><field type="TextField" name="params">({tag:"wywiad"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="11"></object></field></object><object pk="73" model="toolbar.button"><field type="CharField" name="label">wywiad odpowiedź</field><field type="SlugField" name="slug">wywiad-odpowiedz</field><field type="TextField" name="params">({tag:"wywiad_odp"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="21"></object></field></object><object pk="72" model="toolbar.button"><field type="CharField" name="label">wywiad pytanie</field><field type="SlugField" name="slug">wywiad-pytanie</field><field type="TextField" name="params">({tag:"wywiad_pyt"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="21"></object></field></object><object pk="2" model="toolbar.button"><field type="CharField" name="label">Zamień dywiz</field><field type="SlugField" name="slug">zamien_dywiz</field><field type="TextField" name="params">({exprs:[ ["—","---"] ]})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">lineregexp</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip">Zamienia '—' na '---'.</field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="2"></object></field></object><object pk="56" model="toolbar.button"><field type="CharField" name="label">zastępnik wersu</field><field type="SlugField" name="slug">zastepnik-wersu</field><field type="TextField" name="params">({tag:"zastepnik_wersu"})</field><field to="toolbar.scriptlet" name="scriptlet" rel="ManyToOneRel">insert_tag</field><field type="CharField" name="key"></field><field type="CharField" name="tooltip"></field><field to="toolbar.buttongroup" name="group" rel="ManyToManyRel"><object pk="16"></object></field></object><object pk="insert_tag" model="toolbar.scriptlet"><field type="TextField" name="code">$.log(editor, panel, params);\r
+\r
+\r
+\r
+var texteditor = panel.texteditor;\r
+\r
+var text = texteditor.selection();\r
+\r
+texteditor.replaceSelection('&lt;' + params.tag + '&gt;' + text + '&lt;/' + params.tag + '&gt;');\r
+\r
+if (text.length == 0) \r
+\r
+{\r
+\r
+    var pos = texteditor.cursorPosition();\r
+\r
+    texteditor.selectLines(pos.line, pos.character + params.tag.length + 2);\r
+\r
+}\r
+\r
+\r
+\r
+panel.fireEvent('contentChanged');</field></object><object pk="lineregexp" model="toolbar.scriptlet"><field type="TextField" name="code">// params: {exprs: list of {expr: "", repl: "" [, opts: "g"]}}\r
+var cm = panel.texteditor;\r
+\r
+var exprs = $.map(params.exprs, function(expr) {\r
+    var opts = "g";\r
+    if(expr.length &gt; 2)\r
+        opts = expr[2];\r
+    return {rx: new RegExp(expr[0], opts), repl: expr[1]};\r
+});\r
+\r
+var selection = cm.selection();\r
+\r
+if(selection) \r
+{\r
+    var changed = false;\r
+    var lines = selection.split('\n');\r
+    var lines = $.map(lines, function(line) { \r
+        var old_line = line;\r
+        $(exprs).each(function() { \r
+            var expr = this;\r
+            line = line.replace(expr.rx, expr.repl);\r
+        });\r
+        if(old_line != line) changed = true;\r
+        return line;\r
+    });\r
+\r
+    if(changed) {\r
+        cm.replaceSelection( lines.join('\n') );\r
+        panel.fireEvent('contentChanged');\r
+    }\r
+}\r
+else {\r
+    var line = cm.firstLine();\r
+    var hasChanges = false;\r
+    do {\r
+        var content = cm.lineContent(line);    \r
+        var old_content = content;\r
+        $(exprs).each(function() { var expr = this;\r
+            content = content.replace(expr.rx, expr.repl);\r
+        });\r
+        \r
+        if(old_content != content) {\r
+            cm.setLineContent(line, content);\r
+            hasChanges = true;\r
+        }\r
+\r
+        line = cm.nextLine(line);\r
+    } while( !(line === false) );\r
+\r
+    if(hasChanges) panel.fireEvent('contentChanged');\r
+}</field></object><object pk="codemirror_fontsize" model="toolbar.scriptlet"><field type="TextField" name="code">var texteditor = panel.texteditor;\r
+\r
+var frameBody = $('body', $(texteditor.frame).contents());\r
+\r
+\r
+\r
+if(params.fontSize) {\r
+\r
+    frameBody.css('font-size', params.fontSize);\r
+\r
+}\r
+\r
+else {\r
+\r
+    var old_size = parseInt(frameBody.css('font-size'));\r
+\r
+    frameBody.css('font-size', old_size + (params.change || 0) );\r
+\r
 }</field></object></django-objects>\r
index 07ec9a7..06e9f83 100644 (file)
--- a/lib/hg.py
+++ b/lib/hg.py
@@ -5,9 +5,13 @@ import mercurial.merge, mercurial.error
 
 encoding.encoding = 'utf-8'
 
+X = 'g\xc5\xbceg\xc5\xbc\xc3\xb3\xc5\x82ka'
 
-def clearpath(path):
-    return unicode(path).encode("utf-8")
+def sanitize_string(path):
+    if isinstance(path, unicode): #
+        return path.encode('utf-8')
+    else: # it's a string, so we have no idea what encoding it is
+        return path
 
 class Repository(object):
     """Abstrakcja repozytorium Mercurial. Działa z Mercurial w wersji 1.3.1."""
@@ -17,11 +21,10 @@ class Repository(object):
         self.ui.config('ui', 'quiet', 'true')
         self.ui.config('ui', 'interactive', 'false')
         
-        self.real_path = os.path.realpath(path)
-        self.repo = self.open_repository(self.real_path, create)
-        self._pending_files = []
-    
-    def open_repository(self, path, create=False):
+        self.real_path = sanitize_string(os.path.realpath(path))
+        self.repo = self._open_repository(self.real_path, create)
+
+    def _open_repository(self, path, create=False):
         if os.path.isdir(path):
             try:
                 return localrepo.localrepository(self.ui, path)
@@ -44,8 +47,7 @@ class Repository(object):
         return self.in_branch(lambda: self._get_file(path), branch)
 
     def _get_file(self, path):
-        path = clearpath(path)
-
+        path = sanitize_string(path)
         if not self._file_exists(path):
             raise RepositoryException("File not availble in this branch.")
         
@@ -55,26 +57,26 @@ class Repository(object):
         return self.in_branch(lambda: self._file_exists(path), branch)
 
     def _file_exists(self, path):
-        path = clearpath(path)
+        path = sanitize_string(path)
         return self.repo.dirstate[path] != "?"
 
     def write_file(self, path, value, branch):
         return self.in_branch(lambda: self._write_file(path, value), branch)
 
     def _write_file(self, path, value):
-        path = clearpath(path)
+        path = sanitize_string(path)
         return self.repo.wwrite(path, value, [])
 
     def add_file(self, path, value, branch):
         return self.in_branch(lambda: self._add_file(path, value), branch)
 
     def _add_file(self, path, value):
-        path = clearpath(path)
+        path = sanitize_string(path)
         self._write_file(path, value)
         return self.repo.add( [path] )
 
     def _commit(self, message, user=None):
-        return self.repo.commit(text=message, user=user)
+        return self.repo.commit(text=sanitize_string(message), user=sanitize_string(user))
     
     def commit(self, message, branch, user=None):
         return self.in_branch(lambda: self._commit(message, key=key, user=user), branch)
@@ -91,49 +93,143 @@ class Repository(object):
         finally:
             wlock.release()
 
-    def _switch_to_branch(self, bname, create=True):
+    def merge_branches(self, bnameA, bnameB, user, message):
         wlock = self.repo.wlock()
         try:
-            current = self.repo[None].branch()
-            if current == bname:
-                return current
-            try:
-                tip = self.repo.branchtags()[bname]
-            except KeyError, ke:
-                if not create: raise ke
-                
-                # create the branch on the fly
+            return self.merge_revisions(self.get_branch_tip(bnameA),
+                self.get_branch_tip(bnameB), user, message)
+        finally:
+            wlock.release()
 
-                # first switch to default branch
-                default_tip = self.repo.branchtags()['default']
-                mercurial.merge.update(self.repo, default_tip, False, True, None)
+    def diff(self, revA, revB):
+        return UpdateStatus(self.repo.status(revA, revB))
 
-                # set the dirstate to new branch
-                self.repo.dirstate.setbranch(bname)
-                self._commit('Initial commit for automatic branch "%s".' % bname, user="django-admin")
+    def merge_revisions(self, revA, revB, user, message):
+        wlock = self.repo.wlock()
+        try:
+            old = self.repo[None]
+            
+            self._checkout(revA)
+            mergestatus = self._merge(revB)
+            if not mergestatus.isclean():
+                # revert the failed merge
+                self.repo.recover()
+                raise UncleanMerge(u'Failed to merge %d files.' % len(mergestatus.unresolved))
+
+            # commit the clean merge
+            self._commit(message, user)
+
+            # cleanup after yourself
+            self._checkout(old.rev())
+        except util.Abort, ae:
+            raise RepositoryException(u'Failed merge: ' + ae.message)
+        finally:
+            wlock.release()
 
-                # collect the new tip
-                tip = self.repo.branchtags()[bname]
+    def common_ancestor(self, revA, revB):
+        return self.repo[revA].ancestor(self.repo[revB])
+        
+    def _checkout(self, rev, force=True):
+        return MergeStatus(mercurial.merge.update(self.repo, rev, False, force, None))
+        
+    def _merge(self, rev):
+        """ Merge the revision into current working directory """
+        return MergeStatus(mercurial.merge.update(self.repo, rev, True, False, None))
 
-            upstats = mercurial.merge.update(self.repo, tip, False, True, None)
-            return current
+    def _switch_to_branch(self, bname):
+        bname = sanitize_string(bname)
+        wlock = self.repo.wlock()
+        try:
+            current = self.repo[None].branch()
+            if current == bname:
+                return current
+            
+            tip = self.get_branch_tip(bname)
+            status = self._checkout(tip)
+
+            if not status.isclean():
+                raise RepositoryException("Unclean branch switch. This IS REALLY bad.")
+            
+            return current 
         except KeyError, ke:
-            raise RepositoryException("Can't switch to branch '%s': no such branch." % bname , ke)
+            raise RepositoryException((u"Can't switch to branch '%s': no such branch." % bname) , ke)
         except util.Abort, ae:
-            raise RepositoryException("Can't switch to branch '%s': %s"  % (bname, ae.message), ae)
+            raise RepositoryException(u"Can't switch to branch '%s': %s"  % (bname, ae.message), ae)
         finally:
             wlock.release()
 
+    def with_wlock(self, action):
+        wlock = self.repo.wlock()
+        try:
+            action()
+        finally:
+            wlock.release()
+
+    def _create_branch(self, name, parent_rev, msg=None, before_commit=None):
+        """WARNING: leaves the working directory in the new branch"""
+        name = sanitize_string(name)
+        
+        if self.has_branch(name): return # just exit
+
+        self._checkout(parent_rev)
+        self.repo.dirstate.setbranch(name)
+        
+        if msg is None:
+            msg = "Initial commit for branch '%s'." % name
+
+        if before_commit: before_commit()        
+        self._commit(msg, user='platform')
+        return self.get_branch_tip(name)
+
     def write_lock(self):
         """Returns w write lock to the repository."""
         return self.repo.wlock()
 
+    def has_branch(self, name):
+        name = sanitize_string(name)
+        return (name in self.repo.branchmap().keys())
+    
+    def get_branch_tip(self, name):
+        name = sanitize_string(name)
+        return self.repo.branchtags()[name]
 
-class RepositoryException(Exception):
+    def getnode(self, rev):
+        return self.repo[rev]
 
+class MergeStatus(object):
+
+    def __init__(self, mstatus):       
+        self.updated = mstatus[0]
+        self.merged = mstatus[1]
+        self.removed = mstatus[2]
+        self.unresolved = mstatus[3]
+
+    def isclean(self):
+        return self.unresolved == 0
+
+class UpdateStatus(object):
+
+    def __init__(self, mstatus):
+        self.modified = mstatus[0]
+        self.added = mstatus[1]
+        self.removed = mstatus[2]
+        self.deleted = mstatus[3]
+        self.untracked = mstatus[4]
+        self.ignored = mstatus[5]
+        self.clean = mstatus[6]
+
+    def has_changes(self):
+        return bool( len(self.modified) + len(self.added) + \
+            len(self.removed) + len(self.deleted) )
+
+class RepositoryException(Exception):
     def __init__(self, msg, cause=None):
         Exception.__init__(self, msg)
         self.cause = cause
 
+class UncleanMerge(RepositoryException):
+    pass
+
 class RepositoryDoesNotExist(RepositoryException):
     pass
+
index f5b8ce0..0e4f2a0 100755 (executable)
@@ -24,7 +24,7 @@
 
 .upload-file-widget {
     min-width: 20%;
-    width: 25%;
+    width: 35%;
 }
 
 
index c321c78..31d811b 100644 (file)
@@ -334,35 +334,3 @@ div.isection p {
     background-color: yellow;
     border-color: yellow;   
 }
-
-
-/*
- * Object list table
- */
- table.object-list {
-    border-top: 2px solid black;
-    border-left: 2px solid black;
-    border-right: 1px solid black;
-    border-bottom: 1px solid black;
-    width: 60%;
-    margin: auto;
- }
-
-
- table.object-list td, table.object-list th {
-     border-bottom: 1px solid black;
-     border-right: 1px solid black;
-     padding: 0.2em 0.5em;
- }
-
- table.object-list th {
-     text-align: center;
-     background-color: #8080d0;
-     font-size: 120%
- }
-
- table.object-list td.page-navigation {
-     position: relative;
-     text-align: center;
-     background-color: #CCC;
- }
index 9ceb24e..002004e 100644 (file)
@@ -238,7 +238,7 @@ var select = {};
       // Move the start of a range to the start of a node,
       // compensating for the fact that you can't call
       // moveToElementText with text nodes.
-      function moveToNodeStart(range, node) {
+      function moveToNodeStart(range, node) {        
         if (node.nodeType == 3) {
           var count = 0, cur = node.previousSibling;
           while (cur && cur.nodeType == 3) {
@@ -253,7 +253,7 @@ var select = {};
           else range.moveToElementText(node.parentNode);
           if (count) range.move("character", count);
         }
-        else range.moveToElementText(node);
+        else try{range.moveToElementText(node);} catch(e) {};
       }
 
       // Do a binary search through the container object, comparing
index 13c7097..3490b9a 100644 (file)
@@ -151,7 +151,14 @@ Panel.prototype.connectToolbar = function()
     action_buttons.each(function() {
         var button = $(this);
         var hk = button.attr('ui:hotkey');
-        var params = $.evalJSON(button.attr('ui:action-params'));
+
+        try {
+            var params = $.evalJSON(button.attr('ui:action-params'));
+        } catch(object) {
+           $.log('JSON exception in ', button, ': ', object);
+           button.attr('disabled', 'disabled');
+           return;
+        }
 
         var callback = function() {
             editor.callScriptlet(button.attr('ui:action'), self, params);
@@ -242,8 +249,21 @@ Editor.prototype.setupUI = function() {
     $('#toolbar-button-save').click( function (event, data) { 
         self.saveToBranch();
     } );
+
+    $('#toolbar-button-update').click( function (event, data) {
+        if (self.updateUserBranch()) {
+            // commit/update can be called only after proper, save
+            // this means all panels are clean, and will get refreshed
+             // do this only, when there are any changes to local branch
+            self.refreshPanels();
+        }
+    } );
+
     $('#toolbar-button-commit').click( function (event, data) { 
         self.sendPullRequest();
+        event.preventDefault();
+        event.stopPropagation();
+        return false;
     } );
     self.rootDiv.bind('stopResize', function() { 
         self.savePanelOptions()
@@ -364,9 +384,10 @@ Editor.prototype.saveToBranch = function(msg)
                 self.showPopup('save-error', (data.errors && data.errors[0]) || 'Nieznany błąd X_X.');
             }
             else {
-                self.refreshPanels(changed_panel);
+                self.refreshPanels();
                 $('#toolbar-button-save').attr('disabled', 'disabled');
                 $('#toolbar-button-commit').removeAttr('disabled');
+                $('#toolbar-button-update').removeAttr('disabled');
                 if(self.autosaveTimer)
                     clearTimeout(self.autosaveTimer);
 
@@ -402,6 +423,7 @@ Editor.prototype.onContentChanged = function(event, data) {
 
     $('#toolbar-button-save').removeAttr('disabled');
     $('#toolbar-button-commit').attr('disabled', 'disabled');
+    $('#toolbar-button-update').attr('disabled', 'disabled');
     
     if(this.autosaveTimer) return;
     this.autosaveTimer = setTimeout( function() {
@@ -409,11 +431,10 @@ Editor.prototype.onContentChanged = function(event, data) {
     }, 300000 );
 };
 
-Editor.prototype.refreshPanels = function(goodPanel) {
+Editor.prototype.refreshPanels = function() {
     var self = this;
-    var panels = $('#' + self.rootDiv.attr('id') +' > *.panel-wrap', self.rootDiv.parent());
 
-    panels.each(function() {
+    self.allPanels().each(function() {
         var panel = $(this).data('ctrl');
         $.log('Refreshing: ', this, panel);
         if ( panel.changed() )
@@ -424,24 +445,70 @@ Editor.prototype.refreshPanels = function(goodPanel) {
 };             
 
 
+Editor.prototype.updateUserBranch = function() {
+    if( $('.panel-wrap.changed').length != 0)
+        alert("There are unsaved changes - can't update.");
+
+    var self = this;
+    $.ajax({
+        url: $('#toolbar-button-update').attr('ui:ajax-action'),
+       dataType: 'json',
+       success: function(data, textStatus) {
+                switch(data.result) {
+                    case 'done':
+                        self.showPopup('generic-yes', 'Plik uaktualniony.');
+                        self.refreshPanels()
+                        break;
+                    case 'nothing-to-do':
+                        self.showPopup('generic-info', 'Brak zmian do uaktualnienia.');
+                        break;
+                    default:
+                        self.showPopup('generic-error', data.errors && data.errors[0]);
+                }
+       },
+       error: function(rq, tstat, err) {
+               self.showPopup('generic-error', 'Błąd serwera: ' + err);
+       },
+       type: 'POST',
+       data: {}
+    });
+}
+
 Editor.prototype.sendPullRequest = function () {
     if( $('.panel-wrap.changed').length != 0)        
-        alert("There are unsaved changes - can't make a pull request.");
-
-    this.showPopup('not-implemented');
-/*
-       $.ajax({
-               url: '/pull-request',
-               dataType: 'json',
-               success: function(data, textStatus) {
-            $.log('data: ' + data);
-               },
-               error: function(rq, tstat, err) {
-                       $.log('commit error', rq, tstat, err);
-               },
-               type: 'POST',
-               data: {}
-       }); */
+        alert("There are unsaved changes - can't commit.");
+
+    var self =  this;
+
+    /* this.showPopup('not-implemented'); */
+
+    $.log('URL !: ', $('#toolbar-commit-form').attr('action'));
+    
+    $.ajax({        
+        url: $('#toolbar-commit-form').attr('action'),
+       dataType: 'json',
+       success: function(data, textStatus) {
+                switch(data.result) {
+                    case 'done':
+                        self.showPopup('generic-yes', 'Łączenie zmian powiodło się.');
+
+                        if(data.localmodified)
+                            self.refreshPanels()
+                        
+                        break;
+                    case 'nothing-to-do':
+                        self.showPopup('generic-info', 'Brak zmian do połaczenia.');
+                        break;
+                    default:
+                        self.showPopup('generic-error', data.errors && data.errors[0]);
+                }
+       },
+       error: function(rq, tstat, err) {
+               self.showPopup('generic-error', 'Błąd serwera: ' + err);
+       },
+       type: 'POST',
+       data: {'message': $('#toolbar-commit-message').val() }
+    }); 
 }
 
 Editor.prototype.showPopup = function(name, text, timeout)
@@ -454,7 +521,7 @@ Editor.prototype.showPopup = function(name, text, timeout)
         return;
 
     var box = $('#message-box > #' + name);
-    $('*.data', box).html(text);
+    $('*.data', box).html(text || '');
     box.fadeIn();
  
     if(timeout > 0)
@@ -482,6 +549,10 @@ Editor.prototype.advancePopupQueue = function() {
     }
 };
 
+Editor.prototype.allPanels = function() {
+    return $('#' + this.rootDiv.attr('id') +' > *.panel-wrap', this.rootDiv.parent());
+}
+
 
 Editor.prototype.registerScriptlet = function(scriptlet_id, scriptlet_func)
 {
index 315d48f..d6cee14 100644 (file)
@@ -3,35 +3,39 @@
        var LEVEL_INFO = 2;
        var LEVEL_WARN = 3;
        var LOG_LEVEL = LEVEL_DEBUG;
-
-       var mozillaLog = function() {
-               if (window.console)
-                   console.log.apply(this, arguments);
-       };
-
-    var safariLog = function() {
-        if (window.console)
-            console.log.apply(console, arguments);
-    };
+       
+        var standardLog = function() {
+            if (window.console)
+                console.log.apply(console, arguments);
+        };
     
        var operaLog = function() {
                opera.postError(arguments.join(' '));
        };
 
-       var defaultLog = function() { return false; };
+        var msieLog = function() {
+            var args = $.makeArray(arguments);
+            var vals = $.map(args, function(n) {
+                try {
+                    return JSON.stringify(n);
+                } catch(e) {
+                    return ('' + n);
+                }
+            });
 
-       $.log = function( ) {
+            if (window.console)
+                console.log(vals.join(" "));
+        };
+
+       $.log = function() {
                return $.log.browserLog.apply(this, arguments);
        };
 
-       if ($.browser.mozilla)
-               $.log.browserLog = mozillaLog;
-       else if ($.browser.safari)
-           $.log.browserLog = safariLog;
-       else if($.browser.opera)
-               $.log.browserLog = operaLog;
-       else 
-               $.log.browserLog = defaultLog;
-
+        if($.browser.opera)
+            $.log.browserLog = operaLog;
+        else if($.browser.msie)
+            $.log.browserLog = msieLog;
+        else
+            $.log.browserLog = standardLog;
 
 })(jQuery);
index b3a2e4c..fe9d93a 100644 (file)
 {% block breadcrumbs %}<a href="{% url file_list %}">Platforma Redakcyjna</a> &gt; plik {{ hash }}{% endblock breadcrumbs %}
 
 {% block header-toolbar %}
-       <button type="button" class="toolbar-button" id="toolbar-button-commit">Commit</button>
-       <button type="button" class="toolbar-button" id="toolbar-button-save" disabled="disabled">Zapisz</button>
+
+<form action="{% url file_commit hash %}" method="POST" style="display: inline;" id="toolbar-commit-form">
+    <input type="text" size="40" id="toolbar-commit-message" name="message" />
+    <button type="submit" class="toolbar-button" id="toolbar-button-commit">Merge</button>
+</form>
+<button type="button" class="toolbar-button" id="toolbar-button-update"
+        ui:ajax-action="{% url file_update hash %}">Update</button>
+<button type="button" class="toolbar-button" id="toolbar-button-save" disabled="disabled">Save</button>
 {% endblock %}
 
 {% block message-box %}
-    <div class="msg-info" id="save-waiting"><p>Zapisuję dane na serwerze.</p><p class="data" /></div>
-    <div class="msg-success" id="save-successful"><p>Zapisano :)</p><p class="data" /></div>
-    <div class="msg-error" id="save-error"><p>Błąd przy zapisie.</p> <p class="data" /></div>
-    <div class="msg-warning" id="not-implemented"><p>Tej funkcji jeszcze nie ma :(</p><p class="data" /></div>
-    <div class="msg-warning" id="save-warn"><p>Zapisano. Uwagi: (</p><p class="data" /></div>
+    <div class="msg-info" id="save-waiting"><p>Zapisuję dane na serwerze.</p><p class="data"></p></div>
+    <div class="msg-success" id="save-successful"><p>Zapisano :)</p><p class="data"></p></div>
+    <div class="msg-warning" id="save-warn"><p>Zapisano. Uwagi: (</p><p class="data"></p></div>
+    <div class="msg-error" id="save-error"><p>Błąd przy zapisie.</p> <p class="data"></p></div>
+
+    <div class="msg-success" id="generic-yes"><p class="data"></p></div>
+    <div class="msg-info" id="generic-info"><p class="data"></p></div>
+    <div class="msg-error" id="generic-error"><p class="data"></p></div>
+    
+    <div class="msg-warning" id="not-implemented"><p>Tej funkcji jeszcze nie ma :(</p><p class="data"></p></div>
+    
 {% endblock %}
 
 {% block maincontent %}
index 972c33d..cf08247 100644 (file)
@@ -12,6 +12,8 @@ urlpatterns = patterns('',
     url(r'^file/text/'+PATH_END, 'explorer.views.file_xml', name='file_xml'),
     url(r'^file/dc/'+PATH_END, 'explorer.views.file_dc', name='file_dc'),
     url(r'^file/upload', 'explorer.views.file_upload', name='file_upload'),
+    url(r'^file/commit/'+PATH_END, 'explorer.views.file_commit', name='file_commit'),
+    url(r'^file/update/'+PATH_END, 'explorer.views.file_update_local', name='file_update'),
 
     url(r'^images/(?P<folder>[^/]+)/$', 'explorer.views.folder_images', name='folder_image'),
     url(r'^images/$', 'explorer.views.folder_images', {'folder': '.'}, name='folder_image_ajax'),
@@ -23,6 +25,7 @@ urlpatterns = patterns('',
     url(r'^editor/panel/dceditor/'+PATH_END, 'explorer.views.dceditor_panel', name='dceditor_panel'),
     url(r'^editor/'+PATH_END, 'explorer.views.display_editor', name='editor_view'),
 
+    
     # Task managment
     url(r'^manager/pull-requests$', 'explorer.views.pull_requests'),