metaruby 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/History.txt +1 -0
- data/Manifest.txt +40 -0
- data/README.md +318 -0
- data/Rakefile +39 -0
- data/lib/metaruby/class.rb +120 -0
- data/lib/metaruby/dsls/doc.rb +78 -0
- data/lib/metaruby/dsls/find_through_method_missing.rb +76 -0
- data/lib/metaruby/dsls.rb +2 -0
- data/lib/metaruby/gui/exception_view.rb +124 -0
- data/lib/metaruby/gui/html/button.rb +65 -0
- data/lib/metaruby/gui/html/collection.rb +103 -0
- data/lib/metaruby/gui/html/exception_view.css +8 -0
- data/lib/metaruby/gui/html/jquery.min.js +154 -0
- data/lib/metaruby/gui/html/jquery.selectfilter.js +65 -0
- data/lib/metaruby/gui/html/jquery.tagcloud.min.js +8 -0
- data/lib/metaruby/gui/html/jquery.tinysort.min.js +8 -0
- data/lib/metaruby/gui/html/list.rhtml +24 -0
- data/lib/metaruby/gui/html/page.css +55 -0
- data/lib/metaruby/gui/html/page.rb +283 -0
- data/lib/metaruby/gui/html/page.rhtml +13 -0
- data/lib/metaruby/gui/html/page_body.rhtml +17 -0
- data/lib/metaruby/gui/html/rock-website.css +694 -0
- data/lib/metaruby/gui/html.rb +16 -0
- data/lib/metaruby/gui/model_browser.rb +262 -0
- data/lib/metaruby/gui/model_selector.rb +266 -0
- data/lib/metaruby/gui/rendering_manager.rb +112 -0
- data/lib/metaruby/gui/ruby_constants_item_model.rb +253 -0
- data/lib/metaruby/gui.rb +9 -0
- data/lib/metaruby/inherited_attribute.rb +482 -0
- data/lib/metaruby/module.rb +158 -0
- data/lib/metaruby/registration.rb +157 -0
- data/lib/metaruby/test.rb +79 -0
- data/lib/metaruby.rb +17 -0
- data/manifest.xml +12 -0
- data/test/suite.rb +15 -0
- data/test/test_attributes.rb +323 -0
- data/test/test_class.rb +68 -0
- data/test/test_dsls.rb +49 -0
- data/test/test_module.rb +105 -0
- data/test/test_registration.rb +182 -0
- metadata +160 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
/*
|
2
|
+
*
|
3
|
+
* Plugin created by Andras Zoltan-Gyarfas - azolee [at] gmail [dot] com
|
4
|
+
* License: GNU License - http://www.gnu.org/licenses/gpl.html
|
5
|
+
* Demo & latest release: http://realizare-site-web.ro/works/codes/jquery/HTML-Select-List-Filter/index.html
|
6
|
+
* Last modification date: 04/20/2011
|
7
|
+
*
|
8
|
+
*/
|
9
|
+
|
10
|
+
;(function($) {
|
11
|
+
$.fn.selectFilter = function() {
|
12
|
+
var name = $(this).attr("name").replace(/\]/g, '').replace(/\[/g, '');
|
13
|
+
$(this).addClass(name+"_select");
|
14
|
+
var iname = name;
|
15
|
+
$(this).before("<input class='"+iname+"' style='display: block;' type='text' />");
|
16
|
+
$(this).css("display", "block");
|
17
|
+
$("input."+name).live("keyup", function(){
|
18
|
+
var txt = $(this).val().toLowerCase();
|
19
|
+
|
20
|
+
var fields=txt.split(' ');
|
21
|
+
var text = new Array();
|
22
|
+
var tags = new Array();
|
23
|
+
var tag_matcher = new RegExp("^tag:");
|
24
|
+
for (var i = 0; i < fields.length; ++i) {
|
25
|
+
var el = fields[i];
|
26
|
+
if (tag_matcher.test(el)) {
|
27
|
+
el = el.split(':');
|
28
|
+
tags.push(new RegExp(el[1]));
|
29
|
+
}
|
30
|
+
else if (el.length != 0) {
|
31
|
+
text.push(new RegExp(el));
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
if (text.length == 0 && tags.length == 0) {
|
36
|
+
$("."+name+"_select").children('div').show();
|
37
|
+
}
|
38
|
+
else {
|
39
|
+
$("."+name+"_select").children('div').hide();
|
40
|
+
$("."+name+"_select").children('div').each(function(i, selected){
|
41
|
+
var value = $(this).text().toLowerCase();
|
42
|
+
var show = true;
|
43
|
+
for (var i = 0; show && i < text.length; ++i) {
|
44
|
+
if (!text[i].test(value)) {
|
45
|
+
show = false;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
var value = $(this).attr('tags');
|
49
|
+
if (value) {
|
50
|
+
value = value.toLowerCase();
|
51
|
+
for (var i = 0; show && i < tags.length; ++i) {
|
52
|
+
if (!tags[i].test(value)) {
|
53
|
+
show = false;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
if (show) {
|
58
|
+
$(selected).show();
|
59
|
+
}
|
60
|
+
});
|
61
|
+
}
|
62
|
+
});
|
63
|
+
return this;
|
64
|
+
};
|
65
|
+
})(jQuery);
|
@@ -0,0 +1,8 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery TagCloud 0.5.0
|
3
|
+
* Copyright (c) 2008 Ron Valstar
|
4
|
+
* Dual licensed under the MIT and GPL licenses:
|
5
|
+
* http://www.opensource.org/licenses/mit-license.php
|
6
|
+
* http://www.gnu.org/licenses/gpl.html
|
7
|
+
*/
|
8
|
+
(function(F){var C;var A={};var G={};var L=2.399963;F.tagcloud={id:"TagCloud",version:"0.5.0",defaults:{height:null,type:"cloud",sizemax:20,sizemin:10,colormax:"00F",colormin:"B4D2FF",seed:null,power:0.5}};F.fn.extend({tagcloud:function(M){C=F.extend({},F.tagcloud.defaults,M);if(C.seed===null){C.seed=Math.ceil(Math.random()*45309714203)}switch(C.type){case"sphere":case"cloud":A={position:"relative"};G={position:"absolute",display:"block"};break;case"list":A={height:"auto"};G={position:"static",display:"inline"};break}B.setSeed(C.seed+123456);return this.each(function(f,a){var q=F(a);var R=q.find(">li");var S=R.length;var Z=q.width();var l=C.height===null?(0.004*Z*S):C.height;q.css({width:Z,height:l,listStyle:"none",margin:0,padding:0});q.css(A);var e=-2147483647;var r=2147483648;var T=-1;for(var d=0;d<S;d++){var p=F(R[d]);var n=p.attr("value")==-1?T++:p.attr("value");if(n>e){e=n}if(n<r){r=n}T=n}var b=e-r;var X=new Array();for(var d=0;d<S;d++){X[d]=d}for(var d,V,c=X.length;c;d=parseInt(B.rand(0,1000)/1000*c),V=X[--c],X[c]=X[d],X[d]=V){}T=-1;for(var d=0;d<S;d++){var p=F(R[d]);var n=p.attr("value")==-1?T++:p.attr("value");T=n;var g=((S-d-1)/(S-1));var g=(n-r)/b;var m=C.sizemin+g*(C.sizemax-C.sizemin);var N=D(C.colormin,C.colormax,g);p.css({fontSize:m,position:"absolute",color:"#"+N,margin:0,padding:0}).children().css({color:"#"+N});var Y=p.width();var h=p.height();var Q={};if(C.type!="list"){if(C.type=="cloud"){var s=B.rand(0,Z-Y);var W=X[d]*(l/S)-h/2}else{var P=Math.pow(d/S,C.power);var U=(d+Math.PI/2)*L;var s=Z/2-Y/2+0.5*Z*P*Math.sin(U);var W=l/2-h/2+0.5*l*P*Math.cos(U)}Q.left=s;Q.top=W}for(var O in G){Q[O]=G[O]}p.css(Q)}})}});var B=new function(){this.seed=23145678901;this.A=48271;this.M=2147483647;this.Q=this.M/this.A;this.R=this.M%this.A;this.oneOverM=1/this.M};B.setSeed=function(M){this.seed=M};B.next=function(){var M=this.seed/this.Q;var N=this.seed%this.Q;var O=this.A*N-this.R*M;this.seed=O+(O>0?0:this.M);return(this.seed*this.oneOverM)};B.rand=function(N,M){return Math.floor((M-N+1)*this.next()+N)};function I(M){return M.toString(16)}function K(M){return parseInt(M,16)}function H(Q){var M=Q.length==3;var O=[];for(var N=0;N<3;N++){var P=Q.substring(N*(M?1:2),(N+1)*(M?1:2));O.push(K(M?P+P:P))}return O}function J(M){var O="";for(var N=0;N<3;N++){var P=I(M[N]);if(P.length==1){P="0"+P}O+=P}return O}function D(R,S,P){var Q=H(R);var M=H(S);var O=[];for(var N=0;N<3;N++){O.push(Q[N]+Math.floor(P*(M[N]-Q[N])))}return J(O)}function E(M){if(window.console&&window.console.log){if(typeof (M)=="string"){window.console.log(M)}else{for(var N in M){window.console.log(N+": "+M[N])}}}}F.fn.TagCloud=F.fn.Tagcloud=F.fn.tagcloud})(jQuery);
|
@@ -0,0 +1,8 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery TinySort 1.0.1
|
3
|
+
* Copyright (c) 2008 Ron Valstar
|
4
|
+
* Dual licensed under the MIT and GPL licenses:
|
5
|
+
* http://www.opensource.org/licenses/mit-license.php
|
6
|
+
* http://www.gnu.org/licenses/gpl.html
|
7
|
+
*/
|
8
|
+
(function(C){C.tinysort={id:"TinySort",version:"1.0.1",defaults:{order:"asc",attr:"",place:"start",returns:false}};C.fn.extend({tinysort:function(I,J){if(I&&typeof (I)!="string"){J=I;I=null}var F=C.extend({},C.tinysort.defaults,J);var P={};this.each(function(T){var V=(!I||I=="")?C(this):C(this).find(I);var U=F.order=="rand"?""+Math.random():(F.attr==""?V.text():V.attr(F.attr));var S=C(this).parent();if(!P[S]){P[S]={s:[],n:[]}}if(V.length>0){P[S].s.push({s:U,e:C(this),n:T})}else{P[S].n.push({e:C(this),n:T})}});for(var H in P){var E=P[H];E.s.sort(function K(U,T){var S=U.s.toLowerCase?U.s.toLowerCase():U.s;var V=T.s.toLowerCase?T.s.toLowerCase():T.s;if(D(U.s)&&D(T.s)){S=parseFloat(U.s);V=parseFloat(T.s)}return(F.order=="asc"?1:-1)*(S<V?-1:(S>V?1:0))})}var M=[];for(var H in P){var E=P[H];var N=[];var G=C(this).length;switch(F.place){case"first":C.each(E.s,function(S,T){G=Math.min(G,T.n)});break;case"org":C.each(E.s,function(S,T){N.push(T.n)});break;case"end":G=E.n.length;break;default:G=0}var Q=[0,0];for(var L=0;L<C(this).length;L++){var O=L>=G&&L<G+E.s.length;if(A(N,L)){O=true}var R=(O?E.s:E.n)[Q[O?0:1]].e;R.parent().append(R);if(O||!F.returns){M.push(R.get(0))}Q[O?0:1]++}}return this.setArray(M)}});function D(E){return(parseFloat(E)+"")==E}function A(F,G){var E=false;C.each(F,function(I,H){if(!E){E=H==G}});return E}function B(E){if(window.console&&window.console.log){if(typeof (E)=="string"){window.console.log(E)}else{for(var F in E){window.console.log(F+": "+E[F])}}}}C.fn.TinySort=C.fn.Tinysort=C.fn.tsort=C.fn.tinysort})(jQuery);
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<% if options[:filter] %>
|
2
|
+
<script type="text/javascript">
|
3
|
+
jQuery(document).ready(function(){
|
4
|
+
jQuery("div#<%= push_options[:id] %>-list").selectFilter();
|
5
|
+
});
|
6
|
+
</script>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<div name="index_filter" id="<%= push_options[:id] %>-list">
|
10
|
+
<%
|
11
|
+
items.each_with_index do |(data, attributes), index|
|
12
|
+
if attributes
|
13
|
+
div_attributes = attributes.map { |k, v| " \#{k}=\"\#{v}\"" }.join("")
|
14
|
+
end
|
15
|
+
%>
|
16
|
+
<div class="short_doc<%= " list_alt" if index % 2 == 0 %>"<%= div_attributes %>>
|
17
|
+
<% if data.respond_to?(:to_ary) %>
|
18
|
+
<% data = data.join("\n") %>
|
19
|
+
<% end %>
|
20
|
+
<%= data %>
|
21
|
+
</div>
|
22
|
+
<% end %>
|
23
|
+
</div>
|
24
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
body {font-family: arial, sans-serif; }
|
2
|
+
h1 {margin: 40px 0px 10px 0px; padding: 0px 0px 2px 0px; color: rgb(88,144,168); font-weight: bold; -size: 200%;}
|
3
|
+
h2 {width: 500px; margin: 40px 0px 10px 0px; padding: 0px 0px 2px 0px; border-bottom: solid 3px rgb(88,144,168); color: rgb(88,144,168); font-weight: bold; font-size: 160%;}
|
4
|
+
table {clear: left; margin: 0.5em 0px 0.2em 30px; border: solid 1px rgb(150,150,150); empty-cells: show; border-collapse: collapse; background-color: rgb(233,232,244);}
|
5
|
+
table tr {margin: 0px; padding: 0px;}
|
6
|
+
table td {padding: 2px 7px 2px 7px; border: solid 1px rgb(150,150,150); text-align: left; }
|
7
|
+
table th {margin: 0px; padding: 2px 7px 2px 7px; border: solid 1px rgb(150,150,150); empty-cells: show; text-align: left; }
|
8
|
+
table-caption {margin: 0.5em 0px 1.0em 30px; padding: 0px; clear: both; text-align: left; }
|
9
|
+
|
10
|
+
.button_bar {margin: 0px; padding: 0px; font-family: verdana, arial, sans serif; font-size: 1.0em;}
|
11
|
+
.button_bar span {margin-left: .1em; padding: 2px 5px 2px 5px; background-color: rgb(255,255,255); font-weight: normal; font-size: .9em; }
|
12
|
+
.button_bar span em {font-weight: bold; font-style: normal;}
|
13
|
+
.button_bar a {color: rgb(115,162,182); text-decoration: none; font-size: .9em; font-weight: normal;}
|
14
|
+
.button_bar a:hover {color: rgb(50,50,50); text-decoration: none;}
|
15
|
+
|
16
|
+
.body-header-list {margin: 0em 2em 1em 0em; padding: 0 0 0 0; background-color: rgb(238,237,249); font-size: 1.0em;}
|
17
|
+
.body-header-list ul {margin: 5px 0px 0px 0px; padding: 0px; border-bottom: solid 1px rgb(216,206,159); background-color: rgb(219,230,241);}
|
18
|
+
.body-header-list li {list-style: none; margin: 0px; padding: 0px;display: block; min-height: 1.7em; height: auto !important; height: 1.7em; line-height: 1.7em; margin: 0px; padding: 0px 7px 0px 10px; border-top: solid 1px rgb(200,200,200); border-left: solid 7px rgb(219,230,241); color: rgb(75,75,75); font-weight: normal; font-size: 100%; text-decoration: none;}
|
19
|
+
.body-header-list li.title {margin: 0px 0px 0px 0px; padding: 3px 5px 2px 15px; background-color: rgb(156,186,214); color: rgb(255,255,255); text-transform: uppercase; font-weight: bold;}
|
20
|
+
|
21
|
+
.body-header-list li table {clear: left; margin: 0em 0px 0em 25px; empty-cells: show; border-collapse: collapse; }
|
22
|
+
.body-header-list li table tr {margin: 0px; padding: 0px;}
|
23
|
+
.body-header-list li table td {margin: 0px; padding: 0px 7px 0px 7px; border: none; text-align: left;}
|
24
|
+
.body-header-list li table th {margin: 0px; padding: 0px; border: none; empty-cells: show; text-align: left; }
|
25
|
+
|
26
|
+
code {
|
27
|
+
padding:5px;
|
28
|
+
background:#e4e4ff;
|
29
|
+
color:#222;
|
30
|
+
border:2px solid #ddd;
|
31
|
+
margin-top: 1em;
|
32
|
+
margin-bottom: 1em;
|
33
|
+
margin-left: 0em;
|
34
|
+
margin-right: 0em;
|
35
|
+
font-family: monospace;
|
36
|
+
white-space: pre;
|
37
|
+
display: block;
|
38
|
+
}
|
39
|
+
/* Style for the main documentation block for a page */
|
40
|
+
.doc-main { font-size: 130% }
|
41
|
+
.doc-above { margin-bottom: -.3em; padding-bottom: 0em; font-style: italic; }
|
42
|
+
.doc-above p { margin-top: .1em; margin-bottom: .1em; }
|
43
|
+
|
44
|
+
div.short_doc {
|
45
|
+
margin-top: .2em;
|
46
|
+
background-color: rgb(238,237,249);
|
47
|
+
padding: .2em;
|
48
|
+
width: 100%;
|
49
|
+
}
|
50
|
+
div.short_doc td.short_doc {
|
51
|
+
font-style: italic;
|
52
|
+
}
|
53
|
+
div.list_alt {
|
54
|
+
background-color: rgb(230,228,249);
|
55
|
+
}
|
@@ -0,0 +1,283 @@
|
|
1
|
+
module MetaRuby::GUI
|
2
|
+
module HTML
|
3
|
+
RESSOURCES_DIR = File.expand_path(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
# A class that can be used as the webpage container for the Page class
|
6
|
+
class HTMLPage
|
7
|
+
attr_accessor :html
|
8
|
+
|
9
|
+
def main_frame; self end
|
10
|
+
end
|
11
|
+
|
12
|
+
# A helper class that gives us easy-to-use page elements on a
|
13
|
+
# Qt::WebView
|
14
|
+
class Page < Qt::Object
|
15
|
+
attr_reader :fragments
|
16
|
+
attr_reader :view
|
17
|
+
attr_accessor :object_uris
|
18
|
+
|
19
|
+
class Fragment
|
20
|
+
attr_accessor :title
|
21
|
+
attr_accessor :html
|
22
|
+
attr_accessor :id
|
23
|
+
attr_reader :buttons
|
24
|
+
|
25
|
+
def initialize(title, html, view_options = Hash.new)
|
26
|
+
view_options = Kernel.validate_options view_options,
|
27
|
+
:id => nil, :buttons => []
|
28
|
+
@title = title
|
29
|
+
@html = html
|
30
|
+
@id = view_options[:id]
|
31
|
+
@buttons = view_options[:buttons]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def link_to(object, text = nil)
|
36
|
+
text = HTML.escape_html(text || object.name)
|
37
|
+
if uri = uri_for(object)
|
38
|
+
if uri[0, 1] != '/'
|
39
|
+
uri = "/#{uri}"
|
40
|
+
end
|
41
|
+
"<a href=\"link://metaruby#{uri}\">#{text}</a>"
|
42
|
+
else text
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Converts the given text from markdown to HTML and generates the
|
47
|
+
# necessary <div> context.
|
48
|
+
#
|
49
|
+
# @return [String] the HTML snippet that should be used to render
|
50
|
+
# the given text as main documentation
|
51
|
+
def self.main_doc(text)
|
52
|
+
"<div class=\"doc-main\">#{Kramdown::Document.new(text).to_html}</div>"
|
53
|
+
end
|
54
|
+
|
55
|
+
def main_doc(text)
|
56
|
+
self.class.main_doc(text)
|
57
|
+
end
|
58
|
+
|
59
|
+
PAGE_TEMPLATE = File.join(RESSOURCES_DIR, "page.rhtml")
|
60
|
+
PAGE_BODY_TEMPLATE = File.join(RESSOURCES_DIR, "page_body.rhtml")
|
61
|
+
LIST_TEMPLATE = File.join(RESSOURCES_DIR, "list.rhtml")
|
62
|
+
ASSETS = %w{page.css jquery.min.js jquery.selectfilter.js}
|
63
|
+
|
64
|
+
def self.copy_assets_to(target_dir, assets = ASSETS)
|
65
|
+
FileUtils.mkdir_p target_dir
|
66
|
+
assets.each do |file|
|
67
|
+
FileUtils.cp File.join(RESSOURCES_DIR, file), target_dir
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def load_template(*path)
|
72
|
+
path = File.join(*path)
|
73
|
+
@templates[path] ||= ERB.new(File.read(path))
|
74
|
+
@templates[path].filename = path
|
75
|
+
@templates[path]
|
76
|
+
end
|
77
|
+
|
78
|
+
attr_reader :page
|
79
|
+
|
80
|
+
attr_accessor :page_name
|
81
|
+
attr_accessor :title
|
82
|
+
|
83
|
+
def initialize(page)
|
84
|
+
super()
|
85
|
+
@page = page
|
86
|
+
@fragments = []
|
87
|
+
@templates = Hash.new
|
88
|
+
@auto_id = 0
|
89
|
+
|
90
|
+
if page.kind_of?(Qt::WebPage)
|
91
|
+
page.link_delegation_policy = Qt::WebPage::DelegateAllLinks
|
92
|
+
Qt::Object.connect(page, SIGNAL('linkClicked(const QUrl&)'), self, SLOT('pageLinkClicked(const QUrl&)'))
|
93
|
+
end
|
94
|
+
@object_uris = Hash.new
|
95
|
+
end
|
96
|
+
|
97
|
+
def uri_for(object)
|
98
|
+
object_uris[object]
|
99
|
+
end
|
100
|
+
|
101
|
+
# Removes all existing displays
|
102
|
+
def clear
|
103
|
+
page.main_frame.html = ""
|
104
|
+
fragments.clear
|
105
|
+
end
|
106
|
+
|
107
|
+
def scale_attribute(node, name, scale)
|
108
|
+
node.attributes[name] = node.attributes[name].gsub /[\d\.]+/ do |n|
|
109
|
+
(Float(n) * scale).to_s
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def update_html
|
114
|
+
page.main_frame.html = html
|
115
|
+
end
|
116
|
+
|
117
|
+
def html(options = Hash.new)
|
118
|
+
options = Kernel.validate_options options, :ressource_dir => RESSOURCES_DIR
|
119
|
+
ressource_dir = options[:ressource_dir]
|
120
|
+
load_template(PAGE_TEMPLATE).result(binding)
|
121
|
+
end
|
122
|
+
|
123
|
+
def html_body(options = Hash.new)
|
124
|
+
options = Kernel.validate_options options, :ressource_dir => RESSOURCES_DIR
|
125
|
+
ressource_dir = options[:ressource_dir]
|
126
|
+
load_template(PAGE_BODY_TEMPLATE).result(binding)
|
127
|
+
end
|
128
|
+
|
129
|
+
def find_button_by_url(url)
|
130
|
+
id = url.path
|
131
|
+
fragments.each do |fragment|
|
132
|
+
if result = fragment.buttons.find { |b| b.id == id }
|
133
|
+
return result
|
134
|
+
end
|
135
|
+
end
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
|
139
|
+
def find_first_element(selector)
|
140
|
+
page.main_frame.find_first_element(selector)
|
141
|
+
end
|
142
|
+
|
143
|
+
def pageLinkClicked(url)
|
144
|
+
return if url.host != 'metaruby'
|
145
|
+
|
146
|
+
if url.scheme == 'btn'
|
147
|
+
if btn = find_button_by_url(url)
|
148
|
+
new_state = if url.fragment == 'on' then true
|
149
|
+
else false
|
150
|
+
end
|
151
|
+
|
152
|
+
btn.state = new_state
|
153
|
+
new_text = btn.text
|
154
|
+
element = find_first_element("a##{btn.html_id}")
|
155
|
+
element.replace(btn.render)
|
156
|
+
|
157
|
+
emit buttonClicked(btn.id, new_state)
|
158
|
+
else
|
159
|
+
MetaRuby.warn "invalid button URI #{url}: could not find corresponding handler"
|
160
|
+
end
|
161
|
+
elsif url.scheme == 'link'
|
162
|
+
emit linkClicked(url)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
slots 'pageLinkClicked(const QUrl&)'
|
166
|
+
signals 'linkClicked(const QUrl&)', 'buttonClicked(const QString&,bool)'
|
167
|
+
|
168
|
+
# Save the current state of the page, so that it can be restored by
|
169
|
+
# calling {restore}
|
170
|
+
def save
|
171
|
+
@saved_state = fragments.map(&:dup)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Restore the page at the state it was at the last call to {save}
|
175
|
+
def restore
|
176
|
+
return if !@saved_state
|
177
|
+
|
178
|
+
fragments_by_id = Hash.new
|
179
|
+
@saved_state.each do |fragment|
|
180
|
+
fragments_by_id[fragment.id] = fragment
|
181
|
+
end
|
182
|
+
|
183
|
+
# Delete all fragments that are not in the saved state
|
184
|
+
fragments.delete_if do |fragment|
|
185
|
+
element = find_first_element("div##{fragment.id}")
|
186
|
+
if old_fragment = fragments_by_id[fragment.id]
|
187
|
+
if old_fragment.html != fragment.html
|
188
|
+
element.replace(old_fragment.html)
|
189
|
+
end
|
190
|
+
else
|
191
|
+
element.replace("")
|
192
|
+
true
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Adds a fragment to this page, with the given title and HTML
|
198
|
+
# content
|
199
|
+
#
|
200
|
+
# The added fragment is enclosed in a div block to allow for dynamic
|
201
|
+
# replacement
|
202
|
+
#
|
203
|
+
# @option view_options [String] id the ID of the fragment. If given,
|
204
|
+
# and if an existing fragment with the same ID exists, the new
|
205
|
+
# fragment replaces the existing one, and the view is updated
|
206
|
+
# accordingly.
|
207
|
+
#
|
208
|
+
def push(title, html, view_options = Hash.new)
|
209
|
+
if id = view_options[:id]
|
210
|
+
# Check whether we should replace the existing content or
|
211
|
+
# push it new
|
212
|
+
fragment = fragments.find do |fragment|
|
213
|
+
fragment.id == id
|
214
|
+
end
|
215
|
+
if fragment
|
216
|
+
fragment.html = html
|
217
|
+
element = find_first_element("div##{fragment.id}")
|
218
|
+
element.replace("<div id=\"#{id}\">#{html}</div>")
|
219
|
+
return
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
fragments << Fragment.new(title, html, Hash[:id => auto_id].merge(view_options))
|
224
|
+
update_html
|
225
|
+
end
|
226
|
+
|
227
|
+
def auto_id
|
228
|
+
"metaruby-html-page-fragment-#{@auto_id += 1}"
|
229
|
+
end
|
230
|
+
|
231
|
+
# Create an item for the rendering in tables
|
232
|
+
def render_item(name, value = nil)
|
233
|
+
if value
|
234
|
+
"<li><b>#{name}</b>: #{value}</li>"
|
235
|
+
else
|
236
|
+
"<li>#{name}</li>"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Render a list of objects into HTML and push it to this page
|
241
|
+
#
|
242
|
+
# @param [String,nil] title the section's title. If nil, no new
|
243
|
+
# section is created
|
244
|
+
# @param [Array<Object>,Array<(Object,Hash)>] items the list
|
245
|
+
# items, one item per line. If a hash is provided, it is used as
|
246
|
+
# HTML attributes for the lines
|
247
|
+
# @param [Hash] options
|
248
|
+
# @option options [Boolean] filter (false) if true, a filter is
|
249
|
+
# added at the top of the page. You must provide a :id option for
|
250
|
+
# the list for this to work
|
251
|
+
# @option (see #push)
|
252
|
+
def render_list(title, items, options = Hash.new)
|
253
|
+
options, push_options = Kernel.filter_options options, :filter => false, :id => nil
|
254
|
+
if options[:filter] && !options[:id]
|
255
|
+
raise ArgumentError, ":filter is true, but no :id has been given"
|
256
|
+
end
|
257
|
+
html = load_template(LIST_TEMPLATE).result(binding)
|
258
|
+
push(title, html, push_options.merge(:id => options[:id]))
|
259
|
+
end
|
260
|
+
|
261
|
+
signals 'updated()'
|
262
|
+
|
263
|
+
def self.to_html_page(object, renderer, options = Hash.new)
|
264
|
+
webpage = HTMLPage.new
|
265
|
+
page = new(webpage)
|
266
|
+
renderer.new(page).render(object, options)
|
267
|
+
page
|
268
|
+
end
|
269
|
+
|
270
|
+
# Renders an object to HTML using a given rendering class
|
271
|
+
def self.to_html(object, renderer, options = Hash.new)
|
272
|
+
html_options, options = Kernel.filter_options options, :ressource_dir => RESSOURCES_DIR
|
273
|
+
to_html_page(object, renderer, options).html(html_options)
|
274
|
+
end
|
275
|
+
|
276
|
+
def self.to_html_body(object, renderer, options = Hash.new)
|
277
|
+
html_options, options = Kernel.filter_options options, :ressource_dir => RESSOURCES_DIR
|
278
|
+
to_html_page(object, renderer, options).html_body(html_options)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<% prefix = if ressource_dir[0, 1] == "/" then "file://"
|
2
|
+
end
|
3
|
+
%>
|
4
|
+
<html>
|
5
|
+
<link rel="stylesheet" href="<%= prefix %><%= File.join(ressource_dir, 'page.css') %>" type="text/css" />
|
6
|
+
<script type="text/javascript" src="<%= prefix %><%= File.join(ressource_dir, 'jquery.min.js') %>"></script>
|
7
|
+
<script type="text/javascript" src="<%= prefix %><%= File.join(ressource_dir, 'jquery.selectfilter.js') %>"></script>
|
8
|
+
<title><%= page_name %></title>
|
9
|
+
</html>
|
10
|
+
<body>
|
11
|
+
<%= html_body(:ressource_dir => ressource_dir) %>
|
12
|
+
</body>
|
13
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<% if title %>
|
2
|
+
<h1><%= title %></h1>
|
3
|
+
<% end %>
|
4
|
+
<% fragments.each do |fragment| %>
|
5
|
+
<% if fragment.title %>
|
6
|
+
<h2><%= fragment.title %></h2>
|
7
|
+
<% end %>
|
8
|
+
<%= HTML.render_button_bar(fragment.buttons) %>
|
9
|
+
<% if fragment.id %>
|
10
|
+
<div id="<%= fragment.id %>">
|
11
|
+
<% end %>
|
12
|
+
<%= fragment.html %>
|
13
|
+
<% if fragment.id %>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
16
|
+
<% end %>
|
17
|
+
|