Passed
Branch master (32b4c5)
by Askupa
01:33
created

mivhak-dev.js ➔ strToValue   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 7

Duplication

Lines 7
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
c 1
b 0
f 0
nc 4
dl 7
loc 7
rs 9.2
nop 1
1
/**
2
 * Package:      mivhak-js
3
 * URL:          http://products.askupasoftware.com/mivhak-js
4
 * Version:      1.0.0
5
 * Date:         2016-07-07
6
 * Dependencies: jQuery Mousewheel, Ace Editor
7
 * License:      GNU GENERAL PUBLIC LICENSE
8
 *
9
 * Developed by Askupa Software http://www.askupasoftware.com
10
 */
11
/* test-code */
12 View Code Duplication
var testapi = {};
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
13
/* end-test-code */
14
15
(function ( $ ) {// Ace global config
16
ace.config.set('basePath', 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.3/');/**
0 ignored issues
show
Bug introduced by
The variable ace seems to be never declared. If this is a global, consider adding a /** global: ace */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
17
 * Converts a string to it's actual value, if applicable
18
 * 
19
 * @param {String} str
20
 */
21
function strToValue( str )
22
{
23
    if('true' === str.toLowerCase()) return true;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
24
    if('false' === str.toLowerCase()) return false;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
25
    if(!isNaN(str)) return parseFloat(str);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
26
    return str;
27
}
28
29
/**
30
 * Convert hyphened text to camelCase.
31
 * 
32
 * @param {string} str
33
 * @returns {string}
34
 */
35
function toCamelCase( str )
36
{
37
    return str.replace(/-(.)/g,function(match){
38
        return match[1].toUpperCase();
39
    });
40
}
41
42
/**
43
 * Reads the element's 'miv-' attributes and returns their values as an object
44
 * 
45
 * @param {DOMElement} el
46
 * @returns {Object}
47
 */
48
function readAttributes( el ) 
49
{
50
    var options = {};
51
    $.each(el.attributes, function(i, attr){
52
        if(/^miv-/.test(attr.name))
53
        {
54
            options[toCamelCase(attr.name.substr(4))] = strToValue(attr.value);
55
        }
56
    });
57
    return options;
58
}
59
60
/**
61
 * Get the average value of all elements in the given array.
62
 * 
63
 * @param {Array} arr
64
 * @returns {Number}
65
 */
66
function average( arr )
67
{
68
    var i = arr.length, sum = 0;
69
    while(i--) sum += parseFloat(arr[i]);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
70
    return sum/arr.length;
71
}
72
73
/**
74
 * Get the maximum value of all elements in the given array.
75
 * 
76
 * @param {Array} arr
77
 * @returns {Number}
78
 */
79
function max( arr )
80
{
81
    var i = arr.length, maxval = arr[--i];
82
    while(i--) if(arr[i] > maxval) maxval = arr[i];
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
83
    return maxval;
84
}
85
86
/**
87
 * Get the minimum value of all elements in the given array.
88
 * 
89
 * @param {Array} arr
90
 * @returns {Number}
91
 */
92
function min( arr )
93
{
94
    var i = arr.length, minval = arr[--i];
95
    while(i--) if(arr[i] < minval) minval = arr[i];
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
96
    return minval;
97
}
98
99
/**
100
 * Calculate the editor's height based on the number of lines & line height.
101
 * 
102
 * @param {jQuery} $editor Ther editor wrapper element (PRE)
103
 * @returns {Number}
104
 */
105
function getEditorHeight( $editor )
106
{
107
    var height = 0;
108
    $editor.find('.ace_text-layer').children().each(function(){
109
        height += $(this).height();
110
    });
111
    return height;
112
}
113
114
/**
115
 * Convert a string like "3, 5-7" into an array of ranges in to form of
116
 * [
117
 *   {start:2, end:2},
118
 *   {start:4, end:6},
119
 * ]
120
 * The string should be given as a list if comma delimited 1 based ranges.
121
 * The result is given as a 0 based array of ranges.
122
 * 
123
 * @param {string} str
124
 * @returns {Array}
125
 */
126
function strToRange( str )
127
{
128
    var range = str.replace(' ', '').split(','),
129
        i = range.length,
130
        ranges = [],
131
        start, end, splitted;
132
    
133
    while(i--)
134
    {
135
        // Multiple lines highlight
136
        if( range[i].indexOf('-') > -1 )
137
        {
138
            splitted = range[i].split('-');
139
            start = parseInt(splitted[0])-1;
140
            end = parseInt(splitted[1])-1;
141
        }
142
143
        // Single line highlight
144
        else
145
        {
146
            start = parseInt(range[i])-1;
147
            end = start;
148
        }
149
        
150
        ranges.unshift({start:start,end:end});
151
    }
152
    
153
    return ranges;
154
}
155
156
/**
157
 * Request animation frame. Uses setTimeout as a fallback if the browser does
158
 * not support requestAnimationFrame (based on 60 frames per second).
159
 * 
160
 * @param {type} cb
161
 * @returns {Number}
162
 */
163
var raf = window.requestAnimationFrame || 
164
          window.webkitRequestAnimationFrame || 
165
          window.mozRequestAnimationFrame ||
166
          window.msRequestAnimationFrame ||
167
          function(cb) { return window.setTimeout(cb, 1000 / 60); };
168
169
/* test-code */
170
testapi.strToValue = strToValue;
171
testapi.toCamelCase = toCamelCase;
172
testapi.readAttributes = readAttributes;
173
testapi.average = average;
174
testapi.max = max;
175
testapi.min = min;
176
testapi.getEditorHeight = getEditorHeight;
177
testapi.strToRange = strToRange;
178
testapi.raf = raf;
179
/* end-test-code *//**
180
 * The constructor.
181
 * See Mivhal.defaults for available options.
182
 * 
183
 * @param {DOMElement} selection
184
 * @param {Object} options
185
 */
186
function Mivhak( selection, options )
187
{   
188
    // Bail if there are no resources
189
    if(!selection.getElementsByTagName('PRE').length) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
190
    
191
    this.$selection = $( selection );
192
    this.setOptions( options );
193
    this.init();
194
}
195
196
/**
197
 * Check if a given string represents a supported method
198
 * @param {string} method
199
 */
200
Mivhak.methodExists = function( method )
201
{
202
    return typeof method === 'string' && Mivhak.methods[method];
203
};
204
205
/**
206
 * Initiate the code viewer.
207
 */
208
Mivhak.prototype.init = function() 
209
{
210
    this.initState();
211
    this.parseResources();
212
    this.createUI();
213
    this.applyOptions();
214
    this.callMethod('showTab',0); // Show first tab initially
215
};
216
217
/**
218
 * Apply the options that were set by the user. This function is called when
219
 * Mivhak is initiated, and every time the options are updated.
220
 */
221
Mivhak.prototype.applyOptions = function() 
222
{
223
    this.callMethod('setHeight', this.options.height);
224
    this.callMethod('setAccentColor', this.options.accentColor);
225
    if(this.options.collapsed) this.callMethod('collapse');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
226
    if(!this.options.topbar) this.$selection.addClass('mivhak-no-topbar');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
227
    else this.$selection.removeClass('mivhak-no-topbar');
228
    
229
    this.createCaption();
230
    this.createLivePreview();
231
};
232
233
/**
234
 * Initiate this instance's state.
235
 */
236
Mivhak.prototype.initState = function() 
237
{
238
    this.state = {
239
        lineWrap:   true,
240
        collapsed:  false,
241
        height:     0,
242
        activeTab:  null,   // Updated by tabs.showTab
243
        resources:    []      // Generated by parseResources()
244
    };
245
};
246
247
/**
248
 * Set or update this instance's options.
249
 * @param {object} options
250
 */
251
Mivhak.prototype.setOptions = function( options ) 
252
{
253
    // If options were already set, update them
254
    if( typeof this.options !== 'undefined' )
255
        this.options = $.extend(true, {}, this.options, options, readAttributes(this.$selection[0]));
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
256
    
257
    // Otherwise, merge them with the defaults
258
    else this.options = $.extend(true, {}, Mivhak.defaults, options, readAttributes(this.$selection[0]));
259
};
260
261
/**
262
 * Call one of Mivhak's methods. See Mivhak.methods for available methods.
263
 * To apply additional arguments, simply pass the arguments after the methodName
264
 * i.e. callMethod('methodName', arg1, arg2).
265
 * This method is also called internally when making a method call through jQuery
266
 * i.e. $('#el').mivhak('methodName', arg1, arg2);
267
 * 
268
 * @param {string} methodName
269
 */
270
Mivhak.prototype.callMethod = function( methodName )
271
{
272
    if(Mivhak.methodExists(methodName))
273
    {
274
        // Call the method with the original arguments, removing the method's name from the list
275
        var args = [];
276
        Array.prototype.push.apply( args, arguments );
277
        args.shift();
278
        Mivhak.methods[methodName].apply(this, args);
279
    }
280
};
281
282
/**
283
 * Create the user interface.
284
 */
285
Mivhak.prototype.createUI = function() 
286
{
287
    this.tabs = Mivhak.render('tabs',{mivhakInstance: this});
288
    this.topbar = Mivhak.render('top-bar',{mivhakInstance: this});
289
    this.notifier = Mivhak.render('notifier');
290
    
291
    this.$selection.prepend(this.tabs.$el);
292
    this.$selection.prepend(this.topbar.$el);
293
    this.tabs.$el.prepend(this.notifier.$el);
294
};
295
296
/**
297
 * Calculate the height in pixels.
298
 * 
299
 * auto: Automatically calculate the height based on the number of lines.
300
 * min: Calculate the height based on the height of the tab with the maximum number of lines
301
 * max: Calculate the height based on the height of the tab with the minimum number of lines
302
 * average: Calculate the height based on the average height of all tabs
303
 * 
304
 * @param {string|number} h One of (auto|min|max|average) or a custom number
305
 * @returns {Number}
306
 */
307
Mivhak.prototype.calculateHeight = function(h)
308
{
309
    var heights = [],
310
        padding = this.options.padding*2,
311
        i = this.tabs.tabs.length;
312
313
    while(i--)
314
        heights.push(getEditorHeight($(this.tabs.tabs[i].resource.pre))+padding);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
315
316
    if('average' === h) return average(heights);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
317
    if('shortest' === h) return min(heights);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
318
    if('longest' === h) return max(heights);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
319
    if('auto' === h) return getEditorHeight($(this.activeTab.resource.pre))+padding;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
320
    if(!isNaN(h)) return parseInt(h);
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !isNaN(h) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
321
};
322
323
/**
324
 * Loop through each PRE element inside this.$selection and store it's options
325
 * in this.resources, merging it with the default option values.
326
 */
327
Mivhak.prototype.parseResources = function()
328
{
329
    var $this = this;
330
    
331
    this.resources = new Resources();
332
    this.$selection.find('pre').each(function(){
333
        $this.resources.add(this);
334
    });
335
};
336
337
Mivhak.prototype.createCaption = function()
338
{
339
    if(this.options.caption)
340
    {
341
        if(!this.caption)
342
        {
343
            this.caption = Mivhak.render('caption',{text: this.options.caption});
344
            this.$selection.append(this.caption.$el);
345
        }
346
        else this.caption.setText(this.options.caption);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
347
    }
348
    else this.$selection.addClass('mivhak-no-caption');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
349
};
350
351
/**
352
 * Create the live preview iframe window
353
 */
354
Mivhak.prototype.createLivePreview = function()
355
{
356
    if(this.options.runnable && typeof this.preview === 'undefined')
357
    {
358
        this.preview = Mivhak.render('live-preview',{resources: this.resources});
359
        this.tabs.$el.append(this.preview.$el);
360
    }
361
};
362
363
/**
364
 * Remove all generated elements, data and events.
365
 * 
366
 * TODO: keep initial HTML
367
 */
368
Mivhak.prototype.destroy = function() 
369
{
370
    this.$selection.empty();
371
};
372
373
/* test-code */
374
testapi.mivhak = Mivhak;
375
/* end-test-code *//**
376
 * A list of Mivhak default options
377
 */
378
Mivhak.defaults = {
379
    
380
    /**
381
     * Whether to add a live preview (and a "play" button) to run the code
382
     * @type Boolean
383
     */
384
    runnable:       false,
385
    
386
    /**
387
     * Whther to allow the user to edit the code
388
     * @type Boolean
389
     */
390
    editable:       false,
391
    
392
    /**
393
     * Whether to show line numers on the left
394
     * @type Boolean
395
     */
396
    lineNumbers:    false,
397
    
398
    /**
399
     * One of the supported CSS color values (HEX, RGB, etc...) to set as the 
400
     * code viewer's accent color. Controls the scrollbars, tab navigation and 
401
     * dropdown item colors.
402
     * @type String
403
     */
404
    accentColor:    false,
405
    
406
    /**
407
     * Whether to collapse the code viewer initially
408
     * @type Boolean
409
     */
410
    collapsed:      false,
411
    
412
    /**
413
     * Text/HTML string to be displayed at the bottom of the code viewer
414
     * @type Boolean|string
415
     */
416
    caption:        false,
417
    
418
    /**
419
     * The code viewer's theme. One of (dark|light)
420
     * @type String
421
     */
422
    theme:          'light',
423
    
424
    /**
425
     * The code viewer's height. Either a number (for a custom height in pixels) 
426
     * or one of (auto|min|max|average).
427
     * @type String|Number
428
     */
429
    height:         'average',
430
    
431
    /**
432
     * The surrounding padding between the code and the wrapper.
433
     * @type Number
434
     */
435
    padding:        15,
436
    
437
    /**
438
     * Whether to show/hide the top bar
439
     * @type Boolean
440
     */
441
    topbar: true,
442
    
443
    /**
444
     * An array of strings/objects for the settings dropdown menu
445
     * @type Array
446
     */
447
    buttons:        ['wrap','copy','collapse','about']
448
};
449
450
/**
451
 * A list of Mivhak resource default settings (Mivhak resources are any <pre> 
452
 * elements placed inside a Mivhak wrapper element).
453
 */
454
Mivhak.resourceDefaults = {
455
    
456
    /**
457
     * The resource language (one of the supported Ace Editor languages)
458
     * @type string
459
     */
460
    lang:           null,
461
    
462
    /**
463
     * How the resource should be treated in the preview window. One of (script|style|markup)
464
     * @type bool|string
465
     */
466
    runAs:          false,
467
    
468
    /**
469
     * A URL to an external source
470
     * @type bool|string
471
     */
472
    source:         false,
473
    
474
    /**
475
     * Whether to show this resource as a tab. Useful if you want to include
476
     * external libraries for the live preview and don't need to see their contents.
477
     * @type Boolean
478
     */
479
    visible:        true,
480
    
481
    /**
482
     * Mark/highlight a range of lines given as a string in the format '1, 3-4'
483
     * @type bool|string
484
     */
485
    mark:           false,
486
    
487
    /**
488
     * Set the initial line number (1 based).
489
     * @type Number
490
     */
491
    startLine:      1
492
};var Resources = function() {
493
    this.data = [];
494
};
495
496
Resources.prototype.count = function() {
497
    return this.data.length;
498
};
499
500
Resources.prototype.add = function(pre) {
501
    this.data.push($.extend({},
502
        Mivhak.resourceDefaults,{
503
            pre:pre, 
504
            content: pre.textContent
505
        },
506
        readAttributes(pre)
507
    ));
508
};
509
510
Resources.prototype.get = function(i) {
511
    return this.data[i];
512
};
513
514
Resources.prototype.update = function(i, content) {
515
    this.data[i].content = content;
516
};
517
518
// Built-in buttons
519
Mivhak.buttons = {
520
    
521
    /**
522
     * The wrap button features a toggle button and is used to toggle line wrap
523
     * on/off for the currently active tab
524
     */
525
    wrap: {
526
        text: 'Wrap Lines', 
527
        toggle: true, 
528
        click: function(e) {
529
            e.stopPropagation();
530
            this.callMethod('toggleLineWrap');
531
        }
532
    },
533
    
534
    /**
535
     * The copy button copies the code in the currently active tab to clipboard
536
     * (except for Safari, where it selects the code and prompts the user to press command+c)
537
     */
538
    copy: {
539
        text: 'Copy',
540
        click: function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
541
            this.callMethod('copyCode');
542
        }
543
    },
544
    
545
    /**
546
     * The collapse button toggles the entire code viewer into and out of its
547
     * collapsed state.
548
     */
549
    collapse: {
550
        text: 'Colllapse',
551
        click: function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
552
            this.callMethod('collapse');
553
        }
554
    },
555
    
556
    /**
557
     * The about button shows the user information about Mivhak
558
     */
559
    about: {
560
        text: 'About Mivhak',
561
        click: function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
562
            this.notifier.closableNotification('Mivhak.js v1.0.0');
563
        }
564
    }
565
};/**
566
 * jQuery plugin's methods. 
567
 * In all methods, the 'this' keyword is pointing to the calling instance of Mivhak.
568
 * These functions serve as the plugin's public API.
569
 */
