Commits

Anonymous committed 2547b67

add preferred language selection to friendpaste. Users car choose their preferred language and their choice is saved in session.

Comments (0)

Files changed (16)

build/friendpaste.yml

         - src/resizeable.js
         - src/editor.js
         - src/review.js
+        - src/preferred_languages.js
         - src/friendpaste.js
         - src/diff.js 

friendpaste/application.py

 
         # get paste privacy here
         cur_path = [p for p in request.path.split('/') if p]
-        if cur_path and cur_path not in OTHER_PATHS:
+        if cur_path and cur_path[0] not in OTHER_PATHS:
             pasteid = UUID(int=b62decode(cur_path[0])).hex
             request.pasteid = cur_path[0]
             request.privacy = Privacy.for_paste(local.db, pasteid)
             
             
         if request.session.should_save:
-            print "save"
             max_age = settings.SESSION_COOKIE_AGE
             expires = time.time() + settings.SESSION_COOKIE_AGE
             session_store.save(request.session)

friendpaste/settings.py

 SITE_ID=1
 SECRET_KEY='*j4c=nhmvl@up^@)ftp0i8^2r8woplh^tlwen=z1vs=yod^bc%'
 
-TOP_LEXERS=[
-        ('python', 'Python'),
-        ('pycon', 'Python Console Session'),
-        ('pytb', 'Python Traceback'),
-        ('sql', 'SQL'),
-        ('html+django', 'HTML Django/Jinja'),
-        ('css', 'CSS'),
-        ('xml', 'XML'),
-        ('diff', 'Diff'),
-        ('js', 'Javascript'),
-        ('bash', 'Bash')
-]
-    
+TOP_LEXERS=[] 
 ############
 # SESSIONS #
 ############

friendpaste/urls.py

         'paste/review': views.snippet_review,
         'paste/reviewset': views.snippet_reviews_set,
         'login': views.login,
-        'logout': views.logout
+        'logout': views.logout,
+        'languages': views.fplanguages
 
 }
 
     Rule('/services/<page>', endpoint='generic/services'),
     Rule('/services', endpoint='generic/services'),
     Rule('/settings', endpoint='settings'),
+    Rule('/set_languages', endpoint='languages'),
     Rule('/_all_languages', endpoint='paste/all_languages'),
     Rule('/<id>_<rev>/raw', endpoint='paste/raw'),
     Rule('/<id>_<rev>/original', endpoint='paste/original'),
 ])
 
 
-OTHER_PATHS = ['highlight', 'about', 'services', 'settings', '_all_languages', '.']
+OTHER_PATHS = ['highlight', 'about', 'services', 'settings', '_all_languages',
+        'set_languages', '.', '_']

friendpaste/views.py

 from friendpaste.utils.base62 import *
 
 
-def _get_lexers():
-    lexers = get_all_lexers()
-    top_lexers = None
-    nl = []
+def _get_lexers(request):
+    lexers = list(get_all_lexers())
+    top_lexers = []
     ret=[]
 
-    try:
-        top_lexers = settings.TOP_LEXERS
-    except:
-        pass
-    if top_lexers is not None: 
-        nl = [l[0] for l in top_lexers]
+    preferred = request.session.get('languages', [])
+
+    if not preferred: 
+        try:
+            preferred = settings.TOP_LEXERS
+        except:
+            preferred = []
+    
+    # first pass 
+    if preferred:
+        for l in lexers:
+            if l[1][0] in preferred:
+                top_lexers.append((l[1][0],l[0]))
+        top_lexers.sort()
     
     for l in lexers:
-        if l[1][0] not in nl:
+        print l[1][0]
+        if l[1][0] not in preferred:
             ret.append((l[1][0],l[0]))
+
     ret.sort()
-    if top_lexers is not None and top_lexers: 
+    print ret
+    if top_lexers: 
         ret = top_lexers + [('text', '------------')] + ret
-    return ret
+    return [('text', 'Plain text')] + ret
 
-LEXERS_CHOICE = [('text', 'Plain text')] + _get_lexers()
 ALL_LEXERS=get_all_lexers()
 
 
 class PasteForm(Form):
     title=TextField('Title')
     snippet=TextAreaField('Paste', [validators.length(min=3, max=500000)])
-    language = SelectField(u'Programming Language', choices=LEXERS_CHOICE)
+    language = SelectField(u'Programming Language', choices=[])
     code = PasswordField(u'Removal/Lock code', default='')
     privacy = SelectField(u'Kind of paste', choices=PRIVACY_CHOICES, default='open')
     password = PasswordField(u'Password', default='')
         })
 
     form = PasteForm(request.form, prefix='paste')
+    form.language.choices = _get_lexers(request)
+
     if request.method=='POST' and form.validate():
         code = form.data['code']
         if code:
         'snippet': s.content,
         'language': str(s.language)
     })
+    form.language.choices = _get_lexers(request)
 
     error = ""
 
         'snippet': s.content,
         'language': str(s.language)
     })
+
+    form.language.choices = _get_lexers(request)
+
     if request.method == 'POST' and form.validate():
         code = form.data['code']
         if code:
         'snippet': s.content,
         'language': str(s.language)
     })
-   
+    form.language.choices = _get_lexers(request)
+
     s.pasteid = id
 
     reviews_counts = Review.get_reviews_counts(local.db, pasteid, s.revid)
 
 def get_all_languages(request):
     lexers = get_all_lexers()
-    languages=[(l[1][0],l[0]) for l in lexers]
-    languages.sort()
-    return send_json(dict(languages))
+    languages=[[l[1][0],l[0]] for l in lexers]
+    languages.sort(lambda a, b: cmp(a, b))
+    return send_json(languages)
 
 
 def embed(request, id):
 
     return send_json( { 'ok': True, 'settings': _settings } )
 
+def fplanguages(request):
+    if request.method == "POST" and request.is_xhr:
+        try:
+            data = json.loads(request.data)
+        except ValueError:
+            return send_json({'ok': False})
+
+        print data
+        request.session['languages'] = data
+        return send_json({'ok': True})
+    return send_json({'ok': false})
+
+    
 @not_logged
 def login(request, id):
     mimetypes = request.accept_mimetypes

static/css/src/colors.css

     border-color: #899834;
 }
 
+a.prefLanguages {
+    color: #2e8696;
+}
+
+#languagesBox {
+    color: #303030;
+    background: #f8f8f8;
+    border-color: #899834;
+}
+
+#languagesBox h4 {
+    background: #303030;
+    color: #fff;
+}
+
+#flanguages li {
+    color: #303030;
+    border-bottom-color: #dcdcdc;
+}
+
+
+#flanguages li.r2 {
+    background-color: #e5f1f4;
+}

static/css/src/layout.css

 #notify-snippet.hidden {
     display: none;
 }
+
+.prefLanguages {
+    margin-left: 10px;
+    cursor: pointer;
+}
+#languagesBox h4 {
+    height: 16px;
+    padding: 0.3em;
+    margin: 0;
+}
+#languagesBox {
+    display: block;
+    position: absolute;
+    width: 300px;
+    height: 220px;
+    z-index: 1000;
+    margin:0;
+    padding:0;
+    border: 1px solid;
+}
+
+#languagesBox.hidden {
+    display: none;
+}
+
+#languagesBox ul {
+
+    display: block;
+    height: 160px;
+    width: 100%;
+    overflow-y: auto;
+    padding: 0;
+    margin: 0;
+}
+
+#languagesBox #flanguages {
+    margin:0;
+    padding:0;
+    width: 100%;
+}
+#flanguages li {
+    border-bottom: 2px dashed;
+    margin: 0;
+    padding: 0.3em 0 0 0.3em;
+}
+
+#flanguages ul li label {
+    display: inline;
+    margin: 0;
+    padding: 0;
+    padding-right: 0.3em;
+    width: auto;
+    float: none;
+}
+
+#flanguages input[type='checkbox'] {
+    display: inline;
+    margin-right: 0.3em;
+}
+
+#flanguages p.submit-row {
+    text-align: right;
+    padding: 0.3em;
+}

