metaruby 1.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|