570
Mivhak.methods = {
571
    
572
    /**
573
     * Toggle line wrap on/off for the currently active tab. Initially set to 
574
     * on (true) by default.
575
     */
576
    toggleLineWrap: function() {
577
        var $this = this;
578
        this.state.lineWrap = !this.state.lineWrap;
579
        $.each(this.tabs.tabs, function(i,tab) {
580
            tab.editor.getSession().setUseWrapMode($this.state.lineWrap);
581
            tab.vscroll.refresh();
582
            tab.hscroll.refresh();
583
        });
584
    },
585
    
586
    /**
587
     * copy the code in the currently active tab to clipboard (works in all
588
     * browsers apart from Safari, where it selects the code and prompts the 
589
     * user to press command+c)
590
     */
591
    copyCode: function() {
592
        var editor = this.activeTab.editor;
593
        editor.selection.selectAll();
594
        editor.focus();
595
        if(document.execCommand('copy')) {
596
            editor.selection.clearSelection();
597
            this.notifier.timedNotification('Copied to clipboard!', 2000);
598
        }
599
        else this.notifier.timedNotification('Press &#8984;+C to copy the code', 2000);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
600
    },
601
    
602
    /**
603
     * Collapse the code viewer and show a "Show Code" button.
604
     */
605
    collapse: function() {
606
        if(this.state.collapsed) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
607
        var $this = this;
608
        this.state.collapsed = true;
609
        this.notifier.closableNotification('Show Code', function(){$this.callMethod('expand');});
610
        this.$selection.addClass('mivhak-collapsed');
611
        this.callMethod('setHeight',this.notifier.$el.outerHeight(true));
612
    },
613
    
614
    /**
615
     * Expand the code viewer if it's collapsed;
616
     */
617
    expand: function() {
618
        if(!this.state.collapsed) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
619
        this.state.collapsed = false;
620
        this.notifier.hide(); // In case it's called by an external script
621
        this.$selection.removeClass('mivhak-collapsed');
622
        this.callMethod('setHeight',this.options.height);
623
    },
624
    
625
    /**
626
     * Show/activate a tab by the given index (zero based).
627
     * @param {number} index
628
     */
629
    showTab: function(index) {
630
        this.tabs.showTab(index);
631
        this.topbar.activateNavTab(index);
632
        if(this.options.runnable)
633
            this.preview.hide();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
634
    },
635
    
636
    /**
637
     * Set the height of the code viewer. One of (auto|min|max|average) or 
638
     * a custom number.
639
     * @param {string|number} height
640
     */
641
    setHeight: function(height) {
642
        var $this = this;
643
        raf(function(){
644
            $this.state.height = $this.calculateHeight(height);
645
            $this.tabs.$el.height($this.state.height);
646
            $.each($this.tabs.tabs, function(i,tab) {
647
                $(tab.resource.pre).height(height);
648
                tab.editor.resize();
649
                tab.vscroll.refresh();
650
                tab.hscroll.refresh();
651
            });
652
        });
653
    },
654
    
655
    /**
656
     * Set the code viewer's accent color. Applied to the nav-tabs text color, 
657
     * underline, scrollbars and dropdown menu text color.
658
     * 
659
     * @param {string} color
660
     */
661
    setAccentColor: function(color) {
662
        if(!color) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
663
        this.topbar.$el.find('.mivhak-top-bar-button').css({'color': color});
664
        this.topbar.$el.find('.mivhak-dropdown-button').css({'color': color});
665
        this.topbar.$el.find('.mivhak-controls svg').css({'fill': color});
666
        this.tabs.$el.find('.mivhak-scrollbar-thumb').css({'background-color': color});
667
        this.topbar.line.css({'background-color': color});
668
    }
669
};Mivhak.icons = {};
670
671
// <div>Icons made by <a href="http://www.flaticon.com/authors/egor-rumyantsev" title="Egor Rumyantsev">Egor Rumyantsev</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div>
672
Mivhak.icons.play = '<svg viewBox="0 0 232.153 232.153"><g><path style="fill-rule:evenodd;clip-rule:evenodd;" d="M203.791,99.628L49.307,2.294c-4.567-2.719-10.238-2.266-14.521-2.266c-17.132,0-17.056,13.227-17.056,16.578v198.94c0,2.833-0.075,16.579,17.056,16.579c4.283,0,9.955,0.451,14.521-2.267l154.483-97.333c12.68-7.545,10.489-16.449,10.489-16.449S216.471,107.172,203.791,99.628z"/></g></svg>';
673
674
// <div>Icons made by <a href="http://www.flaticon.com/authors/dave-gandy" title="Dave Gandy">Dave Gandy</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div>
675
Mivhak.icons.cog = '<svg viewbox="0 0 438.529 438.529"><g><path d="M436.25,181.438c-1.529-2.002-3.524-3.193-5.995-3.571l-52.249-7.992c-2.854-9.137-6.756-18.461-11.704-27.98c3.422-4.758,8.559-11.466,15.41-20.129c6.851-8.661,11.703-14.987,14.561-18.986c1.523-2.094,2.279-4.281,2.279-6.567c0-2.663-0.66-4.755-1.998-6.28c-6.848-9.708-22.552-25.885-47.106-48.536c-2.275-1.903-4.661-2.854-7.132-2.854c-2.857,0-5.14,0.855-6.854,2.567l-40.539,30.549c-7.806-3.999-16.371-7.52-25.693-10.565l-7.994-52.529c-0.191-2.474-1.287-4.521-3.285-6.139C255.95,0.806,253.623,0,250.954,0h-63.38c-5.52,0-8.947,2.663-10.278,7.993c-2.475,9.513-5.236,27.214-8.28,53.1c-8.947,2.86-17.607,6.476-25.981,10.853l-39.399-30.549c-2.474-1.903-4.948-2.854-7.422-2.854c-4.187,0-13.179,6.804-26.979,20.413c-13.8,13.612-23.169,23.841-28.122,30.69c-1.714,2.474-2.568,4.664-2.568,6.567c0,2.286,0.95,4.57,2.853,6.851c12.751,15.42,22.936,28.549,30.55,39.403c-4.759,8.754-8.47,17.511-11.132,26.265l-53.105,7.992c-2.093,0.382-3.9,1.621-5.424,3.715C0.76,182.531,0,184.722,0,187.002v63.383c0,2.478,0.76,4.709,2.284,6.708c1.524,1.998,3.521,3.195,5.996,3.572l52.25,7.71c2.663,9.325,6.564,18.743,11.704,28.257c-3.424,4.761-8.563,11.468-15.415,20.129c-6.851,8.665-11.709,14.989-14.561,18.986c-1.525,2.102-2.285,4.285-2.285,6.57c0,2.471,0.666,4.658,1.997,6.561c7.423,10.284,23.125,26.272,47.109,47.969c2.095,2.094,4.475,3.138,7.137,3.138c2.857,0,5.236-0.852,7.138-2.563l40.259-30.553c7.808,3.997,16.371,7.519,25.697,10.568l7.993,52.529c0.193,2.471,1.287,4.518,3.283,6.14c1.997,1.622,4.331,2.423,6.995,2.423h63.38c5.53,0,8.952-2.662,10.287-7.994c2.471-9.514,5.229-27.213,8.274-53.098c8.946-2.858,17.607-6.476,25.981-10.855l39.402,30.84c2.663,1.712,5.141,2.563,7.42,2.563c4.186,0,13.131-6.752,26.833-20.27c13.709-13.511,23.13-23.79,28.264-30.837c1.711-1.902,2.569-4.09,2.569-6.561c0-2.478-0.947-4.862-2.857-7.139c-13.698-16.754-23.883-29.882-30.546-39.402c3.806-7.043,7.519-15.701,11.136-25.98l52.817-7.988c2.279-0.383,4.189-1.622,5.708-3.716c1.523-2.098,2.279-4.288,2.279-6.571v-63.376C438.533,185.671,437.777,183.438,436.25,181.438z M270.946,270.939c-14.271,14.277-31.497,21.416-51.676,21.416c-20.177,0-37.401-7.139-51.678-21.416c-14.272-14.271-21.411-31.498-21.411-51.673c0-20.177,7.135-37.401,21.411-51.678c14.277-14.272,31.504-21.411,51.678-21.411c20.179,0,37.406,7.139,51.676,21.411c14.274,14.277,21.413,31.501,21.413,51.678C292.359,239.441,285.221,256.669,270.946,270.939z"/></g></svg>';/**
676
 * The list of registered components.
677
 * 
678
 * @type Array
679
 */
