Ref selector looks the same in Chrome and other browsers
[gitorious:ui3.git] / js / src / components / ref-selector.js
1 /*global cull, dome*/
2 // The global, shared Gitorious namespace
3 this.gts = this.gts || {};
4
5 /**
6  * The ref selector builds an interactive "drop-down" menu from which the
7  * user can select a head or tag, or lookup a specific ref. The menu will
8  * reload the current page for the chosen ref.
9  * 
10  * gts.refSelector(refs, current, urlTemplate)
11  * 
12  * refs is an object containing available heads and tags and the oids they
13  * refer to, e.g.:
14  * 
15  *     { "heads": [["production", "24e9c0d4ce36fbe1dfca4029e3bd206d64e2eecc"],
16  *                 ["redesign", "4c22773401aa2cdd2391bc04443ad7eea193e7b6"],
17  *                 ["web-hooks", "a08053012b9aee5d9733fceb2cf3083f29d9aa7d"],
18  *                 ["master", "48ac677757da7ca052c59ebec0ded6e11eef2642"]],
19  *       "tags": [["v2.4.3", "7a3cffcb3c3db89e8005962850e29a8aab2ab09b"]]
20  *
21  * current is the active ref on the current page, either the refname
22  * or the actual oid.
23  * 
24  * urlTemplate is the URL that will be loaded, where the ref exists as a
25  * placeholder, e.g. "/gitorious/mainline/source/#{ref}:lib". A suitable
26  * template can be created from the current page's URL using
27  * gts.url.templatize(window.location.href, { ref: currentRef });
28  */
29 this.gts.refSelector = (function (e) {
30     function tpl(template, ref) {
31         return (template || "#{ref}").replace(/#\{ref\}/g, ref);
32     }
33
34     /**
35      * Gets the current ref. type is {heads,tags,remotes} and
36      * refs[type] is an array of ref tuples where each tuple contains
37      * the object id and a ref (e.g. "master").
38      */
39     function getCurrent(type, refName, refs) {
40         return cull.select(function (ref) {
41             return ref[0] === refName || ref[1] === refName;
42         }, refs[type] || [])[0];
43     }
44
45     function currentRefLink(refs, current) {
46         var currentRef = getCurrent("heads", current, refs);
47         var type = "branch";
48
49         if (!currentRef) {
50             currentRef = getCurrent("tags", current, refs);
51             type = "tag";
52         }
53
54         if (!currentRef) {
55             type = "ref";
56         }
57
58         return e.a({
59             href: "#",
60             className: "dropdown-toggle",
61             innerHTML: "<span class=\"caret\"></span> <em>" +
62                 type + ":</em> " + ((currentRef && currentRef[0]) || current)
63         });
64     }
65
66     function refInput(url) {
67         var input = e.input({ type: "text", className: "gts-ref-input" });
68         var form = e.form({
69             className: "gts-ref-input",
70             events: {
71                 submit: function (e) {
72                     e.preventDefault();
73                     window.location = tpl(url, input.value);
74                 }
75             }
76         }, [input]);
77
78         return e.li({
79             className: "gts-dropdown-input",
80             events: { click: function (e) { e.stopPropagation(); } }
81         }, [e.strong(["Enter ref: ", form])]);
82     }
83
84     function refItems(label, refs, urlTemplate) {
85         var initial = [e.li({className: "dropdown-label"}, [e.strong(label)])];
86         return cull.reduce(function (elements, ref) {
87             var url = tpl(urlTemplate, ref[1]);
88             elements.push(e.li(e.a({ href: url }, ref[0])));
89             return elements;
90         }, initial, refs.sort());
91     }
92
93     function refsList(refs, urlTemplate) {
94         return e.ul({ className: "dropdown-menu" },
95                     [refInput(urlTemplate)].
96                     concat(refItems("Branches", refs.heads || [], urlTemplate)).
97                     concat(refItems("Tags", refs.tags || [], urlTemplate)));
98     }
99
100     function build(refs, current, urlTemplate) {
101         return e.div({
102             className: "dropdown gts-branch-selector pull-right"
103         }, [currentRefLink(refs, current), refsList(refs, urlTemplate)]);
104     }
105
106     function refSelector(placeholder, refs, ref, refUrlTemplate) {
107         var selector = build(refs, ref, refUrlTemplate);
108         placeholder.parentNode.insertBefore(selector, placeholder);
109         placeholder.parentNode.removeChild(placeholder);
110         this.gts.dropdown(selector);
111     }
112
113     refSelector.build = build;
114     refSelector.getCurrentRef = getCurrent;
115     return refSelector;
116 }(dome.el));