static/css/src/typography.css

 }
 #switch span {
     text-decoration: none;
-    cursor: hand;
+    cursor: pointer;
 }
 
 
 .comment-box h4 {font-size:1em;line-height:1.25;margin-bottom:1.25em;height:1.25em;}
 .comment-box h5 {font-size:0.9em;font-weight:bold;margin-bottom:1.5em;}
 .comment-box h6 {font-size:0.9em;font-weight:bold;}
+
+
+a.prefLanguages {
+    font-size: 0.8em;
+    font-weight: bold;
+}
+
+#languagesBox h4 {
+    font-size: 0.9em;
+    font-weight: bold;
+}

static/js/src/friendpaste.js

         // init source code editor
         new Editor("#paste_snippet");
 
+        new PreferredLanguages("#paste_language"); 
+        
         this.privacy = document.querySelector('.privacy');
         this.privacy.addEventListener('click', this.handlePrivacy.bind(this), false);
             
     constructor: function() {
         var self = this;
 
-        
-
         /* set handlers */
         var edit = document.querySelector(".e");
         var cancel = document.querySelectorAll(".cancel");
         // init minimal editor
         new Editor("#paste_snippet");
 
+        new PreferredLanguages("#paste_language");
+
         new Review();
 
         this.setSettings();

static/js/src/preferred_languages.js

+/*
+  Copyright 2008 by Benoît Chesneau <benoitc@e-engura.com>
+   
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+*/
+
+var PreferredLanguages = base2.Base.extend({
+    select: false,
+    container: false,
+    el: false,
+    selected: false,
+
+    constructor: function(select) {
+        this.getLanguages();
+        this.select = document.querySelector(select); 
+
+        var el = document.createElement('a');
+        el.classList.add('prefLanguages');
+        el.appendChild(document.createTextNode('preferred languages'));
+        this.select.parentNode.appendChild(el);
+
+        this.el = el;
+        this.el.addEventListener("click", this.showBox.bind(this), false);
+    },
+
+    buildBox: function() {
+        var offsets = this.select.getOffsets();
+        var div = document.createElement('div');
+        div.id = "languagesBox";
+        div.style.top = offsets.y + "px";
+        div.style.left = offsets.x + "px";
+        var h4 = document.createElement('h4');
+        h4.innerHTML = "Select preferred languages";
+        div.appendChild(h4);
+        var form = document.createElement('form');
+        form.action = SITE_URI + "/settings";
+        form.name = form.id = "flanguages";
+        var ul = document.createElement('ul');
+        var j = 0; 
+        for (var i=0; i< this.languages.length; i++) {
+            var lang = this.languages[i];
+            var input = document.createElement('input');
+            input.type = "checkbox";
+            input.value = lang[0];
+            input.id = lang[0];
+
+            var label = document.createElement('label');
+            label.setAttribute('for', lang[0])
+            label.innerHTML = lang[1];
+            var li = document.createElement('li');
+            if (j == 1) {
+                j = 0;
+                li.classList.add('r2');
+            } else {
+                j = 1;
+            }
+            
+            li.appendChild(input);
+            li.appendChild(label);
+            ul.appendChild(li);
+        }
+        form.appendChild(ul); 
+        var submit = document.createElement('input');
+        submit.type = "submit";
+        submit.id = submit.name = "slanguage";
+        submit.value = "Done";
+        var p = document.createElement("p");
+        p.classList.add("submit-row");
+        p.appendChild(submit);
+        form.appendChild(p);
+        
+        div.appendChild(form);
+        div.classList.add('hidden');
+        document.body.appendChild(div);
+        this.container = div;
+
+        submit.addEventListener("click", this.setLanguages.bind(this), false);
+    },
+
+    checkLanguages: function() {
+        var options = this.select.options;
+        var checked_languages = {};
+        for (var i=0; i < options.length; i++) {
+            var el = options[i];
+            if (el.innerHTML == '------------')
+                break;
+            checked_languages[el.value] = 1;
+        }
+
+        this.container.querySelectorAll("input[type='checkbox']").forEach(function(el) {
+            if (checked_languages[el.id])
+                el.setAttribute('checked', "checked")
+            else
+                el.checked = false;
+        });
+    },
+
+
+    showBox: function(e) {
+        e.preventDefault();
+        if (!this.container)
+            this.buildBox();
+
+        this.selected = this.select.options[this.select.options.selectedIndex].value;
+
+        this.checkLanguages();
+        this.container.classList.remove('hidden');
+        e.stopPropagation();
+        return false;
+    },
+
+    setLanguages: function(e) {
+        e.preventDefault();
+        
+        // get preferred languages
+        var preferred_languages = [];
+        var idx_preferred = {}; 
+        this.container.querySelectorAll("input[type='checkbox']:checked").forEach(function(el) {
+            preferred_languages.push(el.id);
+            idx_preferred[el.id] = 1;
+        });
+
+        // refresh select
+        this.select.innerHTML = "";
+        var missing = [];
+
+        var default_lang = document.createElement('option');
+        default_lang.value="text";
+        default_lang.innerHTML = "Plain text";
+        this.select.appendChild(default_lang);
+
+        // 1st pass: we add preferred languages at top
+        for(var i=0; i< this.languages.length; i++) {
+            var lang = this.languages[i];
+            if (idx_preferred[lang[0]]) {
+                var option = document.createElement('option');
+                option.value = lang[0];
+                option.innerHTML = lang[1];
+                if (this.selected && lang[0] == this.selected) {
+                    this.selected = false;
+                    option.setAttribute('selected', 'selected');
+                }
+                this.select.appendChild(option);
+            } else {
+                missing.push(lang);
+            }
+        }
+
+        // add separator
+        var sep = document.createElement('option');
+        sep.value = 'text';
+        sep.innerHTML = '------------';
+        this.select.appendChild(sep);
+
+        // second pass, we add missing languages
+        for(var i=0; i< missing.length; i++) {
+            var lang = missing[i];
+            if (lang!=null) {
+                var option = document.createElement('option');
+                option.value = lang[0];
+                option.innerHTML = lang[1];
+                if (this.selected && lang[0] == this.selected) {
+                    option.setAttribute('selected', 'selected');
+                    this.selected = false;
+                }
+                this.select.appendChild(option);
+            }
+        }
+         new Ajax.request({
+            url: "/set_languages",
+            dataType: "json",
+            contentType: "application/json",
+            method: "POST",
+            data: this.toJSON(preferred_languages),
+            success: function(data) {}
+        });
+
+        this.container.classList.add('hidden'); 
+        e.stopPropagation();
+        return false;
+    },
+
+    getLanguages: function() {
+        var self = this;
+        url = "/_all_languages";
+        new Ajax.get(url, null, function(data) {
+            self.languages = data;
+        }, "json");
+    },
+
+    toJSON: function(obj) {
+        return obj !== null ? base2.JSON.toString(obj) : null;
+    }
+});    
+           
+    

templates/base.html

     <script type="text/javascript" src="/static/js/src/ajax.js"></script>
     {% else %}
     <script type="text/javascript"
-        src="/static/js/friendpaste-libs.js?20081122"></script>
+        src="/static/js/friendpaste-libs.js?20081215"></script>
     {% endif %}
     <script>
         localizeDates();

templates/paste/diff.html

 <script type="text/javascript" src="/static/js/src/diff.js"></script>
 {% else %}
 <script type="text/javascript"
-    src="/static/js/friendpaste.js?200811231151"></script>
+    src="/static/js/friendpaste.js?20081215"></script>
 {% endif %}
 <script type="text/javascript">
     new Diff();

templates/paste/edit.html

 <script type="text/javascript" src="/static/js/lib/showdown.js"></script>
 <script type="text/javascript" src="/static/js/src/resizeable.js"></script>
 <script type="text/javascript" src="/static/js/src/editor.js"></script>
+<script type="text/javascript" src="/static/js/src/preferred_languages.js"></script>
 <script type="text/javascript" src="/static/js/src/friendpaste.js"></script>
 {% else %}
 <script type="text/javascript"
 
     // init source code editor
     new Editor("#paste_snippet");
+    
+    new PreferredLanguages("#paste_language"); 
 </script>
 {% endblock %}

templates/paste/index.html

 {% block script %}
 {% if DEBUG %}
 <script type="text/javascript" src="/static/js/src/resizeable.js"></script>
+<script type="text/javascript" src="/static/js/src/editor.js"></script>
+<script type="text/javascript" src="/static/js/src/preferred_languages.js"></script>
+
 <script type="text/javascript" src="/static/js/src/friendpaste.js"></script>
-<script type="text/javascript" src="/static/js/src/editor.js"></script>
 
 {% else %}
 <script type="text/javascript"
 
 <script type="text/javascript">
     new Home(); 
-
-
 </script>
 {% endblock %}

templates/paste/view.html

 <script type="text/javascript" src="/static/js/src/resizeable.js"></script>
 <script type="text/javascript" src="/static/js/src/editor.js"></script>
 <script type="text/javascript" src="/static/js/src/review.js"></script>
+<script type="text/javascript" src="/static/js/src/preferred_languages.js"></script>
 <script type="text/javascript" src="/static/js/src/friendpaste.js"></script>
 {% else %}
 <script type="text/javascript"
-    src="/static/js/friendpaste.js?200811231151"></script>
+    src="/static/js/friendpaste.js?20081215"></script>
 {% endif %}
 <script type="text/javascript">
     new Friendpaste();

templates/services/json.html

 }
 </pre>
     <h2>Get all languages</h2>