680
Mivhak.components = [];
681
682
/**
683
 * Register a new component
684
 * 
685
 * @param {string} name The components name
686
 * @param {Object} options A list of component properties
687
 */
688
Mivhak.component = function(name, options)
689
{
690
    Mivhak.components[name] = options;
691
};
692
693
/**
694
 * Render a new component
695
 * 
696
 * TODO: move this into a seperate library
697
 * 
698
 * @param {string} name The components name
699
 * @param {Object} props A list of component properties. 
700
 * This overrides the component's initial property values.
701
 */
702
Mivhak.render = function(name, props)
703
{
704
    var component = $.extend(true, {}, Mivhak.components[name]);
705
    var el = {};
706
    
707
    // Create the element from the template
708
    el.$el = $(component.template);
709
    
710
    // Create all methods
711
    $.each(component.methods, function(name, method){
712
        el[name] = function() {return method.apply(el,arguments);};
713
    });
714
    
715
    // Set properties
716
    $.each(component.props, function(name, prop){
717
        el[name] = (typeof props !== 'undefined' && props.hasOwnProperty(name) ? props[name] : prop);
718
    });
719
    
720
    // Bind events
721
    $.each(component.events, function(name, method){
722
        el.$el.on(name, function() {return method.apply(el,arguments);});
723
    });
724
    
725
    // Call the 'created' function if exists
726
    if(component.hasOwnProperty('created')) component.created.call(el);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
727
    
728
    return el;
729
};Mivhak.component('caption', {
730
    template: '<div class="mivhak-caption"></div>',
731
    props: {
732
        text: null
733
    },
734
    created: function() {
735
        this.setText(this.text);
736
    },
737
    methods: {
738
        setText: function(text) {
739
            this.$el.html(text);
740
        }
741
    }
742
});Mivhak.component('dropdown', {
743
    template: '<div class="mivhak-dropdown"></div>',
744
    props: {
745
        items: [],
746
        mivhakInstance: null,
747
        visible: false
748
    },
749
    created: function() {
750
        var $this = this;
751
        $.each(this.items, function(i, item) {
752
            if( typeof item === 'string') item = Mivhak.buttons[item];
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
753
            var button = $('<div>',{class: 'mivhak-dropdown-button', text: item.text, click: function(e){item.click.call($this.mivhakInstance,e);}});
754
            if(item.toggle) 
755
            {
756
                button.$toggle = Mivhak.render('toggle');
757
                
758
                // Toggle only if not clicking on the toggle itself (which makes it toggle as it is)
759
                button.click(function(e){if($(e.target).parents('.mivhak-dropdown-button').length !== 1)button.$toggle.toggle();});
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
760
                button.append(button.$toggle.$el);
761
            }
762
            $this.$el.append(button);
763
        });
764
        
765
        // Hide dropdown on outside click
766
        $(window).click(function(e){
767
            if(!$(e.target).closest('.mivhak-icon-cog').length) {
768
                $this.$el.removeClass('mivhak-dropdown-visible');
769
            }
770
        });
771
    },
772
    methods: {
773
        toggle: function() {
774
            this.visible = !this.visible;
775
            this.$el.toggleClass('mivhak-dropdown-visible');
776
        }
777
    }
778
});Mivhak.component('horizontal-scrollbar', {
779
    template: '<div class="mivhak-scrollbar mivhak-h-scrollbar"><div class="mivhak-scrollbar-thumb"></div></div>',
780
    props: {
781
        editor: null,
782
        $inner: null,
783
        $outer: null,
784
        mivhakInstance: null,
785
        minWidth: 50,
786
        state: {
787
            a: 0,    // The total width of the editor
788
            b: 0,    // The width of the viewport, excluding padding
789
            c: 0,    // The width of the viewport, including padding
790
            d: 0,    // The calculated width of the thumb
791
            l: 0     // The current left offset of the viewport
792
        },
793
        initialized: false
794
    },
795
    methods: {
796
        initialize: function() {
797
            if(!this.initialized)
798
            {
799
                this.initialized = true;
800
                this.dragDealer();
801
                var $this = this;
802
                $(window).resize(function(){
803
                    if(!$this.mivhakInstance.state.lineWrap)
804
                        $this.refresh();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
805
                });
806
            }
807
            this.refresh();
808
        },
809
        updateState: function() {
810
            var oldState = $.extend({}, this.state);
811
            this.state.a = this.getEditorWidth();
812
            this.state.b = this.$outer.parent().width();
813
            this.state.c = this.state.b - this.mivhakInstance.options.padding*2;
814
            this.state.d = Math.max(this.state.c*this.state.b/this.state.a,this.minWidth);
815
            this.state.l *=  this.state.a/Math.max(oldState.a,1); // Math.max used to prevent division by zero
816
            return this.state.a !== oldState.a || this.state.b !== oldState.b;
817
        },
818
        refresh: function() {
819
            var $this = this, oldLeft = this.state.l;
820
            raf(function(){
821
                if($this.updateState())
822
                {
823
                    if($this.getDifference() > 0)
824
                    {
825
                        $this.doScroll('left',oldLeft-$this.state.l);
826
                        $this.$el.css({width: $this.state.d + 'px', left: 0});
827
                        $this.moveBar();
828
                    }
829
                    else 
830
                    {
831
                        $this.doScroll('left',$this.state.l);
832
                        $this.$el.css({width: 0});
833
                    }
834
                }
835
            });
836
        },
837
        dragDealer: function(){
838
            var $this = this,
839
                lastPageX;
840
841
            this.$el.on('mousedown.drag', function(e) {
842
                lastPageX = e.pageX;
843
                $this.$el.add(document.body).addClass('mivhak-scrollbar-grabbed');
844
                $(document).on('mousemove.drag', drag).on('mouseup.drag', stop);
845
                return false;
846
            });
847
848
            function drag(e){
849
                var delta = e.pageX - lastPageX,
850
                    didScroll;
851
852
                // Bail if the mouse hasn't moved
853
                if(!delta) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
854
            
855
                lastPageX = e.pageX;
856
                
857
                raf(function(){
858
                    didScroll = $this.doScroll(delta > 0 ? 'right' : 'left', Math.abs(delta*$this.getEditorWidth()/$this.$outer.parent().width()));
859
                    if(0 !== didScroll) $this.moveBar();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
860
                });
861
            }
862
863
            function stop() {
864
                $this.$el.add(document.body).removeClass('mivhak-scrollbar-grabbed');
865
                $(document).off("mousemove.drag mouseup.drag");
866
            }
867
        },
868
        moveBar: function() {
869
            this.$el.css({
870
                left:  (this.state.b-this.state.d)/(this.state.a-this.state.c)*this.state.l + 'px'
871
            });
872
        },
873
        
874
        /**
875
         * Scrolls the editor element in the direction given, provided that there 
876
         * is remaining scroll space
877
         * @param {string} dir
878
         * @param {int} delta
879
         */
880
        doScroll: function(dir, delta) {
881
            var s = this.state,
882
                remaining,
883
                didScroll;
884
            
885
            if('left' === dir) 
886
            {
887
                remaining = s.l;
888
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
889
                s.l -= didScroll;
890
            }
891
            if('right' === dir) 
892
            {
893
                remaining = this.getDifference() - s.l;
894
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
895
                s.l += didScroll;
896
            }
897
            
898
            this.$inner.find('.ace_content').css({'margin-left': -s.l});
899
            return didScroll;
0 ignored issues
show
Bug introduced by
The variable didScroll does not seem to be initialized in case "left" === dir on line 885 is false. Are you sure this can never be the case?
Loading history...
900
        },
901
        
902
        /**
903
         * Returns the difference between the containing div and the editor div
904
         */
905
        getDifference: function()
906
        {
907
            return this.state.a - this.state.c;
908
        },
909
        
910
        /**
911
         * Calculate the editor's width based on the number of lines
912
         */
913
        getEditorWidth: function() {
914
            return this.$inner.find('.ace_content').width();
915
        }
916
    }
917
});Mivhak.component('live-preview', {
918
    template: '<iframe class="mivhak-live-preview" allowtransparency="true" sandbox="allow-scripts allow-pointer-lock allow-same-origin allow-popups allow-modals allow-forms" frameborder="0"></iframe>',
919
    props: {
920
        resources: []
921
    },
922
    methods: {
923
        renderHTML: function() {
924
            var html = '<html>',
925
                head = '<head>',
926
                body = '<body>';
927
            
928
            head += '<meta http-equiv="content-type" content="text/html; charset=UTF-8">';
929
            head += '<meta name="robots" content="noindex, nofollow">';
930
            head += '<meta name="googlebot" content="noindex, nofollow">';
931
            
932
            for(var i = 0; i < this.resources.count(); i++)
933
            {
934
                var source = this.resources.get(i);
935
                if('markup' === source.runAs) body += source.content;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
936
                if('style' === source.runAs) head += this.createStyle(source.content, source.visible ? false : source.source);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
937
                if('script' === source.runAs) head += this.createScript(source.content, source.visible ? false : source.source);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
938
            }
939
            
940
            html += head+'</head>'+body+'</body></html>';
941
            
942
            return html;
943
        },
944
        createScript: function(content,src) {
945
            if(src) return '<script src="'+src+'" type="text/javascript"></script>';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
946
            return '<script>\n//<![CDATA[\nwindow.onload = function(){'+content+'};//]]>\n</script>'; // @see http://stackoverflow.com/questions/66837/when-is-a-cdata-section-necessary-within-a-script-tag
947
        },
948
        createStyle: function(content,href) {
949
            if(href) return '<link href="'+href+'" rel="stylesheet">';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
950
            return '<style>'+content+'</style>';
951
        },
952
        show: function() {
953
            this.$el.addClass('mivhak-active');
954
            this.run();
955
        },
956
        hide: function() {
957
            this.$el.removeClass('mivhak-active');
958
        },
959
        run: function() {
960
            var contents = this.$el.contents(),
961
                doc = contents[0];
962
        
963
            doc.open();
964
            doc.writeln(this.renderHTML());
965
            doc.close();
966
        }
967
    }
968
});Mivhak.component('notifier', {
969
    template: '<div class="mivhak-notifier"></div>',
970
    methods: {
971
        notification: function(html) {
972
            if(!html) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
973
            clearTimeout(this.timeout);
974
            this.$el.off('click');
975
            this.$el.html(html);
976
            this.$el.addClass('mivhak-visible');
977
        },
978
        timedNotification: function(html, timeout) {
979
            var $this = this;
980
            this.notification(html);
981
            this.timeout = setTimeout(function(){
982
                $this.hide();
983
            },timeout);
984
        },
985
        closableNotification: function(html, onclick)
986
        {
987
            var $this = this;
988
            this.notification(html);
989
            this.$el.addClass('mivhak-button');
990
            this.$el.click(function(e){
991
                $this.hide();
992
                if(typeof onclick !== 'undefined')
993
                    onclick.call(null, e);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
994
            });
995
        },
996
        hide: function() {
997
            this.$el.removeClass('mivhak-visible mivhak-button');
998
        }
999
    }
1000
});Mivhak.component('tab-pane', {
1001
    template: '<div class="mivhak-tab-pane"><div class="mivhak-tab-pane-inner"></div></div>',
1002
    props: {
1003
        resource:       null,
1004
        editor:         null,
1005
        index:          null,
1006
        padding:        10,
1007
        mivhakInstance: null
1008
    },
1009
    created: function() {
1010
        this.setEditor();
1011
        this.fetchRemoteSource();
1012
        this.markLines();
1013
        
1014
        this.$el = $(this.resource.pre).wrap(this.$el).parent().parent();
1015
        this.$el.find('.mivhak-tab-pane-inner').css({margin: this.mivhakInstance.options.padding});
1016
        this.setScrollbars();
1017
        
1018
    },
1019
    methods: {
1020
        getTheme: function() {
1021
            return this.mivhakInstance.options.theme === 'light' ? 'clouds' : 'ambiance';
1022
        },
1023
        fetchRemoteSource: function() {
1024
            var $this = this;
1025
            if(this.resource.source) {
1026
                $.ajax(this.resource.source).done(function(res){
1027
                    $this.editor.setValue(res,-1);
1028
                    
1029
                    // Refresh code viewer height
1030
                    $this.mivhakInstance.callMethod('setHeight',$this.mivhakInstance.options.height);
1031
                    
1032
                    // Refresh scrollbars
1033
                    raf(function(){
1034
                        $this.vscroll.refresh();
1035
                        $this.hscroll.refresh();
1036
                    });
1037
                });
1038
                
1039
            }
1040
        },
1041
        setScrollbars: function() {
1042
            var $inner = $(this.resource.pre),
1043
                $outer = this.$el.find('.mivhak-tab-pane-inner');
1044
            
1045
            this.vscroll = Mivhak.render('vertical-scrollbar',{editor: this.editor, $inner: $inner, $outer: $outer, mivhakInstance: this.mivhakInstance});
1046
            this.hscroll = Mivhak.render('horizontal-scrollbar',{editor: this.editor, $inner: $inner, $outer: $outer, mivhakInstance: this.mivhakInstance});
1047
            
1048
            this.$el.append(this.vscroll.$el, this.hscroll.$el);
1049
        },
1050
        show: function() {
1051
            this.$el.addClass('mivhak-tab-pane-active');
1052
            this.editor.focus();
1053
            this.editor.gotoLine(0); // Needed in order to get focus
1054
            
1055
            // Recalculate scrollbar positions based on the now visible element
1056
            this.vscroll.initialize();
1057
            this.hscroll.initialize();
1058
        },
1059
        hide: function() {
1060
            this.$el.removeClass('mivhak-tab-pane-active');
1061
        },
1062
        setEditor: function() {
1063
            
1064
            // Remove redundant space from code
1065
            this.resource.pre.textContent = this.resource.pre.textContent.trim(); 
1066
            
1067
            // Set editor options
1068
            this.editor = ace.edit(this.resource.pre);
0 ignored issues
show
Bug introduced by
The variable ace seems to be never declared. If this is a global, consider adding a /** global: ace */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1069
            this.editor.setReadOnly(!this.mivhakInstance.options.editable);
1070
            this.editor.setTheme("ace/theme/"+this.getTheme());
1071
            this.editor.setShowPrintMargin(false);
1072
            this.editor.renderer.setShowGutter(this.mivhakInstance.options.lineNumbers);
1073
            this.editor.getSession().setMode("ace/mode/"+this.resource.lang);
1074
            this.editor.getSession().setUseWorker(false); // Disable syntax checking
1075
            this.editor.getSession().setUseWrapMode(true); // Set initial line wrapping
1076
1077
            this.editor.setOptions({
1078
                maxLines: Infinity,
1079
                firstLineNumber: this.resource.startLine,
1080
                highlightActiveLine: false,
1081
                fontSize: parseInt(14)
1082
            });
1083
            
1084
            // Update source content for the live preview
1085
            if(this.mivhakInstance.options.editable)
1086
            {
1087
                var $this = this;
1088
                this.editor.getSession().on('change', function(a,b,c) {
0 ignored issues
show
Unused Code introduced by
The parameter b is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter c is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter a is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1089
                    $this.mivhakInstance.resources.update($this.index, $this.editor.getValue());
1090
                });
1091
            }
1092
        },
1093
        markLines: function()
1094
        {
1095
            if(!this.resource.mark) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1096
            var ranges = strToRange(this.resource.mark),
1097
                i = ranges.length,
1098
                AceRange = ace.require("ace/range").Range;
0 ignored issues
show
Bug introduced by
The variable ace seems to be never declared. If this is a global, consider adding a /** global: ace */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1099
1100
            while(i--)
1101
            {
1102
                this.editor.session.addMarker(
1103
                    new AceRange(ranges[i].start, 0, ranges[i].end, 1), // Define the range of the marker
1104
                    "ace_active-line",     // Set the CSS class for the marker
1105
                    "fullLine"             // Marker type
1106
                );
1107
            }
1108
        }
1109
    }
1110
});Mivhak.component('tabs', {
1111
    template: '<div class="mivhak-tabs"></div>',
1112
    props: {
1113
        mivhakInstance: null,
1114
        activeTab: null,
1115
        tabs: []
1116
    },
1117
    created: function() {
1118
        var $this = this;
1119
        this.$el = this.mivhakInstance.$selection.find('pre').wrapAll(this.$el).parent();
1120
        $.each(this.mivhakInstance.resources.data,function(i, resource){
1121
            if(resource.visible)
1122
                $this.tabs.push(Mivhak.render('tab-pane',{
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1123
                    resource: resource,
1124
                    index: i,
1125
                    mivhakInstance: $this.mivhakInstance
1126
                }));
1127
        });
1128
    },
1129
    methods: {
1130
        showTab: function(index){
1131
            var $this = this;
1132
            $.each(this.tabs, function(i, tab){
1133
                if(index === i) {
1134
                    $this.mivhakInstance.activeTab = tab;
1135
                    tab.show();
1136
                }
1137
                else tab.hide();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1138
            });
1139
        }
1140
    }
1141
});Mivhak.component('toggle', {
1142
    template: '<div class="mivhak-toggle"><div class="mivhak-toggle-knob"></div></div>',
1143
    props: {
1144
        on: true
1145
    },
1146
    events: {
1147
        click: function() {
1148
            this.toggle();
1149
        }
1150
    },
1151
    created: function() {
1152
        this.$el.addClass('mivhak-toggle-'+(this.on?'on':'off'));
1153
    },
1154
    methods: {
1155
        toggle: function() {
1156
            this.on = !this.on;
1157
            this.$el.toggleClass('mivhak-toggle-on').toggleClass('mivhak-toggle-off');
1158
        }
1159
    }
1160
});Mivhak.component('top-bar-button', {
1161
    template: '<div class="mivhak-top-bar-button"></div>',
1162
    props: {
1163
        text: null,
1164
        icon: null,
1165
        dropdown: null,
1166
        mivhakInstance: null,
1167
        onClick: function(){}
1168
    },
1169
    events: {
1170
        click: function() {
1171
            this.onClick();
1172
        }
1173
    },
1174
    created: function() {
1175
        var $this = this;
1176
        this.$el.text(this.text);
1177
        if(this.icon) this.$el.addClass('mivhak-icon mivhak-icon-'+this.icon).append($(Mivhak.icons[this.icon]));
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1178
        if(this.dropdown) 
1179
        {
1180
            $this.$el.append(this.dropdown.$el);
1181
            this.onClick = function() {
1182
                $this.toggleActivation();
1183
                $this.dropdown.toggle();
1184
            };
1185
        }
1186
    },
1187
    methods: {
1188
        activate: function() {
1189
            this.$el.addClass('mivhak-button-active');
1190
        },
1191
        deactivate: function() {
1192
            this.$el.removeClass('mivhak-button-active');
1193
        },
1194
        toggleActivation: function() {
1195
            this.$el.toggleClass('mivhak-button-active');
1196
        },
1197
        isActive: function() {
1198
            return this.$el.hasClass('mivhak-button-active');
1199
        }
1200
    }
1201
});Mivhak.component('top-bar', {
1202
    template: '<div class="mivhak-top-bar"><div class="mivhak-nav-tabs"></div><div class="mivhak-controls"></div><div class="mivhak-line"></div></div>',
1203
    props: {
1204
        mivhakInstance: null,
1205
        navTabs: [],
1206
        controls: [],
1207
        line: null
1208
    },
1209
    created: function() {
1210
        this.line = this.$el.find('.mivhak-line');
1211
        this.createTabNav();
1212
        if(this.mivhakInstance.options.runnable) this.createPlayButton();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1213
        this.createCogButton();
1214
    },
1215
    methods: {
1216
        activateNavTab: function(index) {
1217
            var button = this.navTabs[index];
1218
            // Deactivate all tabs and activate this tab
1219
            $.each(this.navTabs, function(i,navTab){navTab.deactivate();});
1220
            button.activate();
1221
1222
            // Position the line
1223
            this.moveLine(button.$el);
1224
        },
1225
        moveLine: function($el) {
1226
            if(typeof $el === 'undefined') {
1227
                this.line.removeClass('mivhak-visible');
1228
                return;
1229
            }
1230
            this.line.width($el.width());
1231
            this.line.css({left:$el.position().left + ($el.outerWidth() - $el.width())/2});
1232
            this.line.addClass('mivhak-visible');
1233
        },
1234
        createTabNav: function() {
1235
            var source, i, pos = 0;
1236
            for(i = 0; i < this.mivhakInstance.resources.count(); i++)
1237
            {
1238
                source = this.mivhakInstance.resources.get(i);
1239
                if(source.visible) this.createNavTabButton(pos++, source.lang);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1240
            }
1241
        },
1242
        createNavTabButton: function(i, lang) {
1243
            var $this = this,
1244
                button = Mivhak.render('top-bar-button',{
1245
                text: lang,
1246
                onClick: function() {
1247
                    $this.mivhakInstance.callMethod('showTab',i);
1248
                }
1249
            });
1250
            this.navTabs.push(button);
1251
            this.$el.find('.mivhak-nav-tabs').append(button.$el);
1252
        },
1253
        createPlayButton: function() {
1254
            var $this = this;
1255
            var playBtn = Mivhak.render('top-bar-button',{
1256
                icon: 'play',
1257
                onClick: function() {
1258
                    $this.mivhakInstance.preview.show();
1259
                    $this.moveLine();
1260
                }
1261
            });
1262
            this.controls.push(playBtn);
1263
            this.$el.find('.mivhak-controls').append(playBtn.$el);
1264
        },
1265
        createCogButton: function() {
1266
            var cogBtn = Mivhak.render('top-bar-button',{
1267
                icon: 'cog',
1268
                mivhakInstance: this.mivhakInstance,
1269
                dropdown: Mivhak.render('dropdown',{
1270
                    mivhakInstance: this.mivhakInstance,
1271
                    items: this.mivhakInstance.options.buttons
1272
                })
1273
            });
1274
            this.controls.push(cogBtn);
1275
            this.$el.find('.mivhak-controls').append(cogBtn.$el);
1276
        }
1277
    }
1278
});Mivhak.component('vertical-scrollbar', {
1279
    template: '<div class="mivhak-scrollbar mivhak-v-scrollbar"><div class="mivhak-scrollbar-thumb"></div></div>',
1280
    props: {
1281
        editor: null,
1282
        $inner: null,
1283
        $outer: null,
1284
        mivhakInstance: null,
1285
        minHeight: 50,
1286
        state: {
1287
            a: 0,    // The total height of the editor
1288
            b: 0,    // The height of the viewport, excluding padding
1289
            c: 0,    // The height of the viewport, including padding
1290
            d: 0,    // The calculated thumb height
1291
            t: 0     // The current top offset of the viewport
1292
        },
1293
        initialized: false
1294
    },
1295
    methods: {
1296
        initialize: function() {
1297
            if(!this.initialized)
1298
            {
1299
                this.initialized = true;
1300
                this.dragDealer();
1301
                var $this = this;
1302
                this.$inner.on('mousewheel', function(e){$this.onScroll.call(this, e);});
1303
                $(window).resize(function(){
1304
                    if($this.mivhakInstance.state.lineWrap)
1305
                        $this.refresh();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1306
                });
1307
            }
1308
            // Refresh anytime initialize is called
1309
            this.refresh();
1310
        },
1311
        updateState: function() {
1312
            var oldState = $.extend({}, this.state);
1313
            this.state.a = getEditorHeight(this.$inner);
1314
            this.state.b = this.mivhakInstance.state.height;
1315
            this.state.c = this.mivhakInstance.state.height-this.mivhakInstance.options.padding*2;
1316
            this.state.d = Math.max(this.state.c*this.state.b/this.state.a,this.minHeight);
1317
            this.state.t *=  this.state.a/Math.max(oldState.a,1); // Math.max used to prevent division by zero
1318
            return this.state.a !== oldState.a || this.state.b !== oldState.b;
1319
        },
1320
        refresh: function() {
1321
            var $this = this, oldTop = this.state.t;
1322
            raf(function(){
1323
                if($this.updateState())
1324
                {
1325
                    if($this.getDifference() > 0)
1326
                    {
1327
                        $this.doScroll('up',oldTop-$this.state.t);
1328
                        $this.$el.css({height: $this.state.d + 'px', top: 0});
1329
                        $this.moveBar();
1330
                    }
1331
                    else 
1332
                    {
1333
                        $this.doScroll('up',$this.state.t);
1334
                        $this.$el.css({height: 0});
1335
                    }
1336
                }
1337
            });
1338
        },
1339
        onScroll: function(e) {
1340
            var didScroll;
1341
            
1342
            if(e.deltaY > 0)
1343
                didScroll = this.doScroll('up',e.deltaY*e.deltaFactor);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1344
            else
1345
                didScroll = this.doScroll('down',-e.deltaY*e.deltaFactor);
1346
            
1347
            if(0 !== didScroll) {
1348
                this.moveBar();
1349
                e.preventDefault(); // Only prevent page scroll if the editor can be scrolled
1350
            }
1351
        },
1352
        dragDealer: function(){
1353
            var $this = this,
1354
                lastPageY;
1355
1356
            this.$el.on('mousedown.drag', function(e) {
1357
                lastPageY = e.pageY;
1358
                $this.$el.add(document.body).addClass('mivhak-scrollbar-grabbed');
1359
                $(document).on('mousemove.drag', drag).on('mouseup.drag', stop);
1360
                return false;
1361
            });
1362
1363
            function drag(e){
1364
                var delta = e.pageY - lastPageY,
1365
                    didScroll;
1366
            
1367
                // Bail if the mouse hasn't moved
1368
                if(!delta) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1369
            
1370
                lastPageY = e.pageY;
1371
                
1372
                raf(function(){
1373
                    didScroll = $this.doScroll(delta > 0 ? 'down' : 'up', Math.abs(delta*getEditorHeight($this.$inner)/$this.$outer.parent().height()));
1374
                    if(0 !== didScroll) $this.moveBar();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
1375
                });
1376
            }
1377
1378
            function stop() {
1379
                $this.$el.add(document.body).removeClass('mivhak-scrollbar-grabbed');
1380
                $(document).off("mousemove.drag mouseup.drag");
1381
            }
1382
        },
1383
        moveBar: function() {
1384
            this.$el.css({
1385
                top:  (this.state.b-this.state.d)/(this.state.a-this.state.c)*this.state.t + 'px'
1386
            });
1387
        },
1388
        
1389
        /**
1390
         * Scrolls the editor element in the direction given, provided that there 
1391
         * is remaining scroll space
1392
         * @param {string} dir
1393
         * @param {int} delta
1394
         */
1395
        doScroll: function(dir, delta) {
1396
            var s = this.state,
1397
                remaining,
1398
                didScroll;
1399
            
1400
            if('up' === dir) 
1401
            {
1402
                remaining = s.t;
1403
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
1404
                s.t -= didScroll;
1405
            }
1406
            if('down' === dir) 
1407
            {
1408
                remaining = this.getDifference() - s.t;
1409
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
1410
                s.t += didScroll;
1411
            }
1412
            
1413
            this.$inner.css({top: -s.t});
1414
            return didScroll;
0 ignored issues
show
Bug introduced by
The variable didScroll does not seem to be initialized in case "up" === dir on line 1400 is false. Are you sure this can never be the case?
Loading history...
1415
        },
1416
        
1417
        /**
1418
         * Returns the difference between the containing div and the editor div
1419
         */
1420
        getDifference: function()
1421
        {
1422
            return this.state.a - this.state.c;
1423
        }
1424
    }
1425
});/**
1426
 * Extends the functionality of jQuery to include Mivhak
1427
 * 
1428
 * @param {Function|Object} methodOrOptions
1429
 * @returns {jQuery} 
1430
 */
