2 Pitchfork Music Player Daemon Client
3 Copyright (C) 2007 Roger Bystrøm
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Why reinvent the wheel when you can make a hexadecagon instead -- Me
25 * Reinventing the wheel to run myself over -- FOB
28 var moveables = new Array();
31 var sliders = new Array();
34 var overlay = new Array();
35 var overlay_adjust = 100; // px
36 var open_overlay_idx = -1;
38 var overlay_hide_on_resize = true; // hide content on resize?
42 function debug(str, force) {
44 /* switch && false to true on release */
45 if(typeof(force)=="undefined" && true) {
49 var d = document.getElementById('debugbox');
51 d = create_node("div");
53 document.body.appendChild(d);
56 var n = create_node("p", null, str);
57 n.style.padding = "0px";
58 n.style.margin = "0px";
61 scrollIntoView(d.lastChild);
71 /* evaluates json response and returns object containing information */
72 function evaluate_json(txt) {
73 var obj = new Object();
80 function browser_is_opera() {
81 return window.opera?true:false;
83 function browser_is_konqueror() {
84 if(navigator.vendor=="KDE")
89 // replaces rem with ins
90 function replace_node(ins, rem) {
91 return rem.parentNode.replaceChild(ins, rem);
93 // removes this nodes children
94 function remove_children(what) {
95 if(!what.hasChildNodes)
97 var buf = create_fragment();
98 while(what.hasChildNodes())
99 buf.appendChild(what.removeChild(what.firstChild));
103 function remove_node(what) {
105 return what.parentNode.removeChild(what);
109 function remove_children_after(container, after) {
110 /* removing elements outside list */
111 var c = container.childNodes;
114 var node = c[parseInt(after)];
116 var t = node.nextSibling;
117 container.removeChild(node);
122 /* insert 'node' first on 'at' */
123 function insert_first(node, at) {
124 if(at.hasChildNodes())
125 at.insertBefore(node, at.firstChild);
126 else at.appendChild(node);
129 function insert_before(what, before) {
130 before.parentNode.insertBefore(what, before);
133 function insert_after(what, after) {
134 var p = after.parentNode;
135 if(after.nextSibling)
136 p.insertBefore(what, after.nextSibling);
137 else p.appendChild(what);
140 function get_node_content(node) {
142 if(node.hasChildNodes && node.hasChildNodes())
143 node = node.childNodes[0];
146 return node.nodeValue;
148 else if(node.textContent) {
149 return node.textContent;
151 else if(node.textValue) {
152 return node.textValue;
162 /*Returns content of first node with specified tag
164 function get_tag(item, tag) {
165 if(item.getElementsByTagName) {
166 var tmp = item.getElementsByTagName(tag)[0];
168 return get_node_content(tmp);
173 function get_absolute_top(node) {
175 while(node.offsetParent != null) {
176 node = node.offsetParent;
178 height += node.offsetTop;
183 // returns the absolute distance to start of page
184 function get_absolute_height(node) {
185 return node.offsetTop + get_absolute_top(node);
188 function get_absolute_left(node) {
189 var left = node.offsetLeft;
190 while(node.offsetParent != null) {
191 node = node.offsetParent;
192 if(node.offsetLeft) {
193 left += node.offsetLeft;
199 function EventListener(to, type, func) {
205 EventListener.prototype.register = function() {
206 if( this.to.addEventListener ) {
207 this.to.addEventListener(this.type, this.func, false);
209 else if(this.to.attachEvent) {
210 this.to.attachEvent(this.type, this.func);
212 else debug("unable to add listener");
215 EventListener.prototype.unregister = function() {
216 if(this.to.removeEventListener) {
217 this.to.removeEventListener(this.type, this.func, false);
219 else if(to.detachEvent) {
220 this.to.detachEvent(this.type, this.func);
222 else debug("unable to detach event");
226 * Creates a new event listener, register it and return it
228 function add_listener(to, event_s, func) {
229 var el = new EventListener(to, event_s,func);
234 function remove_listener(to, event_s, func) {
235 if(to.removeEventListener) {
236 to.removeEventListener(event_s, func, false);
238 else if(to.detachEvent) {
239 to.detachEvent(event_s, func);
241 else debug("unable to detach event");
244 /* Get's first real child (e.g. one with an id
245 * from the element specified */
246 function get_first_real_child(from) {
247 from = from.firstChild;
248 while(from.nextSibling) {
251 from = from.nextSibling;
256 function get_last_real_child(from) {
257 var children = from.childNodes;
258 for(var i=children.length-1; i>=0; i++) {
267 * Operations for creating elements
269 // creates node of type type with id id and content content
270 // returns the node created
271 function create_node(type, id, content) {
272 var node = document.createElement(type);
277 if(content&&content!=null) {
278 if(document.createTextNode)
279 node.appendChild(document.createTextNode(content));
281 node.innerHTML = content;
286 function create_txt(txt) {
287 return document.createTextNode(txt);
290 function create_fragment() {
291 return document.createDocumentFragment();
294 // creates an empty table row with id and
296 function create_tr(id) {
297 return create_node("tr", id, null);
300 function create_param(name, value) {
301 var p = create_node("param");
307 // add a new TD with id id and content to a tr
308 function add_td(tr, content, id) {
309 var n = create_node("td", id, content);
314 function add_li(list, content, id) {
315 var n = create_node("li", id, content);
320 function add_txt(node, txt) {
321 node.appendChild(create_txt(txt));
324 function apply_border_style_to_children(who, style) {
325 var c = who.childNodes;
326 for(var i=0; i<c.length; i++) {
328 c[i].style.borderBottom = style;
329 c[i].style.borderTop = style;
334 function MoveObject(container, on_change, multi_move) {
335 // container, moving, element that's moving, initial position, element that's simulating movement,
336 // function to call if something has been moved and a variable to know if mouse key is down,
337 // if we should be able to move thigns around, optional doubleclick handler, optional selection change handler,
338 // if we *think* something is selected or not
339 this.container = container; // container for the elements
340 this.on_change = on_change; // function to call if something has been moved
341 this.moving = false; // if we are moving something
342 this.moving_elem = null; // the elemnt that is moving
343 this.moving_init_pos = null; // initial position of moving element (pagex,pagey)
344 this.moving_clone = null; // the clone that is actually moving
345 /* this is for interaction between mousemoving and mousedown */
346 this.possible_move_node = null; // the node the user might try moving
347 this.can_move = true; // if we are allowed to move anything at all
348 this.double_click_cb = null; // function to call on doubleclick
349 this.selection_change_cb = null;// function to call when the selection changes (with true or false
350 this.select_if_no_move = false; // if nothing has moved the node should be selected
351 this.multi_move = multi_move; // if allow for multiple move we won't do any of the moving and this function should
352 // return text that should be placed on the "moving" object
353 this.something_selected = false; // whether something currently is selected
356 /* visual effects 0> moving */
358 function setup_node_move(container, on_change, multi_move) {
359 var id = moveables.length;
360 add_listener(container, "mousedown", mouse_down_node);
361 add_listener(container, "mouseup", mouse_up_node);
362 add_listener(container, "mousemove", move_node);
363 container.setAttribute("move_id", id);
367 moveables[id] = new MoveObject(container, on_change, multi_move);
371 function add_move_doubleclick(id, func) {
372 moveables[id].double_click_cb = func;
375 function set_moveable(id, moveable) {
376 moveables[id].can_move = moveable;
379 function set_selection_change_handler(id, func) {
380 moveables[id].selection_change_cb = func;
383 /* NodeSelection _ find out if a node is selected */
384 function is_node_selected(node) {
385 return node.hasAttribute&&node.hasAttribute("selected");
388 /* NodeSelection - set whether a node is selectable or not */
389 function set_node_selectable(node, val) {
390 if(val&&node.hasAttribute&&node.hasAttribute("noselect")) {
391 node.removeAttribute("noselect");
393 else node.setAttribute("noselect", "true");
396 /* NodeSelection - select node */
397 function select_node(node) {
398 if(node.hasAttribute("noselect"))
400 if(node.hasAttribute("selected"))
403 node.setAttribute("selected", "1");
407 function unselect_node(node) {
408 if(node.hasAttribute("selected")) {
409 node.removeAttribute("selected");
413 function unselect_all_nodes(container) {
415 var nodes = xpath_query(container, ".//.[@selected]");
417 var elems = new Array();
418 while((n = nodes.iterateNext())) {
419 elems[elems.length] = n;
421 for(var i=0; i<elems.length; i++)
422 unselect_node(elems[i]);
425 var node = container.childNodes
428 for(var i=0; i < node.length; i++) {
429 if(node[i].hasAttribute("selected"))
430 unselect_node(node[i]);
435 /* will check if anything is selected */
436 function selection_anything_selected(container) {
438 var x = xpath_query(container, ".//.[@selected]", XPathResult.ANY_UNORDERED_NODE_TYPE);
439 return x.singleNodeValue?true:false;
442 var nodes = container.childNodes
443 for(var i=0; i < nodes.length; i++) {
444 if(node.hasAttribute("selected"))
452 /* Will find the first selected node and set attribute needle
453 * in needle if it is found before it returns
455 function find_first_selected_node(container, needle) {
456 if(xpath_ok()&&!needle) {
457 var x = xpath_query(container, ".//.[@selected]", XPathResult.FIRST_ORDERED_NODE_TYPE);
458 return x.singleNodeValue;
461 var nodes = container.childNodes
462 var length = nodes.length;
463 for(var i=0; i < length; i++) {
465 if(needle&&needle==node)
466 needle.setAttribute("needle", "found");
467 if(node.hasAttribute&&node.hasAttribute("selected"))
474 /* selects all nodes between the two */
475 function select_range(from, to) {
478 while(node != null && node!=to) {
479 node = node.nextSibling;
484 /* will return an array of selected elements attribute in container */
485 function get_attribute_from_selected_elems(container, attribute) {
486 var c = container.childNodes;
487 var ret = new Array();
489 for(var i=0; i<l; i++) {
490 if(is_node_selected(c[i]) && c[i].hasAttribute(attribute)) {
491 ret[ret.length] = c[i].getAttribute(attribute);
497 function mouse_down_node(e) {
498 if(e.button!=0&&e.button!=1)
501 move_idx = get_target_id(e.target);
507 var m = moveables[move_idx];
508 var elem = find_target_node(e.target);
511 /* move_node will toggle this if true and call start_node_move to initiate moving*/
512 // do not move if it specified not to move
514 m.moving_init_pos = new Array(e.pageX, e.pageY);
515 m.possible_move_node = elem;
517 var something_selected = true;
518 if(e.detail>1) { // we were just here, don't do the other stuff again..
520 if(m.double_click_cb&&e.detail==2)
521 m.double_click_cb(elem, e);
523 else if(e.ctrlKey||e.metaKey) {
524 if(is_node_selected(elem)) {
526 if(m.selection_change_cb&&m.something_selected)
527 something_selected = selection_anything_selected(m.container);
533 else if (e.shiftKey) {
536 sel_node = find_first_selected_node(m.container, elem);
541 if(elem.hasAttribute("needle")) {
542 select_range(elem, sel_node);
543 elem.removeAttribute("needle");
546 select_range(sel_node, elem);
551 if(!is_node_selected(elem)) {
552 unselect_all_nodes(m.container);
556 m.select_if_no_move = true;
559 /* something selected */
560 if(m.selection_change_cb) {
561 m.selection_change_cb(something_selected);
562 m.something_selected = something_selected;
566 function mouse_up_node(e) {
569 var m = moveables[move_idx];
573 else if(m.select_if_no_move) {
574 var elem = find_target_node(e.target);
575 unselect_all_nodes(m.container);
578 m.select_if_no_move = false;
579 m.possible_move_node = null;
581 m.container.className = m.container.className;
584 /* todo; rework to use elem instead of event */
585 function start_node_move(e, elem) {
588 move_idx = get_target_id(e.target);
592 if(!moveables[move_idx].can_move)
595 var m = moveables[move_idx];
596 var move = find_target_node(e.target);
597 var container = m.container;
599 m.moving = true; // moving
600 m.moving_elem = find_target_node(m.possible_move_node); // what
601 move = m.moving_elem;
602 m.possible_move_node = null;
605 txt = m.multi_move();
606 else if(move.childNodes[1])
607 txt = move.childNodes[1].textContent;
608 else txt = move.childNodes[0].textConten;
610 m.moving_clone = detach_node(move, txt);
611 set_node_offset(e, m.moving_clone);
613 add_listener(document.body, "mouseup", stop_node_move);
614 add_listener(document.body, "mousemove", move_node);
616 container.style.cursor = "move";
622 /* basically the reverse of start */
623 function stop_node_move(e) {
624 if(move_idx<0||!moveables[move_idx].moving)
627 var m = moveables[move_idx];
629 var move = m.moving_elem;
630 var container = m.container;
631 var target = find_target_node(e.target);
634 /* don't move it if we are moving on top of selection */
635 if(target&&!is_node_selected(target)) {
636 m.on_change(null,target);
638 reattach_node(move, null, m.moving_clone, 4); // remove moving node
640 else if(target!=null&&target!=move) {
641 /* if first in list or above the one beeing moved, move it up */
643 if(target==get_first_real_child(container)||target.nextSibling==move) {
645 reattach_node(move, target, m.moving_clone, 1);
647 else if(target.nextSibling!=null) {
648 /* normally default action */
651 if(is_moving_up(container, move, target)) {
655 attach_at = target.nextSibling;
657 reattach_node(move, attach_at, m.moving_clone, 1);
660 /* basically this means we don't know any better */
661 /* should not happen unless target actually is last in list */
662 /*to = get_last_real_child(container);
663 reattach_node(move, container, m[4], 2);*/
665 reattach_node(move, container, m.moving_clone, 2);
669 m.on_change(null, to);
672 reattach_node(move, null, m.moving_clone, 4); // don't move it
675 container.style.cursor = "default";
677 remove_listener(document.body, "mouseup", stop_node_move);
678 remove_listener(document.body, "mousemove", move_node);
680 m.moving_elem = null;
681 m.moving_init_pos = null;
682 m.moving_clone = null;
686 function move_node(e) {
687 var id = window.move_idx || -1;
688 var o = 4; // required offset
690 if(moveables[id].possible_move_node!=null) {
691 var p = moveables[id].moving_init_pos;
692 if(Math.abs(p[0]-e.pageX)>=o||Math.abs(p[1]-e.pageY)>o)
695 if(!moveables[id].moving)
699 set_node_offset(e, moveables[id].moving_clone);
703 function is_moving_up(container, move, target) {
704 var c = container.childNodes;
705 for(var i=0; i<c.length; i++) {
711 debug("Wops, not moving up or down!")
716 function detach_node(d, txt) {
717 var rep = create_node("div");
719 rep.style.width = d.offsetWidth/4 + "px";
720 rep.className = "moving_box";
722 txt = create_node("p", null, txt);
723 txt.className = "nomargin";
724 rep.appendChild(txt);
726 d.setAttribute("old_class", d.className);
727 d.className = "moving";
729 document.body.appendChild(rep);
732 /* reattach node at specified position (at) with action
738 function reattach_node(node, at, clone, action) {
739 node.style.width ="";
741 node.style.position = "";
742 node.className = node.getAttribute("old_class");
745 at.parentNode.insertBefore(node, at);
747 else if(action == 2) {
749 at.appendChild(node);
751 else if(action == 3) {
753 replace_node(node, at);
758 debug("invalid action in reattach_node");
763 function get_target_id(target) {
764 var t = find_target_node(target);
765 if(t!=null&&t.parentNode&&t.parentNode!=null)
766 return t.parentNode.getAttribute("move_id");
770 function find_target_node(target) {
771 while(target != null && target.parentNode&&target.parentNode != null) {
772 for(var i=0; i<moveables.length; i++)
773 if(moveables[i].container==target.parentNode)
775 target = target.parentNode;
780 /* set's this node to the position in event */
781 function set_node_offset(ev, node) {
782 /* relative positioning:*/
785 if(node.hasAttribute("ot")&&node.hasAttribute("ol")) {
786 ot = node.getAttribute("ot");
787 ol = node.getAttribute("ol");
791 ol = node.offsetLeft - 10;
792 node.setAttribute("ot", ot);
793 node.setAttribute("ol", ol);
795 var h = ev.pageY - ot;
796 var l = ev.pageX - ol;
798 var h = ev.pageY - (node.offsetHeight/2);
799 var l = ev.pageX + 10;*/
800 node.style.top = h + "px";
801 node.style.left = l + "px";
805 function Slider(sid, main_slider, change, text_area) {
806 this.main_slider = main_slider;
807 this.change_call = change;
808 this.text_area = text_area;
811 this.user_moving = false;
813 this.timer_last_pos = null;
817 Slider.prototype.setup_timer = function() {
819 clearTimeout(this.timer);
820 this.timer_last_pos = this.moving_pos;
821 this.timer = setTimeout(this.timer_cb, 250);
823 Slider.prototype.timer_cb = function() {
824 // user has managed to hold mousepoitner stil
825 if(this.timer_last_pos==this.moving_pos) {
827 var idx = window.slider_idx;
828 window.slider_send_callback(idx);
830 else if(this.user_moving) {
835 /* should be a div */
836 function setup_slider(slider, callback, txt) {
837 var sid = sliders.length;
839 txt = create_node("p", "slider_txt_" + sid, txt);
840 txt.className = "slider_txt";
841 slider.appendChild(txt);
843 var s = create_node("div");
844 s.className = "slider_main";
845 s.id = "slider_main" + sid;
846 slider.appendChild(s);
847 var pointer = create_node("div", " ");
848 pointer.className = "slider_pointer";
849 pointer.id = "slider_pointer" + sid;
850 s.setAttribute("sliderid", sid);
851 add_listener(s, "mousedown", mouse_down_slider);
852 pointer.style.height = (s.offsetHeight) + "px";
853 s.appendChild(pointer);
854 sliders[sid] = new Slider(sid, slider, callback, txt);
855 set_slider_pos(sid, 0);
859 function get_slider_txt(sid) {
860 return sliders[sid].text_area;
863 function slider_send_callback(sid) {
864 if(sliders[sid].change_call)
865 sliders[sid].change_call(sliders[sid].value);
868 function set_slider_pos(sid, pos, force) {
869 var pointer = document.getElementById("slider_pointer" + sid);
870 var s = document.getElementById("slider_main" + sid);
873 if(pointer==null||s==null) {
874 debug("no slider pointer||main");
878 if(sliders[sid].user_moving&&!force)
886 if(pos==sliders[sid].value) {
890 sliders[sid].value = pos;
892 var dist = (s.offsetWidth * pos) /100
893 if(isNaN(dist)||dist=="NaN")
896 pointer.style.left = dist+ "px";
899 function get_slider_pos(sid) {
900 return sliders[sid].value;
903 function mouse_down_slider(e) {
905 /* TODO: rewrite test */
906 if(!targ||!targ.hasAttribute||!targ.hasAttribute("sliderid")) {
908 targ = targ.parentNode;
910 if(!targ||!targ.hasAttribute||!targ.hasAttribute("sliderid"))
914 slider_idx = targ.getAttribute("sliderid");
916 add_listener(document.body, "mousemove", mouse_move_slider);
917 add_listener(document.body, "mouseup", mouse_up_slider);
919 sliders[slider_idx].user_moving = true;
920 sliders[slider_idx].main_slider.setAttribute("slider_moving", "yeahitis");
922 mouse_move_slider(e); // lazy
923 //e.stopPropagation();
927 function mouse_move_slider(e) {
929 debug("mouse_move_slider should not be called now");
934 var left = slider_get_left(e, slider_idx)
935 set_slider_pos(slider_idx, left, true);
936 sliders[slider_idx].moving_pos = left;
937 sliders[slider_idx].setup_timer(sliders[slider_idx].change_call);
940 function mouse_up_slider(e) {
944 // prolly not necessary though
945 clearTimeout(sliders[slider_idx].timer);
946 sliders[slider_idx].timer = null;
948 remove_listener(document.body, "mousemove", mouse_move_slider);
949 remove_listener(document.body, "mouseup", mouse_up_slider);
950 sliders[slider_idx].user_moving = false;
951 sliders[slider_idx].main_slider.removeAttribute("slider_moving");
952 slider_send_callback(slider_idx);
956 function slider_get_left(e, sid) {
957 var x = e.pageX - get_absolute_left(sliders[sid].main_slider);
958 x = (x*100)/sliders[sid].main_slider.offsetWidth;
965 function OverlayObject(back, sizes, open_callback, close_callback) {
966 this.back = back; // element to put overlay over
967 this.sizes = sizes; // minimum sizes [top, left, min-height, min-width ]
968 this.open_callback = open_callback;
969 this.close_callback = close_callback;
970 this.overlay = null; // the overlay element
971 this.write = null; // write area
975 function setup_overlay(back, sizes, open_callback, close_callback) {
976 var oid = overlay.length;
977 overlay[oid] = new OverlayObject(back, sizes, open_callback, close_callback);
978 var t = create_node("div", "overlay_" + oid);
979 overlay[oid].overlay = t;
980 t.className = "overlay";
981 t.style.height = overlay_adjust + "px";
982 var img = create_node("img", "overlay_close_" + oid);
983 img.src = IMAGE.CLOSE;
984 img.setAttribute("oid", oid);
985 img.className = "close fakelink";
986 img.title = "Close [Ctrl+Shift+X]";
987 add_listener(img, "click", close_overlay_cb);
991 document.body.appendChild(t);
996 function get_overlay_write_area(oid) {
997 if(overlay[oid].write==null) {
998 overlay[oid].write = create_node("div", "overlay_write_area_" + oid);
999 overlay[oid].overlay.appendChild(overlay[oid].write);
1002 return overlay[oid].write;
1005 function open_overlay(oid) {
1006 var sizes = overlay[oid].sizes;
1007 var o = overlay[oid].back;
1008 var top = get_absolute_top(o);
1011 var left = get_absolute_left(o);
1014 var height = o.offsetHeight;
1017 var width = o.offsetWidth;
1021 if(overlay_hide_on_resize&&overlay[oid].write)
1022 overlay[oid].write.style.display = "none";
1024 var op = overlay[oid].overlay;
1025 open_overlay_idx = oid;
1026 op.style.left = left + "px";
1027 op.style.top = top + "px";
1028 op.style.width = overlay_adjust + "px";
1029 op.style.height = overlay_adjust + "px";
1031 op.style.display = "block";
1038 overlay[oid].close_key = keyboard_register_listener(close_overlay_cb, "x", KEYBOARD_CTRL_KEY|KEYBOARD_SHIFT_KEY, true);
1040 setTimeout(adjust_overlay_size, overlay_time, oid, new Array(overlay_adjust, overlay_adjust), new Array(height, width, hx, wx));
1043 function open_overlay_fixed(oid) {
1044 // TODO: find another way to determine these heights
1045 var sizes = new Array(106, 56, 800, 500);
1047 var height = sizes[3];
1048 var width = sizes[2];
1049 var op = overlay[oid].overlay;
1050 open_overlay_idx = oid;
1051 if(overlay_hide_on_resize&&overlay[oid].write)
1052 overlay[oid].write.style.display = "none";
1054 op.style.position = "fixed";
1056 op.style.left = sizes[1] + "px";
1057 op.style.top = sizes[0] + "px";
1058 op.style.width = overlay_adjust + "px";
1059 op.style.height = overlay_adjust + "px";
1061 op.style.display = "block";
1063 /* adjust to browser window */
1065 var w_h = window.innerHeight;
1066 var w_w = window.innerWidth;
1068 /* ignore it if unreasonable values.. */
1069 if(w_h&&w_w&&w_h>100&&w_w>100) {
1070 if(height+sizes[0]+x_o>w_h)
1071 height = w_h - sizes[0] - x_o;
1072 if(width+sizes[1]+x_o>w_w)
1073 width = w_w - sizes[1] - x_o;
1082 overlay[oid].close_key = keyboard_register_listener(close_overlay_cb, "x", KEYBOARD_CTRL_KEY|KEYBOARD_SHIFT_KEY, true);
1084 setTimeout(adjust_overlay_size, overlay_time, oid, new Array(overlay_adjust, overlay_adjust), new Array(height, width, hx, wx));
1087 function adjust_overlay_size(oid, current, dest) {
1088 var h = current[0] = current[0] + (dest[2]*overlay_adjust);
1089 var w = current[1] = current[1] + (dest[3]*overlay_adjust);
1090 var adjusted = false;
1106 overlay[oid].overlay.style.height = h + "px";
1107 overlay[oid].overlay.style.width = w + "px";
1108 //debug("h: " + h + ", w: " + w);
1111 //debug("setting timeout");
1112 setTimeout(adjust_overlay_size, overlay_time, oid, current, dest);
1115 var height = (overlay[oid].overlay.offsetHeight-20);
1116 if(overlay[oid].write) {
1117 if(overlay_hide_on_resize) {
1118 overlay[oid].write.style.display = "block"; // kiss
1121 if(overlay[oid].open_callback)
1122 overlay[oid].open_callback(height);
1126 function close_overlay(oid) {
1127 var o = overlay[oid].overlay;
1128 open_overlay_idx = -1;
1129 o.style.display = "none";
1130 if(overlay[oid].close_key)
1131 overlay[oid].close_key = keyboard_remove_listener(overlay[oid].close_key);
1132 if(overlay[oid].close_callback)
1133 overlay[oid].close_callback();
1135 function close_overlay_cb(e) {
1137 if(t&&t.hasAttribute&&t.hasAttribute("oid")) {
1138 close_overlay(t.getAttribute("oid"));
1141 else if(open_overlay_idx>=0) {
1142 close_overlay(open_overlay_idx);
1148 function stop_propagation(e) {
1149 if(e.stopPropagation)
1150 e.stopPropagation();
1153 function stop_event(e) {
1155 if(e.preventDefault)
1157 if(e.stopPropagation)
1158 e.stopPropagation();
1160 e.returnValue = false;
1164 /* range selection (to put ranges in a "list" */
1165 /* txt: excisting range:
1166 * from: from what number
1167 * to: optional to argument
1169 function add_range(txt, from, to) {
1178 function scrollIntoView(elem, top) {
1181 /* seriously though, if you don't support it, don't claim you do!*/
1182 //if(elem.scrollIntoView) {
1183 if(navigator.product&&navigator.product=="Gecko") {
1184 elem.scrollIntoView(top);
1186 else if(elem.parentNode) {
1189 elem.parentNode.scrollTop=elem.offsetTop - (top?elem.offsetHeight*2:elem.parentNode.offsetHeight);
1194 function setSelectionRange(input, selectionStart, selectionEnd) {
1195 if (input.setSelectionRange) {
1197 input.setSelectionRange(selectionStart, selectionEnd);
1199 else if (input.createTextRange) {
1200 var range = input.createTextRange();
1201 range.collapse(true);
1202 range.moveEnd('character', selectionEnd);
1203 range.moveStart('character', selectionStart);
1208 function setCaretToEnd (input) {
1209 setSelectionRange(input, input.value.length, input.value.length);
1211 function setCaretToBegin (input) {
1212 setSelectionRange(input, 0, 0);
1214 function setCaretToPos (input, pos) {
1215 setSelectionRange(input, pos, pos);
1220 String.prototype.trim = function() {
1221 return this.replace(/^\s+|\s+$/g,'');
1224 function add_string_with_br(to, str) {
1225 str = str.replace("\r", "").split('\n');
1226 for(var i=0; i<str.length; i++) {
1228 to.appendChild(create_txt(str[i]));
1229 to.appendChild(create_node("br"));
1233 function adjust_opacity_timer(node, current_opacity, dest_opacity, last_time) {
1234 var now = get_time();
1241 //debug("time: " + time);
1244 current_opacity+=0.2;
1247 current_opacity+=0.1;
1250 if(current_opacity<dest_opacity) {
1251 node.style.opacity = current_opacity ;
1252 setTimeout(adjust_opacity_timer, time, node, current_opacity, dest_opacity, now);
1255 node.style.opacity = dest_opacity ;
1259 /* what to blink, what color and count, two first arguments are required */
1260 function blink_node(what, color, count) {
1261 if(typeof(count)=='undefined') {
1265 what.style.backgroundColor = color;
1267 what.style.backgroundColor = "";
1269 setTimeout(blink_node, 350, what, color, --count);
1274 /* if content is null, we'll use tabs */
1275 function Popup(point, content) {
1277 this.content = content;
1278 this.popup = create_node("div");
1280 this.popup.appendChild(content);
1281 this.popup.className = "popup";
1282 this.point.appendChild(this.popup);
1285 Popup.prototype.show = function() {
1286 this.popup.style.display = "block";
1288 Popup.prototype.hide = function() {
1289 this.popup.style.display = "";
1291 Popup.prototype.destroy = function() {
1292 remove_node(this.popup);
1295 this.content = null;
1299 function xpath_init() {
1300 xpath_queries = new Hashtable();
1303 /* checks if xpath is available */
1304 function xpath_ok() {
1305 return document.evaluate?true:false;
1308 // remember to check with xpath_ok first when using this function
1309 function xpath_query(container, expression, resulttype, nocache_query) {
1311 resulttype = XPathResult.ANY_TYPE;
1313 return document.evaluate(expression, container, null, resulttype, null);
1316 var e = xpath_queries.get(expression);
1318 e = document.createExpression(expression, null);
1319 xpath_queries.put(expression, e);
1321 return e.evaluate(container, resulttype, null);
1325 function opera_quirk_set_display_none(element, cleanup) {
1327 element.style.display = "none";
1328 element.style.visibility = "";
1331 setTimeout(opera_quirk_set_display_none, 10, element, true);
1332 element.style.visibility = "hidden";
1336 function createCookie(name,value,days) {
1338 var date = new Date();
1339 date.setTime(date.getTime()+(days*24*60*60*1000));
1340 var expires = "; expires="+date.toGMTString();
1342 else var expires = "";
1343 document.cookie = name+"="+value+expires+"; path=/";
1345 function readCookie(name) {
1346 var nameEQ = name + "=";
1347 var ca = document.cookie.split(';');
1348 for(var i=0;i < ca.length;i++) {
1350 while (c.charAt(0)==' ') c = c.substring(1,c.length);
1351 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
1355 function eraseCookie(name) {
1356 createCookie(name,"",-1);
1359 // This function is in the public domain. Feel free to link back to http://jan.moesen.nu/
1360 function sprintf() {
1361 if (!arguments || arguments.length < 1 || !RegExp) {
1364 var str = arguments[0];
1365 var re = /([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X)(.*)/;
1366 var a = b = [], numSubstitutions = 0, numMatches = 0;
1367 while ((a = re.exec(str))) {
1368 var leftpart = a[1], pPad = a[2], pJustify = a[3], pMinLength = a[4];
1369 var pPrecision = a[5], pType = a[6], rightPart = a[7];
1371 //alert(a + '\n' + [a[0], leftpart, pPad, pJustify, pMinLength, pPrecision);
1379 if (numSubstitutions >= arguments.length) {
1380 alert('Error! Not enough function arguments (' + (arguments.length - 1) + ', excluding the string)\nfor the number of substitution parameters in string (' + numSubstitutions + ' so far).');
1382 var param = arguments[numSubstitutions];
1384 if (pPad && pPad.substr(0,1) == "'") pad = leftpart.substr(1,1);
1385 else if (pPad) pad = pPad;
1386 var justifyRight = true;
1387 if (pJustify && pJustify === "-") justifyRight = false;
1389 if (pMinLength) minLength = parseInt(pMinLength);
1391 if (pPrecision && pType == 'f') precision = parseInt(pPrecision.substring(1));
1393 if (pType == 'b') subst = parseInt(param).toString(2);
1394 else if (pType == 'c') subst = String.fromCharCode(parseInt(param));
1395 else if (pType == 'd') subst = parseInt(param) ? parseInt(param) : 0;
1396 else if (pType == 'u') subst = Math.abs(param);
1397 else if (pType == 'f') subst = (precision > -1) ? Math.round(parseFloat(param) * Math.pow(10, precision)) / Math.pow(10, precision): parseFloat(param);
1398 else if (pType == 'o') subst = parseInt(param).toString(8);
1399 else if (pType == 's') subst = param;
1400 else if (pType == 'x') subst = ('' + parseInt(param).toString(16)).toLowerCase();
1401 else if (pType == 'X') subst = ('' + parseInt(param).toString(16)).toUpperCase();
1403 str = leftpart + subst + rightPart;
1410 function setting_set(name, value) {
1411 var s = readCookie("pf_conf");
1417 for(var i=0; i< s.length; i++) {
1418 var tmp = s[i].split("-");
1423 ns+=name + "-" + value;
1433 ns+=name + "-" +value + ":";
1435 createCookie("pf_conf", ns, 200);
1440 function setting_get(name) {
1441 var val = readCookie("pf_conf");
1443 if(!val||!val.length)
1446 val = val.split(":");
1447 for(var i=0; i < val.length; i++) {
1448 var t = val[i].split("-");