diff --git a/horizon/horizon/static/horizon/js/hogan-1.0.5.min.js b/horizon/horizon/static/horizon/js/hogan-1.0.5.min.js new file mode 100644 index 0000000000..038a6fc041 --- /dev/null +++ b/horizon/horizon/static/horizon/js/hogan-1.0.5.min.js @@ -0,0 +1,5 @@ +/** +* @preserve Copyright 2012 Twitter, Inc. +* @license http://www.apache.org/licenses/LICENSE-2.0.txt +*/ +var Hogan={};(function(a){function h(a){return a=String(a===null||a===undefined?"":a),g.test(a)?a.replace(b,"&").replace(c,"<").replace(d,">").replace(e,"'").replace(f,"""):a}a.Template=function j(a,b,c){a&&(this.r=a),this.c=c,this.text=b||""},a.Template.prototype={r:function(a,b,c){return""},v:h,render:function(b,c,d){return this.ri([b],c||{},d)},ri:function(a,b,c){return this.r(a,b,c)},rp:function(a,b,c,d){var e=c[a];return e?(this.c&&typeof e=="string"&&(e=this.c.compile(e)),e.ri(b,c,d)):""},rs:function(a,b,c){var d="",e=a[a.length-1];if(!i(e))return d=c(a,b);for(var f=0;f=0;h--){f=b[h];if(f&&typeof f=="object"&&a in f){e=f[a],g=!0;break}}return g?(!d&&typeof e=="function"&&(e=this.lv(e,b,c)),e):d?!1:""},ho:function(a,b,c,d,e){var f=this.c,g=a.call(b,d,function(a){return f.compile(a,{delimiters:e}).render(b,c)}),h=f.compile(g.toString(),{delimiters:e}).render(b,c);return this.b=h,!1},b:"",ls:function(a,b,c,d,e,f,g){var h=b[b.length-1],i=null;if(!d&&this.c&&a.length>0)return this.ho(a,h,c,this.text.substring(e,f),g);i=a.call(h);if(typeof i=="function"){if(d)return!0;if(this.c)return this.ho(i,h,c,this.text.substring(e,f),g)}return i},lv:function(a,b,c){var d=b[b.length-1],e=a.call(d);return typeof e=="function"&&(e=e.call(d)),e=e.toString(),this.c&&~e.indexOf("{{")?this.c.compile(e).render(d,c):e}};var b=/&/g,c=//g,e=/\'/g,f=/\"/g,g=/[&<>\"\']/,i=Array.isArray||function(a){return Object.prototype.toString.call(a)==="[object Array]"}})(typeof exports!="undefined"?exports:Hogan),function(a){function h(a){a.n.substr(a.n.length-1)==="}"&&(a.n=a.n.substring(0,a.n.length-1))}function i(a){return a.trim?a.trim():a.replace(/^\s*|\s*$/g,"")}function j(a,b,c){if(b.charAt(c)!=a.charAt(0))return!1;for(var d=1,e=a.length;d0){g=a.shift();if(g.tag=="#"||g.tag=="^"||l(g,d))c.push(g),g.nodes=k(a,g.tag,c,d),e.push(g);else{if(g.tag=="/"){if(c.length===0)throw new Error("Closing tag without opener: /"+g.n);f=c.pop();if(g.n!=f.n&&!m(g.n,f.n,d))throw new Error("Nesting error: "+f.n+" vs. "+g.n);return f.end=g.i,e}e.push(g)}}if(c.length>0)throw new Error("missing closing tag: "+c.pop().n);return e}function l(a,b){for(var c=0,d=b.length;c"?b+=t(a[c]):e=="{"||e=="&"?b+=u(a[c].n,p(a[c].n)):e=="\n"?b+=w('"\\n"'+(a.length-1==c?"":" + i")):e=="_v"?b+=v(a[c].n,p(a[c].n)):e===undefined&&(b+=w('"'+o(a[c])+'"'))}return b}function r(a,b,c,d,e,f){return"if(_.s(_."+c+'("'+o(b)+'",c,p,1),'+"c,p,0,"+d+","+e+', "'+f+'")){'+"b += _.rs(c,p,"+'function(c,p){ var b = "";'+q(a)+"return b;});c.pop();}"+'else{b += _.b; _.b = ""};'}function s(a,b,c){return"if (!_.s(_."+c+'("'+o(b)+'",c,p,1),c,p,1,0,0,"")){'+q(a)+"};"}function t(a){return'b += _.rp("'+o(a.n)+'",c,p,"'+(a.indent||"")+'");'}function u(a,b){return"b += (_."+b+'("'+o(a)+'",c,p,0));'}function v(a,b){return"b += (_.v(_."+b+'("'+o(a)+'",c,p,0)));'}function w(a){return"b += "+a+";"}var b=/\S/,c=/\"/g,d=/\n/g,e=/\r/g,f=/\\/g,g={"#":1,"^":2,"/":3,"!":4,">":5,"<":6,"=":7,_v:8,"{":9,"&":10};a.scan=function(c,d){function w(){p.length>0&&(q.push(new String(p)),p="")}function x(){var a=!0;for(var c=t;c"&&(d.indent=q[c].toString()),q.splice(c,1));else b||q.push({tag:"\n"});r=!1,t=q.length}function z(a,b){var c="="+v,d=a.indexOf(c,b),e=i(a.substring(a.indexOf("=",b)+1,d)).split(" ");return u=e[0],v=e[1],d+c.length-1}var e=c.length,f=0,k=1,l=2,m=f,n=null,o=null,p="",q=[],r=!1,s=0,t=0,u="{{",v="}}";d&&(d=d.split(" "),u=d[0],v=d[1]);for(s=0;s"); + form.submit(); + }); + return modal; + }; + + /* Namespace for core functionality related to client-side templating. */ + horizon.templates = { + template_ids: ["#modal_template"], + compiled_templates: {} + }; + + /* Pre-loads and compiles the client-side templates. */ + horizon.templates.compile_templates = function () { + $.each(horizon.templates.template_ids, function (ind, template_id) { + horizon.templates.compiled_templates[template_id] = Hogan.compile($(template_id).text()); + }); + }; + + /* Namespace for core functionality related to modal dialogs. */ + horizon.modals = {}; + + /* Creates a modal dialog from the client-side template. */ + horizon.modals.create = function (title, body, confirm, cancel) { + if (!cancel) { + cancel = "Cancel"; + } + var template = horizon.templates.compiled_templates["#modal_template"], + params = {title: title, body: body, confirm: confirm, cancel: cancel}, + modal = $(template.render(params)).appendTo("body"); + return modal; + }; + return horizon; }; diff --git a/horizon/horizon/templates/horizon/client_side/_modal.html b/horizon/horizon/templates/horizon/client_side/_modal.html new file mode 100644 index 0000000000..678674a22d --- /dev/null +++ b/horizon/horizon/templates/horizon/client_side/_modal.html @@ -0,0 +1,22 @@ +{% extends "horizon/client_side/template.html" %} +{% load horizon %} + +{% block id %}modal_template{% endblock %} + +{% block template %} +{% jstemplate %} + +{% endjstemplate %} +{% endblock %} diff --git a/horizon/horizon/templates/horizon/client_side/template.html b/horizon/horizon/templates/horizon/client_side/template.html new file mode 100644 index 0000000000..1f30fb3230 --- /dev/null +++ b/horizon/horizon/templates/horizon/client_side/template.html @@ -0,0 +1 @@ + diff --git a/horizon/horizon/templatetags/horizon.py b/horizon/horizon/templatetags/horizon.py index 1a812c0d32..5b31f2c9c9 100644 --- a/horizon/horizon/templatetags/horizon.py +++ b/horizon/horizon/templatetags/horizon.py @@ -94,3 +94,25 @@ def horizon_dashboard_nav(context): return {'components': non_empty_panels, 'user': context['request'].user, 'current': context['request'].horizon['panel'].slug} + + +class JSTemplateNode(template.Node): + """ Helper node for the ``jstemplate`` template tag. """ + def __init__(self, nodelist): + self.nodelist = nodelist + + def render(self, context, ): + output = self.nodelist.render(context) + return output.replace('[[', '{{').replace(']]', '}}') + + +@register.tag +def jstemplate(parser, token): + """ + Replaces ``[[`` and ``]]`` with ``{{`` and ``}}`` to avoid conflicts + with Django's template engine when using any of the Mustache-based + templating libraries. + """ + nodelist = parser.parse(('endjstemplate',)) + parser.delete_first_token() + return JSTemplateNode(nodelist) diff --git a/openstack-dashboard/dashboard/templates/base.html b/openstack-dashboard/dashboard/templates/base.html index 5b60dbef9b..e64685f76e 100644 --- a/openstack-dashboard/dashboard/templates/base.html +++ b/openstack-dashboard/dashboard/templates/base.html @@ -21,6 +21,7 @@ + @@ -49,7 +50,8 @@ - {% block footer_js %}{% endblock %} + {% block footer_js %} + {% include "horizon/client_side/_modal.html" %} + {% endblock %} -