1431
$.fn.mivhak = function( methodOrOptions ) {
1432
1433
    // Store arguments for use with methods
1434
    var args = arguments.length > 1 ? Array.apply(null, arguments).slice(1) : null;
1435
1436
    return this.each(function(){
1437
        
1438
        // If this is an options object, set or update the options
1439
        if( typeof methodOrOptions === 'object' || !methodOrOptions )
1440
        {
1441
            // If this is the initial call for this element, instantiate a new Mivhak object
1442
            if( typeof $(this).data( 'mivhak' ) === 'undefined' ) {
1443
                var plugin = new Mivhak( this, methodOrOptions );
1444
                $(this).data( 'mivhak', plugin );
1445
            }
1446
            // Otherwise update existing settings (consequent calls will update, rather than recreate Mivhak)
1447
            else
1448
            {
1449
                $(this).data('mivhak').setOptions( methodOrOptions );
1450
                $(this).data('mivhak').applyOptions();
1451
            }
1452
        }
1453
        
1454
        // If this is a method call, run the method (if it exists)
1455
        else if( Mivhak.methodExists( methodOrOptions )  )
1456
        {
1457
            Mivhak.methods[methodOrOptions].apply($(this).data('mivhak'), args);
1458
        }
1459
    });
1460
};}( jQuery ));