-    <p>To retrieve list of all languages supported by Friendpaste, simply perform a <em>GET</em> operation on <a href="http://friendpaste.com/_all_languages">http://friendpaste.com/all_languages</a> url.</>
+    <p>To retrieve list of all languages supported by Friendpaste, simply perform a <em>GET</em> operation on <a href="http://friendpaste.com/_all_languages">http://friendpaste.com/_all_languages</a> url.</>
     <pre>
 GET /_all_languages HTTP/1.0
 Date: Tue, 29 Apr 2008 05:39:28 +0000GMT
 Content-Type: application/json
 Connection: close
 
-{"text": "Text only", "genshi": "Genshi", "html+myghty": "HTML+Myghty", "trac-wiki": "MoinMoin/Trac Wiki markup", "xml": "XML", "css+mako": "CSS+Mako", "erlang": "Erlang", "python": "Python", "js+genshitext": "JavaScript+Genshi Text", "bat": "Batchfile", "myghty": "Myghty", "d": "D", "js+erb": "JavaScript+Ruby", "squidconf": "SquidConf", "befunge": "Befunge", "apacheconf": "ApacheConf", "csharp": "C#", "tex": "TeX", "llvm": "LLVM", "html+django": "HTML+Django/Jinja", "xml+django": "XML+Django/Jinja", "delphi": "Delphi", "js+smarty": "JavaScript+Smarty", "c-objdump": "c-objdump", "lua": "Lua", "redcode": "Redcode", "rb": "Ruby", "irc": "IRC logs", "html+smarty": "HTML+Smarty", "js": "JavaScript", "bash": "Bash", "c": "C", "vb.net": "VB.net", "ocaml": "OCaml", "genshitext": "Genshi Text", "xml+myghty": "XML+Myghty", "pot": "Gettext Catalog", "smarty": "Smarty", "vim": "VimL", "haskell": "Haskell", "diff": "Diff", "js+django": "JavaScript+Django/Jinja", "rbcon": "Ruby irb session", "pycon": "Python console session", "java": "Java", "js+mako": "JavaScript+Mako", "d-objdump": "d-objdump", "perl": "Perl", "common-lisp": "Common Lisp", "scheme": "Scheme", "rhtml": "RHTML", "html+php": "HTML+PHP", "css+genshitext": "CSS+Genshi Text", "xml+mako": "XML+Mako", "js+php": "JavaScript+PHP", "pytb": "Python Traceback", "gas": "GAS", "rst": "reStructuredText", "cpp-objdump": "cpp-objdump", "xml+smarty": "XML+Smarty", "groff": "Groff", "bbcode": "BBCode", "mysql": "MySQL", "objective-c": "Objective-C", "xml+erb": "XML+Ruby", "erb": "ERB", "css+smarty": "CSS+Smarty", "control": "Debian Control file", "css+erb": "CSS+Ruby", "raw": "Raw token data", "as": "ActionScript", "brainfuck": "Brainfuck", "ini": "INI", "mupad": "MuPAD", "django": "Django/Jinja", "css+myghty": "CSS+Myghty", "mako": "Mako", "make": "Makefile", "minid": "MiniD", "css+django": "CSS+Django/Jinja", "sourceslist": "Debian Sourcelist", "html": "HTML", "css+php": "CSS+PHP", "css": "CSS", "html+mako": "HTML+Mako", "objdump": "objdump", "boo": "Boo", "jsp": "Java Server Page", "sql": "SQL", "php": "PHP", "dylan": "DylanLexer", "xml+php": "XML+PHP", "js+myghty": "JavaScript+Myghty", "lhs": "Literate Haskell", "cpp": "C++", "html+genshi": "HTML+Genshi", "moocode": "MOOCode"}
+[["apacheconf", "ApacheConf"], ["as", "ActionScript"], ["as3", "ActionScript 3"], ["basemake", "Makefile"],
+ ["bash", "Bash"], ["bat", "Batchfile"], ["bbcode", "BBCode"], ["befunge", "Befunge"], ["boo", "Boo"], 
+["brainfuck", "Brainfuck"], ["c", "C"], ["c-objdump", "c-objdump"], ["cheetah", "Cheetah"], ["clojure", 
+"Clojure"], ["common-lisp", "Common Lisp"], ["control", "Debian Control file"], ["cpp", "C++"], 
+["cpp-objdump", "cpp-objdump"], ["csharp", "C#"], ["css", "CSS"], ["css+django", "CSS+Django/Jinja"], 
+["css+erb", "CSS+Ruby"], ["css+genshitext", "CSS+Genshi Text"], ["css+mako", "CSS+Mako"], ["css+myghty",
+ "CSS+Myghty"], ["css+php", "CSS+PHP"], ["css+smarty", "CSS+Smarty"], ["d", "D"], ["d-objdump", 
+"d-objdump"], ["delphi", "Delphi"], ["diff", "Diff"], ["django", "Django/Jinja"], ["dpatch", "Darcs Patch"],
+ ["dylan", "Dylan"], ["erb", "ERB"], ["erlang", "Erlang"], ["fortran", "Fortran"], ["gas", "GAS"], ["genshi",
+ "Genshi"], ["genshitext", "Genshi Text"], ["gnuplot", "Gnuplot"], ["groff", "Groff"], ["haskell", "Haskell"],
+ ["html", "HTML"], ["html+cheetah", "HTML+Cheetah"], ["html+django", "HTML+Django/Jinja"], ["html+genshi",
+ "HTML+Genshi"], ["html+mako", "HTML+Mako"], ["html+myghty", "HTML+Myghty"], ["html+php", "HTML+PHP"],
+ ["html+smarty", "HTML+Smarty"], ["ini", "INI"], ["io", "Io"], ["irc", "IRC logs"], ["java", "Java"]]
 </pre>
 
 </div>