blob: b8e8808e068cb7b6ae55a77cf295900e456f9a9e [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
Nils Diewald7148c6f2015-05-04 15:07:53 +000037
38 /**
39 * Get or set the content of the meun item.
40 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000041 content : function (content) {
42 if (arguments.length === 1)
43 this._content = document.createTextNode(content);
44 return this._content;
45 },
46
Nils Diewald7148c6f2015-05-04 15:07:53 +000047 /**
48 * Get the lower cased field of the item
49 * (used for analyses).
50 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000051 lcField : function () {
52 return this._lcField;
53 },
54
Nils Diewald7148c6f2015-05-04 15:07:53 +000055
56 /**
57 * Get or set the information for action of this item.
58 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000059 action : function (action) {
60 if (arguments.length === 1)
61 this._action = action;
62 return this._action;
63 },
64
65 /**
66 * Check or set if the item is active
67 *
68 * @param {boolean|null} State of activity
69 */
70 active : function (bool) {
71 var cl = this.element().classList;
72 if (bool === undefined)
73 return cl.contains("active");
74 else if (bool)
75 cl.add("active");
76 else
77 cl.remove("active");
78 },
79
80 /**
81 * Check or set if the item is
82 * at the boundary of the menu
83 * list
84 *
85 * @param {boolean|null} State of activity
86 */
87 noMore : function (bool) {
88 var cl = this.element().classList;
89 if (bool === undefined)
90 return cl.contains("no-more");
91 else if (bool)
92 cl.add("no-more");
93 else
94 cl.remove("no-more");
95 },
96
97 /**
98 * Get the document element of the menu item
99 */
100 element : function () {
101 // already defined
102 if (this._element !== undefined)
103 return this._element;
104
105 // Create list item
106 var li = document.createElement("li");
107
108 // Connect action
109 if (this["onclick"] !== undefined) {
110 li["onclick"] = this.onclick.bind(this);
111 };
112
113 // Append template
114 li.appendChild(this.content());
115
116 return this._element = li;
117 },
118
119 /**
120 * Highlight parts of the item
121 *
122 * @param {string} Prefix string for highlights
123 */
124 highlight : function (prefix) {
125 var children = this.element().childNodes;
126 for (var i = children.length -1; i >= 0; i--) {
127 this._highlight(children[i], prefix);
128 };
129 },
130
131 // Highlight a certain substring of the menu item
132 _highlight : function (elem, prefix) {
133
134 if (elem.nodeType === 3) {
135
136 var text = elem.nodeValue;
137 var textlc = text.toLowerCase();
138 var pos = textlc.indexOf(prefix);
139 if (pos >= 0) {
140
141 // First element
142 if (pos > 0) {
143 elem.parentNode.insertBefore(
144 document.createTextNode(text.substr(0, pos)),
145 elem
146 );
147 };
148
149 // Second element
150 var hl = document.createElement("mark");
151 hl.appendChild(
152 document.createTextNode(text.substr(pos, prefix.length))
153 );
154 elem.parentNode.insertBefore(hl, elem);
155
156 // Third element
157 var third = text.substr(pos + prefix.length);
158 if (third.length > 0) {
159 var thirdE = document.createTextNode(third);
160 elem.parentNode.insertBefore(
161 thirdE,
162 elem
163 );
164 this._highlight(thirdE, prefix);
165 };
166
167 var p = elem.parentNode;
168 p.removeChild(elem);
169 };
170 }
171 else {
172 var children = elem.childNodes;
173 for (var i = children.length -1; i >= 0; i--) {
174 this._highlight(children[i], prefix);
175 };
176 };
177 },
178
179 /**
180 * Remove highlight of the menu item
181 */
182 lowlight : function () {
183 var e = this.element();
184
185 var marks = e.getElementsByTagName("mark");
186 for (var i = marks.length - 1; i >= 0; i--) {
187 // Create text node clone
188 var x = document.createTextNode(
189 marks[i].firstChild.nodeValue
190 );
191
192 // Replace with content
193 marks[i].parentNode.replaceChild(
194 x,
195 marks[i]
196 );
197 };
198
199 // Remove consecutive textnodes
200 e.normalize();
201 },
202
203 // Initialize menu item
204 _init : function (params) {
205
206 if (params[0] === undefined)
207 throw new Error("Missing parameters");
208
209 this.content(params[0]);
210
211 if (params.length === 2)
212 this._action = params[1];
213
214 this._lcField = ' ' + this.content().textContent.toLowerCase();
215
216 return this;
217 },
218
219 /**
220 * Return menu list.
221 */
222 menu : function () {
223 return this._menu;
224 }
225});