lit 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Prograils.com
1
+ Copyright (c) 2013 Prograils.com
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Lost in translation
2
2
  ### Rails i18n web interface
3
3
 
4
- Translate your apps with pleasure (sort of...) and for free. It's simple i18n
5
- web interface, build on top of twitter bootstrap, that one may find helpful in
6
- translating app by non-technicals.
4
+ Translate your apps with pleasure (sort of...) and for free. It's simple i18n
5
+ web interface, build on top of twitter bootstrap, that one may find helpful in
6
+ translating app by non-technicals.
7
7
 
8
8
  Highly inspired by Copycopter by thoughtbot.
9
9
 
@@ -15,6 +15,7 @@ Highly inspired by Copycopter by thoughtbot.
15
15
  4. Possibility to synchronize between environments or even apps
16
16
  5. Easy to install - works as an engine, comes with simple generator
17
17
  6. You can always export all translations to plain old YAML file
18
+ 7. Has build in wysiwyg editor ([jQuery TE](http://jqueryte.com/))
18
19
 
19
20
  ### Screenshots
20
21
 
@@ -30,6 +31,7 @@ gem "lit"
30
31
  2. run `bundle install`
31
32
 
32
33
  3. run installation generator `bundle exec rails g lit:install`
34
+ (for production/staging environment `redis` is suggested as key value engine. `hash` will not work in multi process environment)
33
35
 
34
36
  4. After doing above and restarting app, point your browser to ```http://app/lit```
35
37
 
@@ -41,17 +43,18 @@ You may want to take a look at generated initializer in `config/initializers/lit
41
43
 
42
44
  ### ToDo
43
45
 
44
- * ~~Versioning~~
46
+ * ~~Versioning~~
45
47
  * ~~API~~
46
48
  * ~~Synchronization between environments~~
47
49
  * Rewrite initializer
48
50
  * Rewrite exporter (which is now code from copycopter)
49
51
  * ~~Support for array types (ie. `date.abbr_day_names`)~~
50
52
  * ~~Generator~~
51
- * Support for wysiwyg
53
+ * ~~Support for wysiwyg~~
52
54
  * Better cache
53
55
  * ~~Support for other key value providers (ie. Redis does not support Array types in easy way)~~ (not applicable, as array storage works now with redis).
54
56
  * Integration with ActiveAdmin
57
+ * Support for Proc defaults (like in `I18n.t("not_exising_keys", :default=> lambda{|_, options| "text"})` )
55
58
 
56
59
 
57
60
  ### License
@@ -18,4 +18,5 @@
18
18
 
19
19
  $(document).ready(function(){
20
20
  $('.title').tooltip();
21
+
21
22
  });
@@ -0,0 +1,9 @@
1
+ /*!
2
+ *
3
+ * jQuery TE 1.4.0 , http://jqueryte.com/
4
+ * Copyright (C) 2013, Fatih Koca (fattih@fattih.com), (http://jqueryte.com/about)
5
+
6
+ * jQuery TE is provided under the MIT LICENSE.
7
+ *
8
+ */
9
+ (function(e){e.fn.jqte=function(t){function l(e,t,n,r,i){var s=f.length+1;return f.push({name:e,cls:s,command:t,key:n,tag:r,emphasis:i})}var n=[{title:"Text Format"},{title:"Font Size"},{title:"Color"},{title:"Bold",hotkey:"B"},{title:"Italic",hotkey:"I"},{title:"Underline",hotkey:"U"},{title:"Ordered List",hotkey:"."},{title:"Unordered List",hotkey:","},{title:"Subscript",hotkey:"down arrow"},{title:"Superscript",hotkey:"up arrow"},{title:"Outdent",hotkey:"left arrow"},{title:"Indent",hotkey:"right arrow"},{title:"Justify Left"},{title:"Justify Center"},{title:"Justify Right"},{title:"Strike Through",hotkey:"K"},{title:"Add Link",hotkey:"L"},{title:"Remove Link"},{title:"Cleaner Style",hotkey:"Delete"},{title:"Horizontal Rule",hotkey:"H"},{title:"Source"}];var r=[["p","Normal"],["h1","Header 1"],["h2","Header 2"],["h3","Header 3"],["h4","Header 4"],["h5","Header 5"],["h6","Header 6"],["pre","Preformatted"]];var i=["10","12","16","18","20","24","28"];var s=["0,0,0","68,68,68","102,102,102","153,153,153","204,204,204","238,238,238","243,243,243","255,255,255",null,"255,0,0","255,153,0","255,255,0","0,255,0","0,255,255","0,0,255","153,0,255","255,0,255",null,"244,204,204","252,229,205","255,242,204","217,234,211","208,224,227","207,226,243","217,210,233","234,209,220","234,153,153","249,203,156","255,229,153","182,215,168","162,196,201","159,197,232","180,167,214","213,166,189","224,102,102","246,178,107","255,217,102","147,196,125","118,165,175","111,168,220","142,124,195","194,123,160","204,0,0","230,145,56","241,194,50","106,168,79","69,129,142","61,133,198","103,78,167","166,77,121","153,0,0","180,95,6","191,144,0","56,118,29","19,79,92","11,83,148","53,28,117","116,27,71","102,0,0","120,63,4","127,96,0","39,78,19","12,52,61","7,55,99","32,18,77","76,17,48"];var o=["Web Address","E-mail Address","Picture URL"];var u=e.extend({status:true,css:"jqte",title:true,titletext:n,button:"OK",format:true,formats:r,fsize:true,fsizes:i,funit:"px",color:true,linktypes:o,b:true,i:true,u:true,ol:true,ul:true,sub:true,sup:true,outdent:true,indent:true,left:true,center:true,right:true,strike:true,link:true,unlink:true,remove:true,rule:true,source:true,placeholder:false,br:true,p:true,change:"",focus:"",blur:""},t);e.fn.jqteVal=function(t){e(this).closest("."+u.css).find("."+u.css+"_editor").html(t)};var a=navigator.userAgent.toLowerCase();if(/msie [1-7]./.test(a))u.title=false;var f=[];l("format","formats","","",false);l("fsize","fSize","","",false);l("color","colors","","",false);l("b","Bold","B",["b","strong"],true);l("i","Italic","I",["i","em"],true);l("u","Underline","U",["u"],true);l("ol","insertorderedlist","¾",["ol"],true);l("ul","insertunorderedlist","¼",["ul"],true);l("sub","subscript","(",["sub"],true);l("sup","superscript","&",["sup"],true);l("outdent","outdent","%",["blockquote"],false);l("indent","indent","'",["blockquote"],true);l("left","justifyLeft","","",false);l("center","justifyCenter","","",false);l("right","justifyRight","","",false);l("strike","strikeThrough","K",["strike"],true);l("link","linkcreator","L",["a"],true);l("unlink","unlink","",["a"],false);l("remove","removeformat",".","",false);l("rule","inserthorizontalrule","H",["hr"],false);l("source","displaysource","","",false);return this.each(function(){function B(){if(window.getSelection)return window.getSelection();else if(document.selection&&document.selection.createRange&&document.selection.type!="None")return document.selection.createRange()}function j(e,t){var n,r=B();if(window.getSelection){if(r.anchorNode&&r.getRangeAt)n=r.getRangeAt(0);if(n){r.removeAllRanges();r.addRange(n)}if(!a.match(/msie/))document.execCommand("StyleWithCSS",false,false);document.execCommand(e,false,t)}else if(document.selection&&document.selection.createRange&&document.selection.type!="None"){n=document.selection.createRange();n.execCommand(e,false,t)}q(false,false)}function F(t,n,r){if(v.not(":focus"))v.focus();if(window.getSelection){var i=B(),s,o,u;if(i.anchorNode&&i.getRangeAt){s=i.getRangeAt(0);o=document.createElement(t);e(o).attr(n,r);u=s.extractContents();o.appendChild(u);s.insertNode(o);i.removeAllRanges();if(n=="style")q(e(o),r);else q(e(o),false)}}else if(document.selection&&document.selection.createRange&&document.selection.type!="None"){var a=document.selection.createRange();var f=a.htmlText;var l="<"+t+" "+n+'="'+r+'">'+f+"</"+t+">";document.selection.createRange().pasteHTML(l)}}function q(e,t){var n=I();n=n?n:e;if(n&&t==false){if(n.parent().is("[style]"))n.attr("style",n.parent().attr("style"));if(n.is("[style]"))n.find("*").attr("style",n.attr("style"))}else if(e&&t&&e.is("[style]")){var r=t.split(";");r=r[0].split(":");if(e.is("[style*="+r[0]+"]"))e.find("*").css(r[0],r[1]);R(e)}}function R(t){if(t){var t=t[0];if(document.body.createTextRange){var n=document.body.createTextRange();n.moveToElementText(t);n.select()}else if(window.getSelection){var r=window.getSelection();var n=document.createRange();if(t!="undefined"&&t!=null){n.selectNodeContents(t);r.removeAllRanges();r.addRange(n);if(e(t).is(":empty")){e(t).append(" ");R(e(t))}}}}}function U(){if(!p.data("sourceOpened")){var t=I();var n="http://";W(true);if(t){var r=t.prop("tagName").toLowerCase();if(r=="a"&&t.is("[href]")){n=t.attr("href");t.attr(S,"")}else F("a",S,"")}else y.val(n).focus();g.click(function(t){if(e(t.target).hasClass(u.css+"_linktypetext")||e(t.target).hasClass(u.css+"_linktypearrow"))X(true)});w.find("a").click(function(){var t=e(this).attr(u.css+"-linktype");w.data("linktype",t);E.find("."+u.css+"_linktypetext").html(w.find("a:eq("+w.data("linktype")+")").text());V(n);X()});V(n);y.focus().val(n).bind("keypress keyup",function(e){if(e.keyCode==13){z(h.find("["+S+"]"));return false}});b.click(function(){z(h.find("["+S+"]"))})}else W(false)}function z(t){y.focus();R(t);t.removeAttr(S);if(w.data("linktype")!="2")j("createlink",y.val());else{j("insertImage",y.val());v.find("img").each(function(){var t=e(this).prev("a");var n=e(this).next("a");if(t.length>0&&t.html()=="")t.remove();else if(n.length>0&&n.html()=="")n.remove()})}W();v.trigger("change")}function W(e){Q("["+S+"]:not([href])");h.find("["+S+"][href]").removeAttr(S);if(e){p.data("linkOpened",true);d.show()}else{p.data("linkOpened",false);d.hide()}X()}function X(e){if(e)w.show();else w.hide()}function V(e){var t=w.data("linktype");if(t=="1"&&(y.val()=="http://"||y.is("[value^=http://]")||!y.is("[value^=mailto]")))y.val("mailto:");else if(t!="1"&&!y.is("[value^=http://]"))y.val("http://");else y.val(e)}function J(t){if(!p.data("sourceOpened")){if(t=="fSize")styleField=P;else if(t=="colors")styleField=H;K(styleField,true);styleField.find("a").unbind("click").click(function(){var n=e(this).attr(u.css+"-styleval");if(t=="fSize"){styleType="font-size";n=n+u.funit}else if(t=="colors"){styleType="color";n="rgb("+n+")"}var r=G(styleType);F("span","style",styleType+":"+n+";"+r);K("",false);e("."+u.css+"_title").remove();v.trigger("change")})}else K(styleField,false);W(false)}function K(e,t){var n="",r=[{d:"fsizeOpened",f:P},{d:"cpallOpened",f:H}];if(e!=""){for(var i=0;i<r.length;i++){if(e==r[i]["f"])n=r[i]}}if(t){p.data(n["d"],true);n["f"].slideDown(100);for(var i=0;i<r.length;i++){if(n["d"]!=r[i]["d"]){p.data(r[i]["d"],false);r[i]["f"].slideUp(100)}}}else{for(var i=0;i<r.length;i++){p.data(r[i]["d"],false);r[i]["f"].slideUp(100)}}}function Q(t){h.find(t).each(function(){e(this).before(e(this).html()).remove()})}function G(e){var t=I();if(t&&t.is("[style]")&&t.css(e)!=""){var n=t.css(e);t.css(e,"");var r=t.attr("style");t.css(e,n);return r}else return""}function Y(){Z(true);D.find("a").click(function(){e("*",this).click(function(e){e.preventDefault();return false});et(e(this).text());var t=e(this).attr(u.css+"-formatval");j("formatBlock","<"+t+">");Z(false)})}function Z(e){var t=e?true:false;t=e&&D.data("status")?true:false;if(t||!e)D.data("status",false).slideUp(200);else D.data("status",true).slideDown(200)}function et(e){var t=D.closest("."+u.css+"_tool").find("."+u.css+"_tool_label").find("."+u.css+"_tool_text");if(e.length>10)e=e.substr(0,7)+"...";t.html(e)}function tt(e){var t,n,r;t=e.replace(/\n/gim,"").replace(/\r/gim,"").replace(/\t/gim,"").replace(/ /gim," ");n=[/\<span(|\s+.*?)><span(|\s+.*?)>(.*?)<\/span><\/span>/gim,/<(\w*[^p])\s*[^\/>]*>\s*<\/\1>/gim,/\<div(|\s+.*?)>(.*?)\<\/div>/gim,/\<strong(|\s+.*?)>(.*?)\<\/strong>/gim,/\<em(|\s+.*?)>(.*?)\<\/em>/gim];r=["<span$2>$3</span>","","<p$1>$2</p>","<b$1>$2</b>","<i$1>$2</i>"];for(A=0;A<5;A++){for(var i=0;i<n.length;i++){t=t.replace(n[i],r[i])}}if(!u.p)t=t.replace(/\<p(|\s+.*?)>(.*?)\<\/p>/ig,"<br/>$2");if(!u.br){n=[/\<br>(.*?)/ig,/\<br\/>(.*?)/ig];r=["<p>$1</p>","<p>$1</p>"];for(var i=0;i<n.length;i++){t=t.replace(n[i],r[i])}}if(!u.p&&!u.br)t=t.replace(/\<p>(.*?)\<\/p>/ig,"<div>$1</div>");return t}function nt(){var e=v.text()==""&&v.html().length<12?"":v.html();l.val(tt(e))}function rt(){v.html(tt(l.val()))}function it(t){var n=false,r=I(),i;if(r){e.each(t,function(t,s){i=r.prop("tagName").toLowerCase();if(i==s)n=true;else{r.parents().each(function(){i=e(this).prop("tagName").toLowerCase();if(i==s)n=true})}});return n}else return false}function st(t){for(var n=0;n<f.length;n++){if(u[f[n].name]&&f[n].emphasis&&f[n].tag!="")it(f[n].tag)?p.find("."+u.css+"_tool_"+f[n].cls).addClass(m):e("."+u.css+"_tool_"+f[n].cls).removeClass(m)}if(u.format&&e.isArray(u.formats)){var r=false;for(var i=0;i<u.formats.length;i++){var s=[];s[0]=u.formats[i][0];if(u.formats[i][0].length>0&&it(s)){et(u.formats[i][1]);r=true;break}}if(!r)et(u.formats[0][1])}K("",false);Z(false)}if(!e(this).data("jqte")||e(this).data("jqte")==null||e(this).data("jqte")=="undefined")e(this).data("jqte",true);else e(this).data("jqte",false);if(!u.status||!e(this).data("jqte")){if(e(this).closest("."+u.css).length>0){var t=e(this).closest("."+u.css).find("."+u.css+"_editor").html();var n="";e(e(this)[0].attributes).each(function(){if(this.nodeName!="style")n=n+" "+this.nodeName+'="'+this.nodeValue+'"'});var r=e(this).is("[data-origin]")&&e(this).attr("data-origin")!=""?e(this).attr("data-origin"):"textarea";var i=">"+t;if(r=="input"||r=="option"){t=t.replace(/"/g,"&#34;").replace(/'/g,"&#39;").replace(/</g,"<").replace(/>/g,">");i='value="'+t+'">'}var o=e(this).clone();e(this).data("jqte",false).closest("."+u.css).before(o).remove();o.replaceWith("<"+r+n+i+"</"+r+">")}return}var l=e(this);var r=e(this).prop("tagName").toLowerCase();e(this).attr("data-origin",r);var c=e(this).is("[value]")||r=="textarea"?e(this).val():e(this).html();c=c.replace(/&#34;/g,'"').replace(/&#39;/g,"'").replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&");e(this).after('<div class="'+u.css+'"></div>');var h=e(this).next("."+u.css);h.html('<div class="'+u.css+"_toolbar"+'" role="toolbar" unselectable></div><div class="'+u.css+'_linkform" style="display:none" role="dialog"></div><div class="'+u.css+"_editor"+'"></div>');var p=h.find("."+u.css+"_toolbar");var d=h.find("."+u.css+"_linkform");var v=h.find("."+u.css+"_editor");var m=u.css+"_tool_depressed";d.append('<div class="'+u.css+'_linktypeselect" unselectable></div><input class="'+u.css+'_linkinput" type="text/css" value=""><div class="'+u.css+'_linkbutton" unselectable>'+u.button+'</div> <div style="height:1px;float:none;clear:both"></div>');var g=d.find("."+u.css+"_linktypeselect");var y=d.find("."+u.css+"_linkinput");var b=d.find("."+u.css+"_linkbutton");g.append('<div class="'+u.css+'_linktypeview" unselectable></div><div class="'+u.css+'_linktypes" role="menu" unselectable></div>');var w=g.find("."+u.css+"_linktypes");var E=g.find("."+u.css+"_linktypeview");var S=u.css+"-setlink";v.after('<div class="'+u.css+"_source "+u.css+'_hiddenField"></div>');var x=h.find("."+u.css+"_source");l.appendTo(x);if(r!="textarea"){var n="";e(l[0].attributes).each(function(){if(this.nodeName!="type"&&this.nodeName!="value")n=n+" "+this.nodeName+'="'+this.nodeValue+'"'});l.replaceWith("<textarea "+n+">"+c+"</textarea>");l=x.find("textarea")}v.attr("contenteditable","true").html(c);for(var T=0;T<f.length;T++){if(u[f[T].name]){var N=f[T].key.length>0?u.titletext[T].hotkey!=null&&u.titletext[T].hotkey!="undefined"&&u.titletext[T].hotkey!=""?" (Ctrl+"+u.titletext[T].hotkey+")":"":"";var C=u.titletext[T].title!=null&&u.titletext[T].title!="undefined"&&u.titletext[T].title!=""?u.titletext[T].title+N:"";p.append('<div class="'+u.css+"_tool "+u.css+"_tool_"+f[T].cls+'" role="button" data-tool="'+T+'" unselectable><a class="'+u.css+'_tool_icon" unselectable></a></div>');p.find("."+u.css+"_tool[data-tool="+T+"]").data({tag:f[T].tag,command:f[T].command,emphasis:f[T].emphasis,title:C});if(f[T].name=="format"&&e.isArray(u.formats)){var k=u.formats[0][1].length>0&&u.formats[0][1]!="undefined"?u.formats[0][1]:"";p.find("."+u.css+"_tool_"+f[T].cls).find("."+u.css+"_tool_icon").replaceWith('<a class="'+u.css+'_tool_label" unselectable><span class="'+u.css+'_tool_text" unselectable>'+k+'</span><span class="'+u.css+'_tool_icon" unselectable></span></a>');p.find("."+u.css+"_tool_"+f[T].cls).append('<div class="'+u.css+'_formats" unselectable></div>');for(var L=0;L<u.formats.length;L++){p.find("."+u.css+"_formats").append("<a "+u.css+'-formatval="'+u.formats[L][0]+'" class="'+u.css+"_format"+" "+u.css+"_format_"+L+'" role="menuitem" unselectable>'+u.formats[L][1]+"</a>")}p.find("."+u.css+"_formats").data("status",false)}else if(f[T].name=="fsize"&&e.isArray(u.fsizes)){p.find("."+u.css+"_tool_"+f[T].cls).append('<div class="'+u.css+'_fontsizes" unselectable></div>');for(var L=0;L<u.fsizes.length;L++){p.find("."+u.css+"_fontsizes").append("<a "+u.css+'-styleval="'+u.fsizes[L]+'" class="'+u.css+"_fontsize"+'" style="font-size:'+u.fsizes[L]+u.funit+'" role="menuitem" unselectable>Abcdefgh...</a>')}}else if(f[T].name=="color"&&e.isArray(s)){p.find("."+u.css+"_tool_"+f[T].cls).append('<div class="'+u.css+'_cpalette" unselectable></div>');for(var A=0;A<s.length;A++){if(s[A]!=null)p.find("."+u.css+"_cpalette").append("<a "+u.css+'-styleval="'+s[A]+'" class="'+u.css+"_color"+'" style="background-color: rgb('+s[A]+')" role="gridcell" unselectable></a>');else p.find("."+u.css+"_cpalette").append('<div class="'+u.css+"_colorSeperator"+'"></div>')}}}}w.data("linktype","0");for(var T=0;T<3;T++){w.append("<a "+u.css+'-linktype="'+T+'" unselectable>'+u.linktypes[T]+"</a>");E.html('<div class="'+u.css+'_linktypearrow" unselectable></div><div class="'+u.css+'_linktypetext">'+w.find("a:eq("+w.data("linktype")+")").text()+"</div>")}var O="";if(/msie/.test(a))O="-ms-";else if(/chrome/.test(a)||/safari/.test(a)||/yandex/.test(a))O="-webkit-";else if(/mozilla/.test(a))O="-moz-";else if(/opera/.test(a))O="-o-";else if(/konqueror/.test(a))O="-khtml-";else O="";if(u.placeholder&&u.placeholder!=""){h.prepend('<div class="'+u.css+'_placeholder" unselectable><div class="'+u.css+'_placeholder_text">'+u.placeholder+"</div></div>");var M=h.find("."+u.css+"_placeholder");M.click(function(){v.focus()})}h.find("[unselectable]").css(O+"user-select","none").addClass("unselectable").attr("unselectable","on").on("selectstart mousedown",false);var _=p.find("."+u.css+"_tool");var D=p.find("."+u.css+"_formats");var P=p.find("."+u.css+"_fontsizes");var H=p.find("."+u.css+"_cpalette");var I=function(){var t,n;if(window.getSelection){n=getSelection();t=n.anchorNode}if(!t&&document.selection&&document.selection.createRange&&document.selection.type!="None"){n=document.selection;var r=n.getRangeAt?n.getRangeAt(0):n.createRange();t=r.commonAncestorContainer?r.commonAncestorContainer:r.parentElement?r.parentElement():r.item(0)}if(t){return t.nodeName=="#text"?e(t.parentNode):e(t)}else return false};_.unbind("click").click(function(t){if(e(this).data("command")=="displaysource"&&!p.data("sourceOpened")){p.find("."+u.css+"_tool").addClass(u.css+"_hiddenField");e(this).removeClass(u.css+"_hiddenField");p.data("sourceOpened",true);l.css("height",v.outerHeight());x.removeClass(u.css+"_hiddenField");v.addClass(u.css+"_hiddenField");l.focus();W(false);K("",false);Z();if(u.placeholder&&u.placeholder!="")M.hide()}else{if(!p.data("sourceOpened")){if(e(this).data("command")=="linkcreator"){if(!p.data("linkOpened"))U();else{W(false);Z(false)}}else if(e(this).data("command")=="formats"){if(e(this).data("command")=="formats"&&!e(t.target).hasClass(u.css+"_format"))Y();K("",false);if(v.not(":focus"))v.focus()}else if(e(this).data("command")=="fSize"||e(this).data("command")=="colors"){if(e(this).data("command")=="fSize"&&!e(t.target).hasClass(u.css+"_fontsize")||e(this).data("command")=="colors"&&!e(t.target).hasClass(u.css+"_color"))J(e(this).data("command"));Z(false);if(v.not(":focus"))v.focus()}else{if(v.not(":focus"))v.focus();j(e(this).data("command"),null);K("",false);Z(false);X();e(this).data("emphasis")==true&&!e(this).hasClass(m)?e(this).addClass(m):e(this).removeClass(m);x.addClass(u.css+"_hiddenField");v.removeClass(u.css+"_hiddenField")}}else{p.data("sourceOpened",false);p.find("."+u.css+"_tool").removeClass(u.css+"_hiddenField");x.addClass(u.css+"_hiddenField");v.removeClass(u.css+"_hiddenField")}if(u.placeholder&&u.placeholder!="")v.html()!=""?M.hide():M.show()}v.trigger("change")}).hover(function(t){if(u.title&&e(this).data("title")!=""&&(e(t.target).hasClass(u.css+"_tool")||e(t.target).hasClass(u.css+"_tool_icon"))){e("."+u.css+"_title").remove();h.append('<div class="'+u.css+'_title"><div class="'+u.css+'_titleArrow"><div class="'+u.css+'_titleArrowIcon"></div></div><div class="'+u.css+'_titleText">'+e(this).data("title")+"</div></div>");var n=e("."+u.css+"_title:first");var r=n.find("."+u.css+"_titleArrowIcon");var i=e(this).position();var s=i.left+e(this).outerWidth()-n.outerWidth()/2-e(this).outerWidth()/2;var o=i.top+e(this).outerHeight()+5;n.delay(400).css({top:o,left:s}).fadeIn(200)}},function(){e("."+u.css+"_title").remove()});var ot=null;v.bind("keypress keyup keydown drop cut copy paste DOMCharacterDataModified DOMSubtreeModified",function(){if(!p.data("sourceOpened"))e(this).trigger("change");X();if(e.isFunction(u.change))u.change();if(u.placeholder&&u.placeholder!="")e(this).text()!=""?M.hide():M.show()}).bind("change",function(){if(!p.data("sourceOpened")){clearTimeout(ot);ot=setTimeout(nt,10)}}).keydown(function(e){if(e.ctrlKey){for(var t=0;t<f.length;t++){if(u[f[t].name]&&e.keyCode==f[t].key.charCodeAt(0)){if(f[t].command!=""&&f[t].command!="linkcreator")j(f[t].command,null);else if(f[t].command=="linkcreator")U();return false}}}}).bind("mouseup keyup",st).focus(function(){if(e.isFunction(u.focus))u.focus();h.addClass(u.css+"_focused");if(/opera/.test(a)){var t=document.createRange();t.selectNodeContents(v[0]);t.collapse(false);var n=window.getSelection();n.removeAllRanges();n.addRange(t)}}).focusout(function(){_.removeClass(m);K("",false);Z(false);X();if(e.isFunction(u.blur))u.blur();h.removeClass(u.css+"_focused");if(e.isArray(u.formats))et(u.formats[0][1])});l.bind("keydown keyup",function(){setTimeout(rt,0);e(this).height(e(this)[0].scrollHeight);if(e(this).val()=="")e(this).height(0)}).focus(function(){h.addClass(u.css+"_focused")}).focusout(function(){h.removeClass(u.css+"_focused")})})}})(jQuery)
@@ -2,10 +2,11 @@
2
2
  $(document).ready ->
3
3
  $('td.localization_row[data-editing=0]').on 'click', ->
4
4
  $this = $(this)
5
- edited_rows[$this.data('id')] = $this.html()
6
- unless parseInt($this.data('editing'))
7
- $this.data('editing', '1')
8
- $.get $this.data('edit')
5
+ if parseInt($this.data('editing'))==0
6
+ edited_rows[$this.data('id')] = $this.html()
7
+ unless parseInt($this.data('editing'))
8
+ $this.data('editing', '1')
9
+ $.get $this.data('edit')
9
10
  $('td.localization_row').on 'click', 'form button.cancel', (e)->
10
11
  $this = $(this)
11
12
  if $this[0].localName=='button'
@@ -19,3 +20,5 @@ $(document).ready ->
19
20
  $parent = $this.parents('tr.localization_versions_row')
20
21
  $parent.addClass('hidden')
21
22
  $parent.children('td').html('')
23
+ $('tr.localization_key_row').on 'click', 'input.wysiwyg_switch', (e)->
24
+ $(this).parents('form').find("textarea").jqte()
@@ -9,6 +9,7 @@
9
9
  * compiled file, but it's generally better to create a new file per style scope.
10
10
  *
11
11
  *= require './bootstrap_and_overrides.css'
12
+ *= require './jquery-te-1.4.0.css'
12
13
  *= require_self
13
14
  */
14
15
  .detail_wrapper{
@@ -0,0 +1,457 @@
1
+ /*!
2
+ *
3
+ * jQuery TE 1.4.0 , http://jqueryte.com/
4
+ * Copyright (C) 2013, Fatih Koca (fattih@fattih.com), (http://jqueryte.com/about)
5
+
6
+ * jQuery TE is provided under the MIT LICENSE.
7
+ *
8
+ */
9
+
10
+ /* editor's general field */
11
+ .jqte {
12
+ /*margin:30px 0;
13
+ border:#000 1px solid;
14
+ border-radius:5px; -webkit-border-radius:5px; -moz-border-radius:5px;
15
+ box-shadow:0 0 3px #999; -webkit-box-shadow:0 0 3px #999; -moz-box-shadow:0 0 3px #999;
16
+ overflow:hidden;
17
+ transition:box-shadow 0.4s, border 0.4s; -webkit-transition:-webkit-box-shadow 0.4s, border 0.4s; -moz-transition:-moz-box-shadow 0.4s, border 0.4s; -o-transition:-o-box-shadow 0.4s, border 0.4s;
18
+ }
19
+ .jqte * {
20
+ color:#333;
21
+ font-family:Arial, Helvetica, sans-serif;
22
+ font-size:14px;
23
+ }
24
+ .jqte_focused {
25
+ border-color:#00AAE7;
26
+ box-shadow:0 0 10px #00BDFF; -webkit-box-shadow:0 0 10px #00BDFF; -moz-box-shadow:0 0 10px #00BDFF;
27
+ }
28
+ /* toolbar */
29
+ .jqte_toolbar {
30
+ overflow:auto;
31
+ padding:3px 4px;
32
+ background:#EEE;
33
+ border-bottom:#BBB 1px solid;
34
+ }
35
+ .jqte_tool {
36
+ float:left;
37
+ margin:0;
38
+ padding:0;
39
+ cursor:pointer;
40
+ }
41
+ .jqte_tool, .jqte_tool_icon, .jqte_tool_label {
42
+ border:#EEE 1px solid;
43
+ border-radius:3px; -webkit-border-radius:3px; -moz-border-radius:3px
44
+ }
45
+ .jqte_hiddenField {
46
+ display:none
47
+ }
48
+ .jqte_tool_icon {
49
+ display:block;
50
+ width:22px;
51
+ height:22px;
52
+ background:image-url('lit/jquery-te.png') no-repeat
53
+ }
54
+ .jqte_tool.jqte_tool_1 .jqte_tool_label {
55
+ position:relative;
56
+ display:block;
57
+ padding:3px;
58
+ width:70px;
59
+ height:16px;
60
+ overflow:hidden;
61
+ }
62
+ .jqte_tool.jqte_tool_1 .jqte_tool_text {
63
+ font:bold 13px Arial,sans-serif;
64
+ color:#222;
65
+ }
66
+ .jqte_tool.jqte_tool_1 .jqte_tool_icon {
67
+ position:absolute;
68
+ top:10px;
69
+ right:2px;
70
+ width:6px;
71
+ height:4px;
72
+ background-position:-19px -23px;
73
+ border:none;
74
+ border-radius:none; -webkit-border-radius:none; -moz-border-radius:none
75
+ }
76
+ .jqte_tool.jqte_tool_2 .jqte_tool_icon {
77
+ background-position:0 0
78
+ }
79
+ .jqte_tool.jqte_tool_3 .jqte_tool_icon {
80
+ background-position:-22px 0
81
+ }
82
+ .jqte_tool.jqte_tool_4 .jqte_tool_icon {
83
+ background-position:-44px 0
84
+ }
85
+ .jqte_tool.jqte_tool_5 .jqte_tool_icon {
86
+ background-position:-66px 0
87
+ }
88
+ .jqte_tool.jqte_tool_6 .jqte_tool_icon {
89
+ background-position:-88px 0
90
+ }
91
+ .jqte_tool.jqte_tool_7 .jqte_tool_icon {
92
+ background-position:-110px 0
93
+ }
94
+ .jqte_tool.jqte_tool_8 .jqte_tool_icon {
95
+ background-position:-132px 0
96
+ }
97
+ .jqte_tool.jqte_tool_9 .jqte_tool_icon {
98
+ background-position:-154px 0
99
+ }
100
+ .jqte_tool.jqte_tool_10 .jqte_tool_icon {
101
+ background-position:-176px 0
102
+ }
103
+ .jqte_tool.jqte_tool_11 .jqte_tool_icon {
104
+ background-position:-198px 0
105
+ }
106
+ .jqte_tool.jqte_tool_12 .jqte_tool_icon {
107
+ background-position:-220px 0
108
+ }
109
+ .jqte_tool.jqte_tool_13 .jqte_tool_icon {
110
+ background-position:-242px 0
111
+ }
112
+ .jqte_tool.jqte_tool_14 .jqte_tool_icon {
113
+ background-position:-264px 0
114
+ }
115
+ .jqte_tool.jqte_tool_15 .jqte_tool_icon {
116
+ background-position:-286px 0
117
+ }
118
+ .jqte_tool.jqte_tool_16 .jqte_tool_icon {
119
+ background-position:-308px 0
120
+ }
121
+ .jqte_tool.jqte_tool_17 .jqte_tool_icon {
122
+ background-position:-330px 0
123
+ }
124
+ .jqte_tool.jqte_tool_18 .jqte_tool_icon {
125
+ background-position:-352px 0
126
+ }
127
+ .jqte_tool.jqte_tool_19 .jqte_tool_icon {
128
+ background-position:-374px 0
129
+ }
130
+ .jqte_tool.jqte_tool_20 .jqte_tool_icon {
131
+ background-position:-396px 0
132
+ }
133
+ .jqte_tool.jqte_tool_21 .jqte_tool_icon {
134
+ background-position:-418px 0
135
+ }
136
+ .jqte_tool:hover {
137
+ background-color:#FFF;
138
+ border-color:#FFF
139
+ }
140
+ .jqte_tool:hover .jqte_tool_icon, .jqte_tool:hover .jqte_tool_label {
141
+ border:#AAA 1px solid
142
+ }
143
+ .jqte_tool:active .jqte_tool_icon, .jqte_tool:active .jqte_tool_label {
144
+ border:#777 1px solid
145
+ }
146
+ .jqte_tool.jqte_tool_1:hover .jqte_tool_icon, .jqte_tool.jqte_tool_1:active .jqte_tool_icon {
147
+ border:none
148
+ }
149
+ .jqte_tool_depressed {
150
+ background-color:#DDD;
151
+ border-color:#CCC
152
+ }
153
+ .jqte_tool_depressed .jqte_tool_icon {
154
+ border-color:#AAA
155
+ }
156
+ .jqte_tool_depressed:hover {
157
+ background-color:#EEE
158
+ }
159
+ /* link form area */
160
+ .jqte_linkform {
161
+ padding:5px 10px;
162
+ background:#DDD;
163
+ border-bottom:#BBB 1px solid;
164
+ }
165
+ .jqte_linktypeselect {
166
+ position:relative;
167
+ float:left;
168
+ width:130px;
169
+ background:#EEE;
170
+ cursor:pointer
171
+ }
172
+ .jqte_linktypeselect:Active {
173
+ background:#FFF
174
+ }
175
+ .jqte_linktypeview {
176
+ padding:3px 5px;
177
+ border:#333 1px solid;
178
+ color:#777;
179
+ border-radius:3px; -webkit-border-radius:3px; -moz-border-radius:3px;
180
+ outline:none
181
+ }
182
+ .jqte_linktypetext {
183
+ font-size:12px;
184
+ }
185
+ .jqte_linktypearrow {
186
+ position:absolute;
187
+ bottom:8px; right:6px;
188
+ width:7px;
189
+ height:6px;
190
+ margin:0 auto;
191
+ background:image-url('lit/jquery-te.png') -7px -23px no-repeat;
192
+ }
193
+ .jqte_linktypes {
194
+ display:none;
195
+ position:absolute;
196
+ top:22px; left:1px;
197
+ width:125px;
198
+ background:#FFF;
199
+ border:#333 1px solid;
200
+ box-shadow:0 1px 4px #AAA; -webkit-box-shadow:0 1px 4px #AAA; -moz-box-shadow:0 1px 4px #AAA
201
+ }
202
+ .jqte_linktypes a {
203
+ display:block;
204
+ padding:4px 5px;
205
+ font-size:12px;
206
+ }
207
+ .jqte_linktypes a:hover {
208
+ background:#DDD
209
+ }
210
+ .jqte_linkinput {
211
+ float:left;
212
+ margin:0 5px;
213
+ padding:3px 5px;
214
+ width:300px;
215
+ background:#EEE;
216
+ border:#333 1px solid;
217
+ color:#777;
218
+ font-size:12px;
219
+ border-radius:3px; -webkit-border-radius:3px; -moz-border-radius:3px;
220
+ outline:none;
221
+ }
222
+ .jqte_linkinput:focus, .jqte_linkinput:hover {
223
+ background:#FFF;
224
+ border-color:#000;
225
+ }
226
+ .jqte_linkbutton {
227
+ float:left;
228
+ padding:3px 12px;
229
+ background:#AAA;
230
+ border:#333 1px solid;
231
+ color:#FFF;
232
+ font-size:12px;
233
+ font-weight:bold;
234
+ cursor:pointer;
235
+ border-radius:3px; -webkit-border-radius:3px; -moz-border-radius:3px;
236
+ box-shadow:inset 0 1px #EEE; -webkit-box-shadow:inset 0 1px #EEE; -moz-box-shadow:inset 0 1px #EEE;
237
+ }
238
+ .jqte_linkbutton:hover {
239
+ background:#A1A1A1
240
+ }
241
+ .jqte_linkbutton:active {
242
+ box-shadow:inset 0 1px #CCC; -webkit-box-shadow:inset 0 1px #CCC; -moz-box-shadow:inset 0 1px #CCC;
243
+ background:#888;
244
+ }
245
+
246
+ /* text format menu */
247
+ .jqte_formats {
248
+ display:none;
249
+ position:absolute;
250
+ width:180px;
251
+ oveflow-x:hidden;
252
+ overflow-y:auto;
253
+ background:#FFF;
254
+ border:#AAA 1px solid;
255
+ box-shadow:0 0 5px #AAA; -webkit-box-shadow:0 0 5px #AAA; -moz-box-shadow:0 0 5px #AAA;
256
+ z-index:100;
257
+ }
258
+ .jqte_format {
259
+ display:block;
260
+ padding:4px 7px;
261
+ font-size:13px
262
+ }
263
+ .jqte_format:hover {
264
+ background:#DDD
265
+ }
266
+ .jqte_format_1, .jqte_format_2, .jqte_format_3, .jqte_format_4, .jqte_format_5, .jqte_format_6 {
267
+ font-weight:bold
268
+ }
269
+ .jqte_format_1 {
270
+ font-size:22px
271
+ }
272
+ .jqte_format_2 {
273
+ font-size:20px
274
+ }
275
+ .jqte_format_3 {
276
+ font-size:18px
277
+ }
278
+ .jqte_format_4 {
279
+ font-size:16px
280
+ }
281
+ .jqte_format_5 {
282
+ font-size:14px
283
+ }
284
+ .jqte_format_6 {
285
+ font-size:12px
286
+ }
287
+ .jqte_format_7 {
288
+ font-family:"Courier New", Courier, monospace
289
+ }
290
+
291
+ /* font size menu */
292
+ .jqte_fontsizes {
293
+ display:none;
294
+ position:absolute;
295
+ width:180px;
296
+ height:198px;
297
+ oveflow-x:hidden;
298
+ overflow-y:auto;
299
+ background:#FFF;
300
+ border:#AAA 1px solid;
301
+ box-shadow:0 0 5px #AAA; -webkit-box-shadow:0 0 5px #AAA; -moz-box-shadow:0 0 5px #AAA
302
+ }
303
+ .jqte_fontsize {
304
+ display:block;
305
+ padding:3px 7px;
306
+ }
307
+ .jqte_fontsize:hover {
308
+ background:#DDD
309
+ }
310
+
311
+ /* color pallette */
312
+ .jqte_cpalette {
313
+ display:none;
314
+ position:absolute;
315
+ padding:6px;
316
+ width:144px;
317
+ background:#FFF;
318
+ border:#AAA 1px solid;
319
+ box-shadow:0 0 5px #AAA; -webkit-box-shadow:0 0 5px #AAA; -moz-box-shadow:0 0 5px #AAA
320
+ }
321
+ .jqte_color {
322
+ display:block;
323
+ float:left;
324
+ width:16px;
325
+ height:16px;
326
+ border:#FFF 1px solid;
327
+ }
328
+ .jqte_color:hover {
329
+ border-color:#000
330
+ }
331
+ .jqte_colorSeperator {
332
+ float:none;
333
+ clear:both;
334
+ height:7px;
335
+ }
336
+
337
+ /* editor area */
338
+ .jqte_editor, .jqte_source {
339
+ padding:10px;
340
+ background:#FFF;
341
+ min-height:100px;
342
+ max-height:900px;
343
+ overflow:auto;
344
+ outline:none;
345
+ word-wrap:break-word; -ms-word-wrap:break-word;
346
+ resize:vertical
347
+ }
348
+ .jqte_editor div, .jqte_editor p {
349
+ margin:0 0 7px
350
+ }
351
+ .jqte_editor a:link, .jqte_editor a:link * {
352
+ color:#0066FF !important;
353
+ text-decoration:underline;
354
+ }
355
+ .jqte_editor blockquote {
356
+ margin-top:0;
357
+ margin-bottom:7px
358
+ }
359
+ .jqte_editor img {
360
+ float:left;
361
+ margin:0 10px 5px 0
362
+ }
363
+ .jqte_editor a[jqte-setlink], .jqte_editor a[jqte-setlink] * {
364
+ background:#3297FD !important;
365
+ color:#FFF !important
366
+ }
367
+ .jqte_editor h1, .jqte_editor h2, .jqte_editor h3, .jqte_editor h4, .jqte_editor h5, .jqte_editor h6, .jqte_editor pre {
368
+ display:block;
369
+ margin:0 0 3px;
370
+ }
371
+ .jqte_editor h1, .jqte_editor h1 * {
372
+ font-size:26px
373
+ }
374
+ .jqte_editor h2, .jqte_editor h2 * {
375
+ font-size:24px
376
+ }
377
+ .jqte_editor h3, .jqte_editor h3 * {
378
+ font-size:22px
379
+ }
380
+ .jqte_editor h4, .jqte_editor h4 * {
381
+ font-size:20px
382
+ }
383
+ .jqte_editor h5, .jqte_editor h5 * {
384
+ font-size:18px
385
+ }
386
+ .jqte_editor h6, .jqte_editor h6 * {
387
+ font-size:15px
388
+ }
389
+ .jqte_editor pre, .jqte_editor pre * {
390
+ font-family:"Courier New", Courier, monospace
391
+ }
392
+
393
+ /* source area */
394
+ .jqte_source, .jqte_source textarea {
395
+ background:#FFF1E8
396
+ }
397
+ .jqte_source textarea {
398
+ margin:0 !important;
399
+ padding:0 !important;
400
+ display:block !important;
401
+ width:100% !important;
402
+ min-height:100px;
403
+ font-family:Courier, Arial, sans-serif !important;
404
+ font-weight:normal;
405
+ font-size:15px;
406
+ overflow:hidden !important;
407
+ outline:none;
408
+ resize:none;
409
+ }
410
+ .jqte_source textarea, .jqte_source textarea:focus {
411
+ css_shadow:none !important;
412
+ background:none !important;
413
+ border:none !important;
414
+ }
415
+
416
+ /* title box */
417
+ .jqte_title {
418
+ display:none;
419
+ position:absolute;
420
+ z-index:9999;
421
+ }
422
+ .jqte_titleArrow {
423
+ position:relative;
424
+ }
425
+ .jqte_titleArrowIcon {
426
+ width:7px;
427
+ height:6px;
428
+ margin:0 auto;
429
+ background:image-url('lit/jquery-te.png') 0 -23px no-repeat;
430
+ }
431
+ .jqte_titleText {
432
+ padding:5px 7px;
433
+ margin-top:0;
434
+ min-width:5px;
435
+ min-height:1px;
436
+ max-width:400px;
437
+ background:#000;
438
+ border-radius:3px; -webkit-border-radius:3px; -moz-border-radius:3px;
439
+ word-wrap:break-word; -ms-word-wrap:break-word
440
+ }
441
+ .jqte_titleText, .jqte_titleText * {
442
+ color:#FFF;
443
+ font-size:11px
444
+ }
445
+
446
+ /* placeholder */
447
+ .jqte_placeholder {
448
+ position:relative;
449
+ display:none;
450
+ }
451
+ .jqte_placeholder_text {
452
+ position:absolute;
453
+ top:43px;
454
+ left:10px;
455
+ font-size:14px;
456
+ color:#CCC;
457
+ }
@@ -36,16 +36,12 @@ module Lit
36
36
  def get_localization_scope
37
37
  @search_options = params.slice(*valid_keys)
38
38
  @search_options[:include_completed] = '1' if @search_options.empty?
39
- @scope = LocalizationKey.uniq.search(@search_options)
39
+ @scope = LocalizationKey.uniq.preload(:localizations => :locale).search(@search_options)
40
40
  end
41
41
 
42
42
  def get_localization_keys
43
- key_parts = if @search_options[:key_prefix].present?
44
- key_parts = @search_options[:key_prefix].split('.').length
45
- else
46
- 0
47
- end
48
- @prefixes = @scope.uniq.pluck(:localization_key).map{|lk| lk.split('.').shift(key_parts+1).join('.') }.uniq.sort
43
+ key_parts = @search_options[:key_prefix].to_s.split('.').length
44
+ @prefixes = @scope.reorder(nil).uniq.pluck(:localization_key).map{|lk| lk.split('.').shift(key_parts+1).join('.') }.uniq.sort
49
45
  if @search_options[:key_prefix].present?
50
46
  parts = @search_options[:key_prefix].split('.')
51
47
  @parent_prefix = parts[0,parts.length-1].join('.')
@@ -58,8 +54,51 @@ module Lit
58
54
  end
59
55
 
60
56
  def valid_keys
61
- %w( key include_completed key_prefix )
57
+ %w( key include_completed key_prefix order )
62
58
  end
63
59
 
60
+ def grouped_localizations
61
+ @_grouped_localizations ||= begin
62
+ {}.tap do |hash|
63
+ @localization_keys.each do |lk|
64
+ hash[lk] = {}
65
+ lk.localizations.each do |l|
66
+ hash[lk][l.locale.locale.to_sym] = l
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def localization_for(locale, localization_key)
74
+ @_localization_for ||= {}
75
+ key = [locale, localization_key]
76
+ ret = @_localization_for[key]
77
+ if ret == false
78
+ nil
79
+ elsif ret.nil?
80
+ ret = grouped_localizations[localization_key][locale]
81
+ unless ret
82
+ Lit.init.cache.refresh_key("#{locale}.#{localization_key.localization_key}")
83
+ ret = localization_key.localizations.where(:locale_id=>Lit.init.cache.find_locale(locale).id).first
84
+ end
85
+ @_localization_for[key] = ret ? ret : false
86
+ else
87
+ ret
88
+ end
89
+
90
+ end
91
+
92
+ helper_method :localization_for
93
+
94
+ def has_versions?(localization)
95
+ @_versions ||= begin
96
+ ids = grouped_localizations.values.map(&:values).flatten.map(&:id)
97
+ Lit::Localization.where(:id => ids).joins(:versions).group("#{Lit::Localization.quoted_table_name}.id").count
98
+ end
99
+ @_versions[localization.id].to_i > 0
100
+ end
101
+
102
+ helper_method :has_versions?
64
103
  end
65
104
  end
@@ -52,8 +52,24 @@ module Lit
52
52
  mark_completed!
53
53
  end
54
54
 
55
+ def self.order_options
56
+ ["localization_key asc", "localization_key desc", "created_at asc", "created_at desc"]
57
+ end
58
+
59
+ # it can be overridden in parent application, for example: {:order => "created_at desc"}
60
+ def self.default_search_options
61
+ {}
62
+ end
63
+
55
64
  def self.search(options={})
56
- s = scoped.ordered
65
+ options = options.reverse_merge(default_search_options)
66
+ s = self
67
+ if options[:order] && order_options.include?(options[:order])
68
+ column, order = options[:order].split(" ")
69
+ s = s.order("#{Lit::LocalizationKey.quoted_table_name}.#{connection.quote_column_name(column)} #{order}" )
70
+ else
71
+ s = s.ordered
72
+ end
57
73
  if options[:key_prefix].present?
58
74
  q = "#{options[:key_prefix]}%"
59
75
  s = s.where('lit_localization_keys.localization_key like ?', q)
@@ -1,4 +1,5 @@
1
1
  <h3><%= t('.header', :default=>"Localization keys") %></h3>
2
+ <% available_locales = I18n.backend.available_locales %>
2
3
 
3
4
  <table class="table">
4
5
  <%- @localization_keys.each do |lk| %>
@@ -16,13 +17,9 @@
16
17
  </div>
17
18
  <div class="detail_wrapper">
18
19
  <table class="table table-bordered table-striped">
19
- <%- I18n.backend.available_locales.each do |locale| %>
20
- <%- localization = lk.localizations.where(:locale_id=>Lit.init.cache.find_locale(locale).id).first %>
20
+ <%- available_locales.each do |locale| %>
21
+ <%- localization = localization_for(locale, lk) %>
21
22
  <tr>
22
- <% unless localization %>
23
- <% Lit.init.cache.refresh_key("#{locale}.#{lk.localization_key}") %>
24
- <%- localization = lk.localizations.where(:locale_id=>Lit.init.cache.find_locale(locale).id).first %>
25
- <% end %>
26
23
  <td class="localization_row" data-id="<%= localization.id%>" data-edit="<%= edit_localization_key_localization_path(lk, localization) %>" data-editing=0 data-content="">
27
24
  <%= render :partial=>"localization_row", :locals=>{:localization=>Lit.init.cache["#{locale}.#{lk.localization_key}"]} %>
28
25
  </td>
@@ -30,7 +27,7 @@
30
27
  <%= image_tag "lit/famfamfam_flags/#{locale[0,2]}.png" %>
31
28
  <%= locale %>
32
29
  <% if localization %>
33
- <%= link_to lit.previous_versions_localization_key_localization_path(lk, localization), :class=>"show_prev_versions #{'hidden' if localization.versions.empty?}", :remote=>true do %>
30
+ <%= link_to lit.previous_versions_localization_key_localization_path(lk, localization), :class=>"show_prev_versions #{'hidden' unless has_versions?(localization)}", :remote=>true do %>
34
31
  <%= draw_icon 'random', :title=>t('lit.common.previous_versions', :default=>"Previous versions") %>
35
32
  <% end %>
36
33
  <% end %>
@@ -78,10 +75,18 @@
78
75
  starred
79
76
  <% end %>
80
77
  </li>
78
+ <li class="nav-header"><%= t(".order_by", :default => "Order by") %>:</li>
79
+ <% Lit::LocalizationKey.order_options.each do |order| %>
80
+ <li class="<%= "active" if order == @search_options[:order] %>">
81
+ <%= link_to url_for(@search_options.merge(:order => order)) do %>
82
+ <%= t("lit.order_options.#{order.gsub(" ", "_")}", :default => order.humanize) %>
83
+ <% end %>
84
+ </li>
85
+ <% end %>
81
86
  <li class="nav-header">Narrow with prefix</li>
82
87
  <% if @search_options[:key_prefix].present? %>
83
88
  <li>
84
- <%= link_to url_for(:key_prefix=>p, :key=>@search_options[:key], :include_completed=>@search_options[:include_completed]), :title=>(@parent_prefix.present? ? @parent_prefix : 'show all') do %>
89
+ <%= link_to url_for(@search_options.merge(:key_prefix=>p)), :title=>(@parent_prefix.present? ? @parent_prefix : 'show all') do %>
85
90
  <%= draw_icon('chevron-left') %>
86
91
  <%= @parent_prefix.present? ? @parent_prefix.split('.').last : 'show all' %>
87
92
  <% end %>
@@ -89,7 +94,7 @@
89
94
  <% end %>
90
95
  <% @prefixes.each do |p| %>
91
96
  <li class="key_prefix">
92
- <%= link_to url_for(:key_prefix=>p, :key=>@search_options[:key], :include_completed=>@search_options[:include_completed]) do %>
97
+ <%= link_to url_for(@search_options.merge(:key_prefix=>p)) do %>
93
98
  <%= draw_icon('chevron-right') %>
94
99
  <%= p.split('.').last %>
95
100
  <% end %>
@@ -8,7 +8,12 @@
8
8
  </ul>
9
9
  <% else %>
10
10
  <%= f.text_area :translated_value, :size=>"40x2", :class=>"input-xxlarge" %>
11
+ <br />
12
+ <label>
13
+ <input type="checkbox" class="wysiwyg_switch" /> use wysiwyg editor
14
+ </label>
11
15
  <% end %>
16
+
12
17
  <button class="btn" type="submit"><%= t('lit.common.update', :default=>"Update") %></button>
13
18
  <button class="btn cancel"><%= t('lit.common.cancel', :default=>'Cancel') %></button>
14
19
  <% end %>
@@ -1,4 +1,7 @@
1
1
  var $row = $('td.localization_row[data-id="<%= @localization.id %>"]');
2
2
  $row.data('editing', 1);
3
3
  $row.html("<%= ejs render(:partial=>'form') %>");
4
- $row.find('textarea').focus();
4
+ $row.find('textarea').focus();
5
+ <% if @localization.full_key.ends_with?('_html') -%>
6
+ $row.find('.wysiwyg_switch').click();
7
+ <% end %>
@@ -30,6 +30,6 @@
30
30
  </div>
31
31
 
32
32
  <div class="actions">
33
- <%= f.submit t('lit.common.save'), :class=>"btn" %>
33
+ <%= f.submit t('lit.common.save', :default => "Save"), :class=>"btn" %>
34
34
  </div>
35
35
  <% end %>
@@ -1,3 +1,21 @@
1
+ module I18n
2
+ class << self
3
+ @@cache_store = nil
4
+
5
+ def cache_store
6
+ @@cache_store || I18n.backend.cache
7
+ end
8
+
9
+ def cache_store=(store)
10
+ @@cache_store = store
11
+ end
12
+
13
+ def perform_caching?
14
+ !cache_store.nil?
15
+ end
16
+ end
17
+ end
18
+
1
19
  module Lit
2
20
  class Cache
3
21
 
@@ -42,6 +60,13 @@ module Lit
42
60
  @localizations[key] = localization.get_value if localization
43
61
  end
44
62
 
63
+ def delete_locale(key)
64
+ key = key.to_s
65
+ locale_key, key_without_locale = split_key(key)
66
+ locale = find_locale(locale_key)
67
+ delete_localization(locale, key_without_locale)
68
+ end
69
+
45
70
  def load_all_translations(oninit=false)
46
71
  doinit = false
47
72
  first = Localization.order('id ASC').first
@@ -83,6 +108,8 @@ module Lit
83
108
  load_all_translations
84
109
  end
85
110
 
111
+ alias_method :clear, :reset
112
+
86
113
  def find_locale(locale_key)
87
114
  locale_key = locale_key.to_s
88
115
  @locale_cache ||= {}
@@ -178,6 +205,15 @@ module Lit
178
205
  end
179
206
  end
180
207
 
208
+ def delete_localization(locale, key_without_locale)
209
+ localization = find_localization(locale, key_without_locale)
210
+ if localization.persisted?
211
+ @localizations.delete("#{locale.locale}.#{key_without_locale}")
212
+ @localization_keys.delete(key_without_locale)
213
+ localization.destroy # or localization.default_value = nil; localization.save!
214
+ end
215
+ end
216
+
181
217
  ## checks parameter type and returns value basing on it
182
218
  ## symbols are beeing looked up in db
183
219
  ## string are returned directly
@@ -196,7 +232,7 @@ module Lit
196
232
  when String then
197
233
  new_value = v
198
234
  when Proc then
199
- new_value = v.call
235
+ new_value = nil # was v.call - requires more love
200
236
  else
201
237
  new_value = v.to_s
202
238
  end
@@ -213,10 +249,7 @@ module Lit
213
249
  end
214
250
 
215
251
  def split_key(key)
216
- key_split = key.split('.')
217
- locale_key = key_split.first
218
- key_without_locale = key_split[1..-1].join('.')
219
- [locale_key, key_without_locale]
252
+ key.split('.', 2)
220
253
  end
221
254
 
222
255
  def find_or_create_localization_key(key_without_locale)
@@ -4,6 +4,8 @@ module Lit
4
4
  class I18nBackend
5
5
  include I18n::Backend::Simple::Implementation
6
6
 
7
+ attr_reader :cache
8
+
7
9
  def initialize(cache)
8
10
  @cache = cache
9
11
  end
@@ -18,7 +20,7 @@ module Lit
18
20
  end
19
21
 
20
22
  def available_locales
21
- Lit::Locale.ordered.visible.all.map{|l| l.locale.to_sym }
23
+ Lit::Locale.ordered.visible.map{|l| l.locale.to_sym }
22
24
  end
23
25
 
24
26
  # Stores the given translations.
@@ -39,6 +41,8 @@ module Lit
39
41
 
40
42
  ## check in cache or in simple backend
41
43
  content = @cache[key_with_locale] || super
44
+ return content if parts.size <= 1
45
+
42
46
  newly_created = false
43
47
  unless @cache.has_key?(key_with_locale)
44
48
  @cache.init_key_with_value(key_with_locale, content)
@@ -59,25 +63,29 @@ module Lit
59
63
  end
60
64
  elsif data.respond_to?(:to_str)
61
65
  key = ([locale] + scope).join('.')
62
- @cache[key] = data
66
+ @cache[key] ||= data
67
+ elsif data.nil?
68
+ key = ([locale] + scope).join('.')
69
+ @cache.delete_locale(key)
63
70
  end
64
71
  end
65
72
 
66
73
  def load_translations(*filenames)
67
- super
68
74
  @cache.load_all_translations
75
+ super
69
76
  end
70
77
 
71
78
  def default(locale, object, subject, options = {})
72
79
  content = super(locale, object, subject, options)
73
80
  if content.respond_to?(:to_str)
74
81
  parts = I18n.normalize_keys(locale, object, options[:scope], options[:separator])
75
- key = parts.join('.')
76
- @cache[key] = content
82
+ if parts.size > 1
83
+ key = parts.join('.')
84
+ @cache[key] = content
85
+ end
77
86
  end
78
87
  content
79
88
  end
80
89
 
81
- attr_reader :cache
82
90
  end
83
91
  end
@@ -1,3 +1,3 @@
1
1
  module Lit
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-25 00:00:00.000000000 Z
12
+ date: 2013-10-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -381,13 +381,16 @@ files:
381
381
  - app/assets/images/lit/famfamfam_flags/za.png
382
382
  - app/assets/images/lit/famfamfam_flags/zm.png
383
383
  - app/assets/images/lit/famfamfam_flags/zw.png
384
+ - app/assets/images/lit/jquery-te.png
384
385
  - app/assets/javascripts/lit/application.js
385
386
  - app/assets/javascripts/lit/bootstrap.js.coffee
386
387
  - app/assets/javascripts/lit/dashboard.js
388
+ - app/assets/javascripts/lit/jquery-te-1.4.0.min.js
387
389
  - app/assets/javascripts/lit/localizations.js.coffee
388
390
  - app/assets/stylesheets/lit/application.css
389
391
  - app/assets/stylesheets/lit/bootstrap_and_overrides.css.scss
390
392
  - app/assets/stylesheets/lit/dashboard.css
393
+ - app/assets/stylesheets/lit/jquery-te-1.4.0.css.scss
391
394
  - app/controllers/lit/api/v1/base_controller.rb
392
395
  - app/controllers/lit/api/v1/locales_controller.rb
393
396
  - app/controllers/lit/api/v1/localization_keys_controller.rb
@@ -430,7 +433,7 @@ files:
430
433
  - app/views/lit/localization_keys/star.js.erb
431
434
  - app/views/lit/localizations/_form.html.erb
432
435
  - app/views/lit/localizations/_previous_versions_rows.html.erb
433
- - app/views/lit/localizations/edit.html.erb
436
+ - app/views/lit/localizations/edit.js.erb
434
437
  - app/views/lit/localizations/previous_versions.js.erb
435
438
  - app/views/lit/localizations/update.js.erb
436
439
  - app/views/lit/sources/_form.html.erb
@@ -464,7 +467,8 @@ files:
464
467
  - Rakefile
465
468
  - README.md
466
469
  homepage: https://github.com/prograils/lit
467
- licenses: []
470
+ licenses:
471
+ - MIT
468
472
  post_install_message:
469
473
  rdoc_options: []
470
474
  require_paths: