blob: b80aa2aef10bb83939a6a10dcfe7094e3a6b2233 [file] [log] [blame]
Nils Diewald0e6992a2015-04-14 20:13:52 +00001/*
2 * MenuItems may define:
3 *
4 * onclick: action happen on click and enter.
5 * further: action happen on right arrow
6 */
7
8/**
9 * Item in the Dropdown menu
10 */
11define({
12 /**
13 * Create a new MenuItem object.
14 *
15 * @constructor
16 * @this {MenuItem}
17 * @param {Array.<string>} An array object of name, action and
18 * optionally a description
19 */
20 create : function (params) {
21 return Object.create(this)._init(params);
22 },
23
24 /**
25 * Upgrade this object to another object,
26 * while private data stays intact.
27 *
28 * @param {Object] An object with properties.
29 */
30 upgradeTo : function (props) {
31 for (var prop in props) {
32 this[prop] = props[prop];
33 };
34 return this;
35 },
36
37 content : function (content) {
38 if (arguments.length === 1)
39 this._content = document.createTextNode(content);
40 return this._content;
41 },
42
43 lcField : function () {
44 return this._lcField;
45 },
46
47 action : function (action) {
48 if (arguments.length === 1)
49 this._action = action;
50 return this._action;
51 },
52
53 /**
54 * Check or set if the item is active
55 *
56 * @param {boolean|null} State of activity
57 */
58 active : function (bool) {
59 var cl = this.element().classList;
60 if (bool === undefined)
61 return cl.contains("active");
62 else if (bool)
63 cl.add("active");
64 else
65 cl.remove("active");
66 },
67
68 /**
69 * Check or set if the item is
70 * at the boundary of the menu
71 * list
72 *
73 * @param {boolean|null} State of activity
74 */
75 noMore : function (bool) {
76 var cl = this.element().classList;
77 if (bool === undefined)
78 return cl.contains("no-more");
79 else if (bool)
80 cl.add("no-more");
81 else
82 cl.remove("no-more");
83 },
84
85 /**
86 * Get the document element of the menu item
87 */
88 element : function () {
89 // already defined
90 if (this._element !== undefined)
91 return this._element;
92
93 // Create list item
94 var li = document.createElement("li");
95
96 // Connect action
97 if (this["onclick"] !== undefined) {
98 li["onclick"] = this.onclick.bind(this);
99 };
100
101 // Append template
102 li.appendChild(this.content());
103
104 return this._element = li;
105 },
106
107 /**
108 * Highlight parts of the item
109 *
110 * @param {string} Prefix string for highlights
111 */
112 highlight : function (prefix) {
113 var children = this.element().childNodes;
114 for (var i = children.length -1; i >= 0; i--) {
115 this._highlight(children[i], prefix);
116 };
117 },
118
119 // Highlight a certain substring of the menu item
120 _highlight : function (elem, prefix) {
121
122 if (elem.nodeType === 3) {
123
124 var text = elem.nodeValue;
125 var textlc = text.toLowerCase();
126 var pos = textlc.indexOf(prefix);
127 if (pos >= 0) {
128
129 // First element
130 if (pos > 0) {
131 elem.parentNode.insertBefore(
132 document.createTextNode(text.substr(0, pos)),
133 elem
134 );
135 };
136
137 // Second element
138 var hl = document.createElement("mark");
139 hl.appendChild(
140 document.createTextNode(text.substr(pos, prefix.length))
141 );
142 elem.parentNode.insertBefore(hl, elem);
143
144 // Third element
145 var third = text.substr(pos + prefix.length);
146 if (third.length > 0) {
147 var thirdE = document.createTextNode(third);
148 elem.parentNode.insertBefore(
149 thirdE,
150 elem
151 );
152 this._highlight(thirdE, prefix);
153 };
154
155 var p = elem.parentNode;
156 p.removeChild(elem);
157 };
158 }
159 else {
160 var children = elem.childNodes;
161 for (var i = children.length -1; i >= 0; i--) {
162 this._highlight(children[i], prefix);
163 };
164 };
165 },
166
167 /**
168 * Remove highlight of the menu item
169 */
170 lowlight : function () {
171 var e = this.element();
172
173 var marks = e.getElementsByTagName("mark");
174 for (var i = marks.length - 1; i >= 0; i--) {
175 // Create text node clone
176 var x = document.createTextNode(
177 marks[i].firstChild.nodeValue
178 );
179
180 // Replace with content
181 marks[i].parentNode.replaceChild(
182 x,
183 marks[i]
184 );
185 };
186
187 // Remove consecutive textnodes
188 e.normalize();
189 },
190
191 // Initialize menu item
192 _init : function (params) {
193
194 if (params[0] === undefined)
195 throw new Error("Missing parameters");
196
197 this.content(params[0]);
198
199 if (params.length === 2)
200 this._action = params[1];
201
202 this._lcField = ' ' + this.content().textContent.toLowerCase();
203
204 return this;
205 },
206
207 /**
208 * Return menu list.
209 */
210 menu : function () {
211 return this._menu;
212 }
213});