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

build/mivhak.js   F

Complexity

Total Complexity 232
Complexity/F 1.65

Size

Lines of Code 1434
Function Count 141

Duplication

Duplicated Lines 1434
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
wmc 232
c 1
b 0
f 0
nc 1310720
mnd 3
bc 177
fnc 141
dl 1434
loc 1434
rs 2.4
bpm 1.2553
cpm 1.6453
noi 58

98 Functions

Rating   Name   Duplication   Size   Complexity  
B Mivhak.render 28 28 2
A mivhak.js ➔ getEditorHeight 8 8 1
A mivhak.js ➔ average 6 6 2
A Mivhak.component(ꞌtab-paneꞌ).methods.show 9 9 1
A Mivhak.component(ꞌvertical-scrollbarꞌ).methods.updateState 9 9 1
A Mivhak.component(ꞌnotifierꞌ).methods.notification 7 7 2
A Mivhak.component(ꞌtop-bar-buttonꞌ).methods.isActive 3 3 1
A Mivhak.component(ꞌvertical-scrollbarꞌ).methods.refresh 19 19 1
A Mivhak.component(ꞌtop-barꞌ).methods.createNavTabButton 11 11 1
A Resources.add 9 9 1
A Mivhak.methods.copyCode 10 10 2
A Mivhak.component(ꞌvertical-scrollbarꞌ).methods.initialize 15 15 2
A Resources.count 3 3 1
A mivhak.js ➔ max 6 6 3
A Mivhak.component(ꞌtab-paneꞌ).methods.hide 3 3 1
A Mivhak.methods.toggleLineWrap 9 9 1
A Mivhak.component(ꞌhorizontal-scrollbarꞌ).methods.getEditorWidth 3 3 1
A Mivhak.component(ꞌhorizontal-scrollbarꞌ).methods.initialize 13 13 2
A Mivhak.component(ꞌtab-paneꞌ).methods.getTheme 3 3 2
A mivhak.js ➔ ??? 1 1 1
A Mivhak.component(ꞌtop-barꞌ).methods.activateNavTab 9 9 1
A mivhak.js ➔ readAttributes 11 11 1
A Mivhak.component(ꞌtop-barꞌ).methods.createCogButton 12 12 1
A Mivhak.component(ꞌtab-paneꞌ).methods.fetchRemoteSource 18 18 2
A Mivhak.component(ꞌnotifierꞌ).methods.closableNotification 11 11 1
A Mivhak.component(ꞌhorizontal-scrollbarꞌ).methods.updateState 9 9 1
A Mivhak.component(ꞌtabsꞌ).methods.showTab 10 10 1
A Mivhak.methods.setHeight 13 13 1
A Mivhak.methodExists 4 4 1
A Mivhak.methods.expand 7 7 2
A Mivhak.component(ꞌtop-barꞌ).created 6 6 2
A Mivhak.component(ꞌtop-bar-buttonꞌ).methods.toggleActivation 3 3 1
A Resources.update 3 3 1
A Mivhak.buttons.copy.click 3 3 1
A Mivhak.component(ꞌhorizontal-scrollbarꞌ).methods.getDifference 4 4 1
A mivhak.js ➔ strToValue 7 7 4
A Mivhak.methods.showTab 6 6 2
B Mivhak.calculateHeight 15 15 7
A Mivhak.component(ꞌlive-previewꞌ).methods.run 8 8 1
A Mivhak.initState 10 10 1
A Mivhak.component(ꞌlive-previewꞌ).methods.show 4 4 1
A Mivhak.setOptions 9 9 2
A Mivhak.component(ꞌvertical-scrollbarꞌ).methods.getDifference 4 4 1
B Mivhak.component(ꞌtab-paneꞌ).methods.setEditor 31 31 2
A mivhak.js ➔ toCamelCase 6 6 1
A Mivhak.component(ꞌlive-previewꞌ).methods.hide 3 3 1
A Mivhak.destroy 4 4 1
A Mivhak.component(ꞌtoggleꞌ).created 3 3 2
A Mivhak.component(ꞌdropdownꞌ).methods.toggle 4 4 1
B Mivhak.component(ꞌlive-previewꞌ).methods.renderHTML 21 21 7
A mivhak.js ➔ min 6 6 3
A Mivhak.createCaption 13 13 3
B mivhak.js ➔ strToRange 29 29 3
A Mivhak.init 8 8 1
A Mivhak.component(ꞌtab-paneꞌ).methods.markLines 16 16 3
A Mivhak.component(ꞌlive-previewꞌ).methods.createScript 4 4 2
A Mivhak.component(ꞌtoggleꞌ).events.click 3 3 1
A Mivhak.callMethod 11 11 2
A Mivhak.createLivePreview 8 8 3
B $.fn.mivhak 30 30 2
A Mivhak.component(ꞌdropdownꞌ).created 23 23 1
A Mivhak.component(ꞌtab-paneꞌ).created 10 10 1
B Mivhak.component(ꞌvertical-scrollbarꞌ).methods.doScroll 21 21 5
A Mivhak.component(ꞌtop-barꞌ).methods.createTabNav 8 8 3
A Mivhak.component(ꞌtop-bar-buttonꞌ).created 13 13 3
A Mivhak.createUI 10 10 1
A Mivhak.component(ꞌcaptionꞌ).created 3 3 1
A Mivhak.component(ꞌtop-barꞌ).methods.moveLine 9 9 2
A Mivhak.component(ꞌtop-barꞌ).methods.createPlayButton 12 12 1
A Mivhak.buttons.about.click 3 3 1
A Mivhak.buttons.collapse.click 3 3 1
A Mivhak.applyOptions 11 11 3
A Mivhak.component(ꞌtoggleꞌ).methods.toggle 4 4 1
A Mivhak.component 4 4 1
A Mivhak.methods.setAccentColor 8 8 2
A Mivhak.component(ꞌvertical-scrollbarꞌ).methods.moveBar 5 5 1
A Mivhak.methods.collapse 8 8 2
A Mivhak.component(ꞌtop-bar-buttonꞌ).methods.deactivate 3 3 1
B Mivhak.component(ꞌvertical-scrollbarꞌ).methods.dragDealer 31 31 1
A mivhak.js ➔ Resources 3 3 1
A Mivhak.component(ꞌvertical-scrollbarꞌ).methods.onScroll 13 13 3
A Mivhak.component(ꞌtop-bar-buttonꞌ).methods.activate 3 3 1
A Mivhak.component(ꞌlive-previewꞌ).methods.createStyle 4 4 2
A Mivhak.component(ꞌtabsꞌ).created 12 12 1
A Resources.get 3 3 1
A Mivhak.component(ꞌtab-paneꞌ).methods.setScrollbars 9 9 1
A Mivhak.component(ꞌtop-bar-buttonꞌ).events.click 3 3 1
A Mivhak.component(ꞌhorizontal-scrollbarꞌ).methods.refresh 19 19 1
A Mivhak.buttons.wrap.click 4 4 1
A Mivhak.component(ꞌcaptionꞌ).methods.setText 3 3 1
A Mivhak.component(ꞌnotifierꞌ).methods.hide 3 3 1
A Mivhak.component(ꞌhorizontal-scrollbarꞌ).methods.moveBar 5 5 1
A Mivhak.component(ꞌnotifierꞌ).methods.timedNotification 7 7 1
A Mivhak.component(ꞌtop-bar-buttonꞌ).props.onClick 1 1 1
A mivhak.js ➔ Mivhak 9 9 2
B Mivhak.component(ꞌhorizontal-scrollbarꞌ).methods.dragDealer 31 31 1
B Mivhak.component(ꞌhorizontal-scrollbarꞌ).methods.doScroll 21 21 5
A Mivhak.parseResources 9 9 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like build/mivhak.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
12 View Code Duplication
(function ( $ ) {// Ace global config
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
13
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...
14
 * Converts a string to it's actual value, if applicable
15
 * 
16
 * @param {String} str
17
 */
18
function strToValue( str )
19
{
20
    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...
21
    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...
22
    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...
23
    return str;
24
}
25
26
/**
27
 * Convert hyphened text to camelCase.
28
 * 
29
 * @param {string} str
30
 * @returns {string}
31
 */
32
function toCamelCase( str )
33
{
34
    return str.replace(/-(.)/g,function(match){
35
        return match[1].toUpperCase();
36
    });
37
}
38
39
/**
40
 * Reads the element's 'miv-' attributes and returns their values as an object
41
 * 
42
 * @param {DOMElement} el
43
 * @returns {Object}
44
 */
45
function readAttributes( el ) 
46
{
47
    var options = {};
48
    $.each(el.attributes, function(i, attr){
49
        if(/^miv-/.test(attr.name))
50
        {
51
            options[toCamelCase(attr.name.substr(4))] = strToValue(attr.value);
52
        }
53
    });
54
    return options;
55
}
56
57
/**
58
 * Get the average value of all elements in the given array.
59
 * 
60
 * @param {Array} arr
61
 * @returns {Number}
62
 */
63
function average( arr )
64
{
65
    var i = arr.length, sum = 0;
66
    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...
67
    return sum/arr.length;
68
}
69
70
/**
71
 * Get the maximum value of all elements in the given array.
72
 * 
73
 * @param {Array} arr
74
 * @returns {Number}
75
 */
76
function max( arr )
77
{
78
    var i = arr.length, maxval = arr[--i];
79
    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...
80
    return maxval;
81
}
82
83
/**
84
 * Get the minimum value of all elements in the given array.
85
 * 
86
 * @param {Array} arr
87
 * @returns {Number}
88
 */
89
function min( arr )
90
{
91
    var i = arr.length, minval = arr[--i];
92
    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...
93
    return minval;
94
}
95
96
/**
97
 * Calculate the editor's height based on the number of lines & line height.
98
 * 
99
 * @param {jQuery} $editor Ther editor wrapper element (PRE)
100
 * @returns {Number}
101
 */
102
function getEditorHeight( $editor )
103
{
104
    var height = 0;
105
    $editor.find('.ace_text-layer').children().each(function(){
106
        height += $(this).height();
107
    });
108
    return height;
109
}
110
111
/**
112
 * Convert a string like "3, 5-7" into an array of ranges in to form of
113
 * [
114
 *   {start:2, end:2},
115
 *   {start:4, end:6},
116
 * ]
117
 * The string should be given as a list if comma delimited 1 based ranges.
118
 * The result is given as a 0 based array of ranges.
119
 * 
120
 * @param {string} str
121
 * @returns {Array}
122
 */
123
function strToRange( str )
124
{
125
    var range = str.replace(' ', '').split(','),
126
        i = range.length,
127
        ranges = [],
128
        start, end, splitted;
129
    
130
    while(i--)
131
    {
132
        // Multiple lines highlight
133
        if( range[i].indexOf('-') > -1 )
134
        {
135
            splitted = range[i].split('-');
136
            start = parseInt(splitted[0])-1;
137
            end = parseInt(splitted[1])-1;
138
        }
139
140
        // Single line highlight
141
        else
142
        {
143
            start = parseInt(range[i])-1;
144
            end = start;
145
        }
146
        
147
        ranges.unshift({start:start,end:end});
148
    }
149
    
150
    return ranges;
151
}
152
153
/**
154
 * Request animation frame. Uses setTimeout as a fallback if the browser does
155
 * not support requestAnimationFrame (based on 60 frames per second).
156
 * 
157
 * @param {type} cb
158
 * @returns {Number}
159
 */
160
var raf = window.requestAnimationFrame || 
161
          window.webkitRequestAnimationFrame || 
162
          window.mozRequestAnimationFrame ||
163
          window.msRequestAnimationFrame ||
164
          function(cb) { return window.setTimeout(cb, 1000 / 60); };
165
166
/**
167
 * The constructor.
168
 * See Mivhal.defaults for available options.
169
 * 
170
 * @param {DOMElement} selection
171
 * @param {Object} options
172
 */
173
function Mivhak( selection, options )
174
{   
175
    // Bail if there are no resources
176
    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...
177
    
178
    this.$selection = $( selection );
179
    this.setOptions( options );
180
    this.init();
181
}
182
183
/**
184
 * Check if a given string represents a supported method
185
 * @param {string} method
186
 */
187
Mivhak.methodExists = function( method )
188
{
189
    return typeof method === 'string' && Mivhak.methods[method];
190
};
191
192
/**
193
 * Initiate the code viewer.
194
 */
195
Mivhak.prototype.init = function() 
196
{
197
    this.initState();
198
    this.parseResources();
199
    this.createUI();
200
    this.applyOptions();
201
    this.callMethod('showTab',0); // Show first tab initially
202
};
203
204
/**
205
 * Apply the options that were set by the user. This function is called when
206
 * Mivhak is initiated, and every time the options are updated.
207
 */
208
Mivhak.prototype.applyOptions = function() 
209
{
210
    this.callMethod('setHeight', this.options.height);
211
    this.callMethod('setAccentColor', this.options.accentColor);
212
    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...
213
    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...
214
    else this.$selection.removeClass('mivhak-no-topbar');
215
    
216
    this.createCaption();
217
    this.createLivePreview();
218
};
219
220
/**
221
 * Initiate this instance's state.
222
 */
223
Mivhak.prototype.initState = function() 
224
{
225
    this.state = {
226
        lineWrap:   true,
227
        collapsed:  false,
228
        height:     0,
229
        activeTab:  null,   // Updated by tabs.showTab
230
        resources:    []      // Generated by parseResources()
231
    };
232
};
233
234
/**
235
 * Set or update this instance's options.
236
 * @param {object} options
237
 */
238
Mivhak.prototype.setOptions = function( options ) 
239
{
240
    // If options were already set, update them
241
    if( typeof this.options !== 'undefined' )
242
        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...
243
    
244
    // Otherwise, merge them with the defaults
245
    else this.options = $.extend(true, {}, Mivhak.defaults, options, readAttributes(this.$selection[0]));
246
};
247
248
/**
249
 * Call one of Mivhak's methods. See Mivhak.methods for available methods.
250
 * To apply additional arguments, simply pass the arguments after the methodName
251
 * i.e. callMethod('methodName', arg1, arg2).
252
 * This method is also called internally when making a method call through jQuery
253
 * i.e. $('#el').mivhak('methodName', arg1, arg2);
254
 * 
255
 * @param {string} methodName
256
 */
257
Mivhak.prototype.callMethod = function( methodName )
258
{
259
    if(Mivhak.methodExists(methodName))
260
    {
261
        // Call the method with the original arguments, removing the method's name from the list
262
        var args = [];
263
        Array.prototype.push.apply( args, arguments );
264
        args.shift();
265
        Mivhak.methods[methodName].apply(this, args);
266
    }
267
};
268
269
/**
270
 * Create the user interface.
271
 */
272
Mivhak.prototype.createUI = function() 
273
{
274
    this.tabs = Mivhak.render('tabs',{mivhakInstance: this});
275
    this.topbar = Mivhak.render('top-bar',{mivhakInstance: this});
276
    this.notifier = Mivhak.render('notifier');
277
    
278
    this.$selection.prepend(this.tabs.$el);
279
    this.$selection.prepend(this.topbar.$el);
280
    this.tabs.$el.prepend(this.notifier.$el);
281
};
282
283
/**
284
 * Calculate the height in pixels.
285
 * 
286
 * auto: Automatically calculate the height based on the number of lines.
287
 * min: Calculate the height based on the height of the tab with the maximum number of lines
288
 * max: Calculate the height based on the height of the tab with the minimum number of lines
289
 * average: Calculate the height based on the average height of all tabs
290
 * 
291
 * @param {string|number} h One of (auto|min|max|average) or a custom number
292
 * @returns {Number}
293
 */
294
Mivhak.prototype.calculateHeight = function(h)
295
{
296
    var heights = [],
297
        padding = this.options.padding*2,
298
        i = this.tabs.tabs.length;
299
300
    while(i--)
301
        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...
302
303
    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...
304
    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...
305
    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...
306
    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...
307
    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...
308
};
309
310
/**
311
 * Loop through each PRE element inside this.$selection and store it's options
312
 * in this.resources, merging it with the default option values.
313
 */
314
Mivhak.prototype.parseResources = function()
315
{
316
    var $this = this;
317
    
318
    this.resources = new Resources();
319
    this.$selection.find('pre').each(function(){
320
        $this.resources.add(this);
321
    });
322
};
323
324
Mivhak.prototype.createCaption = function()
325
{
326
    if(this.options.caption)
327
    {
328
        if(!this.caption)
329
        {
330
            this.caption = Mivhak.render('caption',{text: this.options.caption});
331
            this.$selection.append(this.caption.$el);
332
        }
333
        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...
334
    }
335
    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...
336
};
337
338
/**
339
 * Create the live preview iframe window
340
 */
341
Mivhak.prototype.createLivePreview = function()
342
{
343
    if(this.options.runnable && typeof this.preview === 'undefined')
344
    {
345
        this.preview = Mivhak.render('live-preview',{resources: this.resources});
346
        this.tabs.$el.append(this.preview.$el);
347
    }
348
};
349
350
/**
351
 * Remove all generated elements, data and events.
352
 * 
353
 * TODO: keep initial HTML
354
 */
355
Mivhak.prototype.destroy = function() 
356
{
357
    this.$selection.empty();
358
};
359
360
/**
361
 * A list of Mivhak default options
362
 */
363
Mivhak.defaults = {
364
    
365
    /**
366
     * Whether to add a live preview (and a "play" button) to run the code
367
     * @type Boolean
368
     */
369
    runnable:       false,
370
    
371
    /**
372
     * Whther to allow the user to edit the code
373
     * @type Boolean
374
     */
375
    editable:       false,
376
    
377
    /**
378
     * Whether to show line numers on the left
379
     * @type Boolean
380
     */
381
    lineNumbers:    false,
382
    
383
    /**
384
     * One of the supported CSS color values (HEX, RGB, etc...) to set as the 
385
     * code viewer's accent color. Controls the scrollbars, tab navigation and 
386
     * dropdown item colors.
387
     * @type String
388
     */
389
    accentColor:    false,
390
    
391
    /**
392
     * Whether to collapse the code viewer initially
393
     * @type Boolean
394
     */
395
    collapsed:      false,
396
    
397
    /**
398
     * Text/HTML string to be displayed at the bottom of the code viewer
399
     * @type Boolean|string
400
     */
401
    caption:        false,
402
    
403
    /**
404
     * The code viewer's theme. One of (dark|light)
405
     * @type String
406
     */
407
    theme:          'light',
408
    
409
    /**
410
     * The code viewer's height. Either a number (for a custom height in pixels) 
411
     * or one of (auto|min|max|average).
412
     * @type String|Number
413
     */
414
    height:         'average',
415
    
416
    /**
417
     * The surrounding padding between the code and the wrapper.
418
     * @type Number
419
     */
420
    padding:        15,
421
    
422
    /**
423
     * Whether to show/hide the top bar
424
     * @type Boolean
425
     */
426
    topbar: true,
427
    
428
    /**
429
     * An array of strings/objects for the settings dropdown menu
430
     * @type Array
431
     */
432
    buttons:        ['wrap','copy','collapse','about']
433
};
434
435
/**
436
 * A list of Mivhak resource default settings (Mivhak resources are any <pre> 
437
 * elements placed inside a Mivhak wrapper element).
438
 */
439
Mivhak.resourceDefaults = {
440
    
441
    /**
442
     * The resource language (one of the supported Ace Editor languages)
443
     * @type string
444
     */
445
    lang:           null,
446
    
447
    /**
448
     * How the resource should be treated in the preview window. One of (script|style|markup)
449
     * @type bool|string
450
     */
451
    runAs:          false,
452
    
453
    /**
454
     * A URL to an external source
455
     * @type bool|string
456
     */
457
    source:         false,
458
    
459
    /**
460
     * Whether to show this resource as a tab. Useful if you want to include
461
     * external libraries for the live preview and don't need to see their contents.
462
     * @type Boolean
463
     */
464
    visible:        true,
465
    
466
    /**
467
     * Mark/highlight a range of lines given as a string in the format '1, 3-4'
468
     * @type bool|string
469
     */
470
    mark:           false,
471
    
472
    /**
473
     * Set the initial line number (1 based).
474
     * @type Number
475
     */
476
    startLine:      1
477
};var Resources = function() {
478
    this.data = [];
479
};
480
481
Resources.prototype.count = function() {
482
    return this.data.length;
483
};
484
485
Resources.prototype.add = function(pre) {
486
    this.data.push($.extend({},
487
        Mivhak.resourceDefaults,{
488
            pre:pre, 
489
            content: pre.textContent
490
        },
491
        readAttributes(pre)
492
    ));
493
};
494
495
Resources.prototype.get = function(i) {
496
    return this.data[i];
497
};
498
499
Resources.prototype.update = function(i, content) {
500
    this.data[i].content = content;
501
};
502
503
// Built-in buttons
504
Mivhak.buttons = {
505
    
506
    /**
507
     * The wrap button features a toggle button and is used to toggle line wrap
508
     * on/off for the currently active tab
509
     */
510
    wrap: {
511
        text: 'Wrap Lines', 
512
        toggle: true, 
513
        click: function(e) {
514
            e.stopPropagation();
515
            this.callMethod('toggleLineWrap');
516
        }
517
    },
518
    
519
    /**
520
     * The copy button copies the code in the currently active tab to clipboard
521
     * (except for Safari, where it selects the code and prompts the user to press command+c)
522
     */
523
    copy: {
524
        text: 'Copy',
525
        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...
526
            this.callMethod('copyCode');
527
        }
528
    },
529
    
530
    /**
531
     * The collapse button toggles the entire code viewer into and out of its
532
     * collapsed state.
533
     */
534
    collapse: {
535
        text: 'Colllapse',
536
        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...
537
            this.callMethod('collapse');
538
        }
539
    },
540
    
541
    /**
542
     * The about button shows the user information about Mivhak
543
     */
544
    about: {
545
        text: 'About Mivhak',
546
        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...
547
            this.notifier.closableNotification('Mivhak.js v1.0.0');
548
        }
549
    }
550
};/**
551
 * jQuery plugin's methods. 
552
 * In all methods, the 'this' keyword is pointing to the calling instance of Mivhak.
553
 * These functions serve as the plugin's public API.
554
 */
555
Mivhak.methods = {
556
    
557
    /**
558
     * Toggle line wrap on/off for the currently active tab. Initially set to 
559
     * on (true) by default.
560
     */
561
    toggleLineWrap: function() {
562
        var $this = this;
563
        this.state.lineWrap = !this.state.lineWrap;
564
        $.each(this.tabs.tabs, function(i,tab) {
565
            tab.editor.getSession().setUseWrapMode($this.state.lineWrap);
566
            tab.vscroll.refresh();
567
            tab.hscroll.refresh();
568
        });
569
    },
570
    
571
    /**
572
     * copy the code in the currently active tab to clipboard (works in all
573
     * browsers apart from Safari, where it selects the code and prompts the 
574
     * user to press command+c)
575
     */
576
    copyCode: function() {
577
        var editor = this.activeTab.editor;
578
        editor.selection.selectAll();
579
        editor.focus();
580
        if(document.execCommand('copy')) {
581
            editor.selection.clearSelection();
582
            this.notifier.timedNotification('Copied to clipboard!', 2000);
583
        }
584
        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...
585
    },
586
    
587
    /**
588
     * Collapse the code viewer and show a "Show Code" button.
589
     */
590
    collapse: function() {
591
        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...
592
        var $this = this;
593
        this.state.collapsed = true;
594
        this.notifier.closableNotification('Show Code', function(){$this.callMethod('expand');});
595
        this.$selection.addClass('mivhak-collapsed');
596
        this.callMethod('setHeight',this.notifier.$el.outerHeight(true));
597
    },
598
    
599
    /**
600
     * Expand the code viewer if it's collapsed;
601
     */
602
    expand: function() {
603
        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...
604
        this.state.collapsed = false;
605
        this.notifier.hide(); // In case it's called by an external script
606
        this.$selection.removeClass('mivhak-collapsed');
607
        this.callMethod('setHeight',this.options.height);
608
    },
609
    
610
    /**
611
     * Show/activate a tab by the given index (zero based).
612
     * @param {number} index
613
     */
614
    showTab: function(index) {
615
        this.tabs.showTab(index);
616
        this.topbar.activateNavTab(index);
617
        if(this.options.runnable)
618
            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...
619
    },
620
    
621
    /**
622
     * Set the height of the code viewer. One of (auto|min|max|average) or 
623
     * a custom number.
624
     * @param {string|number} height
625
     */
626
    setHeight: function(height) {
627
        var $this = this;
628
        raf(function(){
629
            $this.state.height = $this.calculateHeight(height);
630
            $this.tabs.$el.height($this.state.height);
631
            $.each($this.tabs.tabs, function(i,tab) {
632
                $(tab.resource.pre).height(height);
633
                tab.editor.resize();
634
                tab.vscroll.refresh();
635
                tab.hscroll.refresh();
636
            });
637
        });
638
    },
639
    
640
    /**
641
     * Set the code viewer's accent color. Applied to the nav-tabs text color, 
642
     * underline, scrollbars and dropdown menu text color.
643
     * 
644
     * @param {string} color
645
     */
646
    setAccentColor: function(color) {
647
        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...
648
        this.topbar.$el.find('.mivhak-top-bar-button').css({'color': color});
649
        this.topbar.$el.find('.mivhak-dropdown-button').css({'color': color});
650
        this.topbar.$el.find('.mivhak-controls svg').css({'fill': color});
651
        this.tabs.$el.find('.mivhak-scrollbar-thumb').css({'background-color': color});
652
        this.topbar.line.css({'background-color': color});
653
    }
654
};Mivhak.icons = {};
655
656
// <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>
657
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>';
658
659
// <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>
660
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>';/**
661
 * The list of registered components.
662
 * 
663
 * @type Array
664
 */
665
Mivhak.components = [];
666
667
/**
668
 * Register a new component
669
 * 
670
 * @param {string} name The components name
671
 * @param {Object} options A list of component properties
672
 */
673
Mivhak.component = function(name, options)
674
{
675
    Mivhak.components[name] = options;
676
};
677
678
/**
679
 * Render a new component
680
 * 
681
 * TODO: move this into a seperate library
682
 * 
683
 * @param {string} name The components name
684
 * @param {Object} props A list of component properties. 
685
 * This overrides the component's initial property values.
686
 */
687
Mivhak.render = function(name, props)
688
{
689
    var component = $.extend(true, {}, Mivhak.components[name]);
690
    var el = {};
691
    
692
    // Create the element from the template
693
    el.$el = $(component.template);
694
    
695
    // Create all methods
696
    $.each(component.methods, function(name, method){
697
        el[name] = function() {return method.apply(el,arguments);};
698
    });
699
    
700
    // Set properties
701
    $.each(component.props, function(name, prop){
702
        el[name] = (typeof props !== 'undefined' && props.hasOwnProperty(name) ? props[name] : prop);
703
    });
704
    
705
    // Bind events
706
    $.each(component.events, function(name, method){
707
        el.$el.on(name, function() {return method.apply(el,arguments);});
708
    });
709
    
710
    // Call the 'created' function if exists
711
    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...
712
    
713
    return el;
714
};Mivhak.component('caption', {
715
    template: '<div class="mivhak-caption"></div>',
716
    props: {
717
        text: null
718
    },
719
    created: function() {
720
        this.setText(this.text);
721
    },
722
    methods: {
723
        setText: function(text) {
724
            this.$el.html(text);
725
        }
726
    }
727
});Mivhak.component('dropdown', {
728
    template: '<div class="mivhak-dropdown"></div>',
729
    props: {
730
        items: [],
731
        mivhakInstance: null,
732
        visible: false
733
    },
734
    created: function() {
735
        var $this = this;
736
        $.each(this.items, function(i, item) {
737
            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...
738
            var button = $('<div>',{class: 'mivhak-dropdown-button', text: item.text, click: function(e){item.click.call($this.mivhakInstance,e);}});
739
            if(item.toggle) 
740
            {
741
                button.$toggle = Mivhak.render('toggle');
742
                
743
                // Toggle only if not clicking on the toggle itself (which makes it toggle as it is)
744
                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...
745
                button.append(button.$toggle.$el);
746
            }
747
            $this.$el.append(button);
748
        });
749
        
750
        // Hide dropdown on outside click
751
        $(window).click(function(e){
752
            if(!$(e.target).closest('.mivhak-icon-cog').length) {
753
                $this.$el.removeClass('mivhak-dropdown-visible');
754
            }
755
        });
756
    },
757
    methods: {
758
        toggle: function() {
759
            this.visible = !this.visible;
760
            this.$el.toggleClass('mivhak-dropdown-visible');
761
        }
762
    }
763
});Mivhak.component('horizontal-scrollbar', {
764
    template: '<div class="mivhak-scrollbar mivhak-h-scrollbar"><div class="mivhak-scrollbar-thumb"></div></div>',
765
    props: {
766
        editor: null,
767
        $inner: null,
768
        $outer: null,
769
        mivhakInstance: null,
770
        minWidth: 50,
771
        state: {
772
            a: 0,    // The total width of the editor
773
            b: 0,    // The width of the viewport, excluding padding
774
            c: 0,    // The width of the viewport, including padding
775
            d: 0,    // The calculated width of the thumb
776
            l: 0     // The current left offset of the viewport
777
        },
778
        initialized: false
779
    },
780
    methods: {
781
        initialize: function() {
782
            if(!this.initialized)
783
            {
784
                this.initialized = true;
785
                this.dragDealer();
786
                var $this = this;
787
                $(window).resize(function(){
788
                    if(!$this.mivhakInstance.state.lineWrap)
789
                        $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...
790
                });
791
            }
792
            this.refresh();
793
        },
794
        updateState: function() {
795
            var oldState = $.extend({}, this.state);
796
            this.state.a = this.getEditorWidth();
797
            this.state.b = this.$outer.parent().width();
798
            this.state.c = this.state.b - this.mivhakInstance.options.padding*2;
799
            this.state.d = Math.max(this.state.c*this.state.b/this.state.a,this.minWidth);
800
            this.state.l *=  this.state.a/Math.max(oldState.a,1); // Math.max used to prevent division by zero
801
            return this.state.a !== oldState.a || this.state.b !== oldState.b;
802
        },
803
        refresh: function() {
804
            var $this = this, oldLeft = this.state.l;
805
            raf(function(){
806
                if($this.updateState())
807
                {
808
                    if($this.getDifference() > 0)
809
                    {
810
                        $this.doScroll('left',oldLeft-$this.state.l);
811
                        $this.$el.css({width: $this.state.d + 'px', left: 0});
812
                        $this.moveBar();
813
                    }
814
                    else 
815
                    {
816
                        $this.doScroll('left',$this.state.l);
817
                        $this.$el.css({width: 0});
818
                    }
819
                }
820
            });
821
        },
822
        dragDealer: function(){
823
            var $this = this,
824
                lastPageX;
825
826
            this.$el.on('mousedown.drag', function(e) {
827
                lastPageX = e.pageX;
828
                $this.$el.add(document.body).addClass('mivhak-scrollbar-grabbed');
829
                $(document).on('mousemove.drag', drag).on('mouseup.drag', stop);
830
                return false;
831
            });
832
833
            function drag(e){
834
                var delta = e.pageX - lastPageX,
835
                    didScroll;
836
837
                // Bail if the mouse hasn't moved
838
                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...
839
            
840
                lastPageX = e.pageX;
841
                
842
                raf(function(){
843
                    didScroll = $this.doScroll(delta > 0 ? 'right' : 'left', Math.abs(delta*$this.getEditorWidth()/$this.$outer.parent().width()));
844
                    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...
845
                });
846
            }
847
848
            function stop() {
849
                $this.$el.add(document.body).removeClass('mivhak-scrollbar-grabbed');
850
                $(document).off("mousemove.drag mouseup.drag");
851
            }
852
        },
853
        moveBar: function() {
854
            this.$el.css({
855
                left:  (this.state.b-this.state.d)/(this.state.a-this.state.c)*this.state.l + 'px'
856
            });
857
        },
858
        
859
        /**
860
         * Scrolls the editor element in the direction given, provided that there 
861
         * is remaining scroll space
862
         * @param {string} dir
863
         * @param {int} delta
864
         */
865
        doScroll: function(dir, delta) {
866
            var s = this.state,
867
                remaining,
868
                didScroll;
869
            
870
            if('left' === dir) 
871
            {
872
                remaining = s.l;
873
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
874
                s.l -= didScroll;
875
            }
876
            if('right' === dir) 
877
            {
878
                remaining = this.getDifference() - s.l;
879
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
880
                s.l += didScroll;
881
            }
882
            
883
            this.$inner.find('.ace_content').css({'margin-left': -s.l});
884
            return didScroll;
0 ignored issues
show
Bug introduced by
The variable didScroll does not seem to be initialized in case "left" === dir on line 870 is false. Are you sure this can never be the case?
Loading history...
885
        },
886
        
887
        /**
888
         * Returns the difference between the containing div and the editor div
889
         */
890
        getDifference: function()
891
        {
892
            return this.state.a - this.state.c;
893
        },
894
        
895
        /**
896
         * Calculate the editor's width based on the number of lines
897
         */
898
        getEditorWidth: function() {
899
            return this.$inner.find('.ace_content').width();
900
        }
901
    }
902
});Mivhak.component('live-preview', {
903
    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>',
904
    props: {
905
        resources: []
906
    },
907
    methods: {
908
        renderHTML: function() {
909
            var html = '<html>',
910
                head = '<head>',
911
                body = '<body>';
912
            
913
            head += '<meta http-equiv="content-type" content="text/html; charset=UTF-8">';
914
            head += '<meta name="robots" content="noindex, nofollow">';
915
            head += '<meta name="googlebot" content="noindex, nofollow">';
916
            
917
            for(var i = 0; i < this.resources.count(); i++)
918
            {
919
                var source = this.resources.get(i);
920
                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...
921
                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...
922
                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...
923
            }
924
            
925
            html += head+'</head>'+body+'</body></html>';
926
            
927
            return html;
928
        },
929
        createScript: function(content,src) {
930
            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...
931
            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
932
        },
933
        createStyle: function(content,href) {
934
            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...
935
            return '<style>'+content+'</style>';
936
        },
937
        show: function() {
938
            this.$el.addClass('mivhak-active');
939
            this.run();
940
        },
941
        hide: function() {
942
            this.$el.removeClass('mivhak-active');
943
        },
944
        run: function() {
945
            var contents = this.$el.contents(),
946
                doc = contents[0];
947
        
948
            doc.open();
949
            doc.writeln(this.renderHTML());
950
            doc.close();
951
        }
952
    }
953
});Mivhak.component('notifier', {
954
    template: '<div class="mivhak-notifier"></div>',
955
    methods: {
956
        notification: function(html) {
957
            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...
958
            clearTimeout(this.timeout);
959
            this.$el.off('click');
960
            this.$el.html(html);
961
            this.$el.addClass('mivhak-visible');
962
        },
963
        timedNotification: function(html, timeout) {
964
            var $this = this;
965
            this.notification(html);
966
            this.timeout = setTimeout(function(){
967
                $this.hide();
968
            },timeout);
969
        },
970
        closableNotification: function(html, onclick)
971
        {
972
            var $this = this;
973
            this.notification(html);
974
            this.$el.addClass('mivhak-button');
975
            this.$el.click(function(e){
976
                $this.hide();
977
                if(typeof onclick !== 'undefined')
978
                    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...
979
            });
980
        },
981
        hide: function() {
982
            this.$el.removeClass('mivhak-visible mivhak-button');
983
        }
984
    }
985
});Mivhak.component('tab-pane', {
986
    template: '<div class="mivhak-tab-pane"><div class="mivhak-tab-pane-inner"></div></div>',
987
    props: {
988
        resource:       null,
989
        editor:         null,
990
        index:          null,
991
        padding:        10,
992
        mivhakInstance: null
993
    },
994
    created: function() {
995
        this.setEditor();
996
        this.fetchRemoteSource();
997
        this.markLines();
998
        
999
        this.$el = $(this.resource.pre).wrap(this.$el).parent().parent();
1000
        this.$el.find('.mivhak-tab-pane-inner').css({margin: this.mivhakInstance.options.padding});
1001
        this.setScrollbars();
1002
        
1003
    },
1004
    methods: {
1005
        getTheme: function() {
1006
            return this.mivhakInstance.options.theme === 'light' ? 'clouds' : 'ambiance';
1007
        },
1008
        fetchRemoteSource: function() {
1009
            var $this = this;
1010
            if(this.resource.source) {
1011
                $.ajax(this.resource.source).done(function(res){
1012
                    $this.editor.setValue(res,-1);
1013
                    
1014
                    // Refresh code viewer height
1015
                    $this.mivhakInstance.callMethod('setHeight',$this.mivhakInstance.options.height);
1016
                    
1017
                    // Refresh scrollbars
1018
                    raf(function(){
1019
                        $this.vscroll.refresh();
1020
                        $this.hscroll.refresh();
1021
                    });
1022
                });
1023
                
1024
            }
1025
        },
1026
        setScrollbars: function() {
1027
            var $inner = $(this.resource.pre),
1028
                $outer = this.$el.find('.mivhak-tab-pane-inner');
1029
            
1030
            this.vscroll = Mivhak.render('vertical-scrollbar',{editor: this.editor, $inner: $inner, $outer: $outer, mivhakInstance: this.mivhakInstance});
1031
            this.hscroll = Mivhak.render('horizontal-scrollbar',{editor: this.editor, $inner: $inner, $outer: $outer, mivhakInstance: this.mivhakInstance});
1032
            
1033
            this.$el.append(this.vscroll.$el, this.hscroll.$el);
1034
        },
1035
        show: function() {
1036
            this.$el.addClass('mivhak-tab-pane-active');
1037
            this.editor.focus();
1038
            this.editor.gotoLine(0); // Needed in order to get focus
1039
            
1040
            // Recalculate scrollbar positions based on the now visible element
1041
            this.vscroll.initialize();
1042
            this.hscroll.initialize();
1043
        },
1044
        hide: function() {
1045
            this.$el.removeClass('mivhak-tab-pane-active');
1046
        },
1047
        setEditor: function() {
1048
            
1049
            // Remove redundant space from code
1050
            this.resource.pre.textContent = this.resource.pre.textContent.trim(); 
1051
            
1052
            // Set editor options
1053
            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...
1054
            this.editor.setReadOnly(!this.mivhakInstance.options.editable);
1055
            this.editor.setTheme("ace/theme/"+this.getTheme());
1056
            this.editor.setShowPrintMargin(false);
1057
            this.editor.renderer.setShowGutter(this.mivhakInstance.options.lineNumbers);
1058
            this.editor.getSession().setMode("ace/mode/"+this.resource.lang);
1059
            this.editor.getSession().setUseWorker(false); // Disable syntax checking
1060
            this.editor.getSession().setUseWrapMode(true); // Set initial line wrapping
1061
1062
            this.editor.setOptions({
1063
                maxLines: Infinity,
1064
                firstLineNumber: this.resource.startLine,
1065
                highlightActiveLine: false,
1066
                fontSize: parseInt(14)
1067
            });
1068
            
1069
            // Update source content for the live preview
1070
            if(this.mivhakInstance.options.editable)
1071
            {
1072
                var $this = this;
1073
                this.editor.getSession().on('change', function(a,b,c) {
0 ignored issues
show
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...
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...
1074
                    $this.mivhakInstance.resources.update($this.index, $this.editor.getValue());
1075
                });
1076
            }
1077
        },
1078
        markLines: function()
1079
        {
1080
            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...
1081
            var ranges = strToRange(this.resource.mark),
1082
                i = ranges.length,
1083
                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...
1084
1085
            while(i--)
1086
            {
1087
                this.editor.session.addMarker(
1088
                    new AceRange(ranges[i].start, 0, ranges[i].end, 1), // Define the range of the marker
1089
                    "ace_active-line",     // Set the CSS class for the marker
1090
                    "fullLine"             // Marker type
1091
                );
1092
            }
1093
        }
1094
    }
1095
});Mivhak.component('tabs', {
1096
    template: '<div class="mivhak-tabs"></div>',
1097
    props: {
1098
        mivhakInstance: null,
1099
        activeTab: null,
1100
        tabs: []
1101
    },
1102
    created: function() {
1103
        var $this = this;
1104
        this.$el = this.mivhakInstance.$selection.find('pre').wrapAll(this.$el).parent();
1105
        $.each(this.mivhakInstance.resources.data,function(i, resource){
1106
            if(resource.visible)
1107
                $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...
1108
                    resource: resource,
1109
                    index: i,
1110
                    mivhakInstance: $this.mivhakInstance
1111
                }));
1112
        });
1113
    },
1114
    methods: {
1115
        showTab: function(index){
1116
            var $this = this;
1117
            $.each(this.tabs, function(i, tab){
1118
                if(index === i) {
1119
                    $this.mivhakInstance.activeTab = tab;
1120
                    tab.show();
1121
                }
1122
                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...
1123
            });
1124
        }
1125
    }
1126
});Mivhak.component('toggle', {
1127
    template: '<div class="mivhak-toggle"><div class="mivhak-toggle-knob"></div></div>',
1128
    props: {
1129
        on: true
1130
    },
1131
    events: {
1132
        click: function() {
1133
            this.toggle();
1134
        }
1135
    },
1136
    created: function() {
1137
        this.$el.addClass('mivhak-toggle-'+(this.on?'on':'off'));
1138
    },
1139
    methods: {
1140
        toggle: function() {
1141
            this.on = !this.on;
1142
            this.$el.toggleClass('mivhak-toggle-on').toggleClass('mivhak-toggle-off');
1143
        }
1144
    }
1145
});Mivhak.component('top-bar-button', {
1146
    template: '<div class="mivhak-top-bar-button"></div>',
1147
    props: {
1148
        text: null,
1149
        icon: null,
1150
        dropdown: null,
1151
        mivhakInstance: null,
1152
        onClick: function(){}
1153
    },
1154
    events: {
1155
        click: function() {
1156
            this.onClick();
1157
        }
1158
    },
1159
    created: function() {
1160
        var $this = this;
1161
        this.$el.text(this.text);
1162
        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...
1163
        if(this.dropdown) 
1164
        {
1165
            $this.$el.append(this.dropdown.$el);
1166
            this.onClick = function() {
1167
                $this.toggleActivation();
1168
                $this.dropdown.toggle();
1169
            };
1170
        }
1171
    },
1172
    methods: {
1173
        activate: function() {
1174
            this.$el.addClass('mivhak-button-active');
1175
        },
1176
        deactivate: function() {
1177
            this.$el.removeClass('mivhak-button-active');
1178
        },
1179
        toggleActivation: function() {
1180
            this.$el.toggleClass('mivhak-button-active');
1181
        },
1182
        isActive: function() {
1183
            return this.$el.hasClass('mivhak-button-active');
1184
        }
1185
    }
1186
});Mivhak.component('top-bar', {
1187
    template: '<div class="mivhak-top-bar"><div class="mivhak-nav-tabs"></div><div class="mivhak-controls"></div><div class="mivhak-line"></div></div>',
1188
    props: {
1189
        mivhakInstance: null,
1190
        navTabs: [],
1191
        controls: [],
1192
        line: null
1193
    },
1194
    created: function() {
1195
        this.line = this.$el.find('.mivhak-line');
1196
        this.createTabNav();
1197
        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...
1198
        this.createCogButton();
1199
    },
1200
    methods: {
1201
        activateNavTab: function(index) {
1202
            var button = this.navTabs[index];
1203
            // Deactivate all tabs and activate this tab
1204
            $.each(this.navTabs, function(i,navTab){navTab.deactivate();});
1205
            button.activate();
1206
1207
            // Position the line
1208
            this.moveLine(button.$el);
1209
        },
1210
        moveLine: function($el) {
1211
            if(typeof $el === 'undefined') {
1212
                this.line.removeClass('mivhak-visible');
1213
                return;
1214
            }
1215
            this.line.width($el.width());
1216
            this.line.css({left:$el.position().left + ($el.outerWidth() - $el.width())/2});
1217
            this.line.addClass('mivhak-visible');
1218
        },
1219
        createTabNav: function() {
1220
            var source, i, pos = 0;
1221
            for(i = 0; i < this.mivhakInstance.resources.count(); i++)
1222
            {
1223
                source = this.mivhakInstance.resources.get(i);
1224
                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...
1225
            }
1226
        },
1227
        createNavTabButton: function(i, lang) {
1228
            var $this = this,
1229
                button = Mivhak.render('top-bar-button',{
1230
                text: lang,
1231
                onClick: function() {
1232
                    $this.mivhakInstance.callMethod('showTab',i);
1233
                }
1234
            });
1235
            this.navTabs.push(button);
1236
            this.$el.find('.mivhak-nav-tabs').append(button.$el);
1237
        },
1238
        createPlayButton: function() {
1239
            var $this = this;
1240
            var playBtn = Mivhak.render('top-bar-button',{
1241
                icon: 'play',
1242
                onClick: function() {
1243
                    $this.mivhakInstance.preview.show();
1244
                    $this.moveLine();
1245
                }
1246
            });
1247
            this.controls.push(playBtn);
1248
            this.$el.find('.mivhak-controls').append(playBtn.$el);
1249
        },
1250
        createCogButton: function() {
1251
            var cogBtn = Mivhak.render('top-bar-button',{
1252
                icon: 'cog',
1253
                mivhakInstance: this.mivhakInstance,
1254
                dropdown: Mivhak.render('dropdown',{
1255
                    mivhakInstance: this.mivhakInstance,
1256
                    items: this.mivhakInstance.options.buttons
1257
                })
1258
            });
1259
            this.controls.push(cogBtn);
1260
            this.$el.find('.mivhak-controls').append(cogBtn.$el);
1261
        }
1262
    }
1263
});Mivhak.component('vertical-scrollbar', {
1264
    template: '<div class="mivhak-scrollbar mivhak-v-scrollbar"><div class="mivhak-scrollbar-thumb"></div></div>',
1265
    props: {
1266
        editor: null,
1267
        $inner: null,
1268
        $outer: null,
1269
        mivhakInstance: null,
1270
        minHeight: 50,
1271
        state: {
1272
            a: 0,    // The total height of the editor
1273
            b: 0,    // The height of the viewport, excluding padding
1274
            c: 0,    // The height of the viewport, including padding
1275
            d: 0,    // The calculated thumb height
1276
            t: 0     // The current top offset of the viewport
1277
        },
1278
        initialized: false
1279
    },
1280
    methods: {
1281
        initialize: function() {
1282
            if(!this.initialized)
1283
            {
1284
                this.initialized = true;
1285
                this.dragDealer();
1286
                var $this = this;
1287
                this.$inner.on('mousewheel', function(e){$this.onScroll.call(this, e);});
1288
                $(window).resize(function(){
1289
                    if($this.mivhakInstance.state.lineWrap)
1290
                        $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...
1291
                });
1292
            }
1293
            // Refresh anytime initialize is called
1294
            this.refresh();
1295
        },
1296
        updateState: function() {
1297
            var oldState = $.extend({}, this.state);
1298
            this.state.a = getEditorHeight(this.$inner);
1299
            this.state.b = this.mivhakInstance.state.height;
1300
            this.state.c = this.mivhakInstance.state.height-this.mivhakInstance.options.padding*2;
1301
            this.state.d = Math.max(this.state.c*this.state.b/this.state.a,this.minHeight);
1302
            this.state.t *=  this.state.a/Math.max(oldState.a,1); // Math.max used to prevent division by zero
1303
            return this.state.a !== oldState.a || this.state.b !== oldState.b;
1304
        },
1305
        refresh: function() {
1306
            var $this = this, oldTop = this.state.t;
1307
            raf(function(){
1308
                if($this.updateState())
1309
                {
1310
                    if($this.getDifference() > 0)
1311
                    {
1312
                        $this.doScroll('up',oldTop-$this.state.t);
1313
                        $this.$el.css({height: $this.state.d + 'px', top: 0});
1314
                        $this.moveBar();
1315
                    }
1316
                    else 
1317
                    {
1318
                        $this.doScroll('up',$this.state.t);
1319
                        $this.$el.css({height: 0});
1320
                    }
1321
                }
1322
            });
1323
        },
1324
        onScroll: function(e) {
1325
            var didScroll;
1326
            
1327
            if(e.deltaY > 0)
1328
                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...
1329
            else
1330
                didScroll = this.doScroll('down',-e.deltaY*e.deltaFactor);
1331
            
1332
            if(0 !== didScroll) {
1333
                this.moveBar();
1334
                e.preventDefault(); // Only prevent page scroll if the editor can be scrolled
1335
            }
1336
        },
1337
        dragDealer: function(){
1338
            var $this = this,
1339
                lastPageY;
1340
1341
            this.$el.on('mousedown.drag', function(e) {
1342
                lastPageY = e.pageY;
1343
                $this.$el.add(document.body).addClass('mivhak-scrollbar-grabbed');
1344
                $(document).on('mousemove.drag', drag).on('mouseup.drag', stop);
1345
                return false;
1346
            });
1347
1348
            function drag(e){
1349
                var delta = e.pageY - lastPageY,
1350
                    didScroll;
1351
            
1352
                // Bail if the mouse hasn't moved
1353
                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...
1354
            
1355
                lastPageY = e.pageY;
1356
                
1357
                raf(function(){
1358
                    didScroll = $this.doScroll(delta > 0 ? 'down' : 'up', Math.abs(delta*getEditorHeight($this.$inner)/$this.$outer.parent().height()));
1359
                    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...
1360
                });
1361
            }
1362
1363
            function stop() {
1364
                $this.$el.add(document.body).removeClass('mivhak-scrollbar-grabbed');
1365
                $(document).off("mousemove.drag mouseup.drag");
1366
            }
1367
        },
1368
        moveBar: function() {
1369
            this.$el.css({
1370
                top:  (this.state.b-this.state.d)/(this.state.a-this.state.c)*this.state.t + 'px'
1371
            });
1372
        },
1373
        
1374
        /**
1375
         * Scrolls the editor element in the direction given, provided that there 
1376
         * is remaining scroll space
1377
         * @param {string} dir
1378
         * @param {int} delta
1379
         */
1380
        doScroll: function(dir, delta) {
1381
            var s = this.state,
1382
                remaining,
1383
                didScroll;
1384
            
1385
            if('up' === dir) 
1386
            {
1387
                remaining = s.t;
1388
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
1389
                s.t -= didScroll;
1390
            }
1391
            if('down' === dir) 
1392
            {
1393
                remaining = this.getDifference() - s.t;
1394
                didScroll = remaining > 0 ? Math.min(remaining,delta) : 0;
1395
                s.t += didScroll;
1396
            }
1397
            
1398
            this.$inner.css({top: -s.t});
1399
            return didScroll;
0 ignored issues
show
Bug introduced by
The variable didScroll does not seem to be initialized in case "up" === dir on line 1385 is false. Are you sure this can never be the case?
Loading history...
1400
        },
1401
        
1402
        /**
1403
         * Returns the difference between the containing div and the editor div
1404
         */
1405
        getDifference: function()
1406
        {
1407
            return this.state.a - this.state.c;
1408
        }
1409
    }
1410
});/**
1411
 * Extends the functionality of jQuery to include Mivhak
1412
 * 
1413
 * @param {Function|Object} methodOrOptions
1414
 * @returns {jQuery} 
1415
 */
1416
$.fn.mivhak = function( methodOrOptions ) {
1417
1418
    // Store arguments for use with methods
1419
    var args = arguments.length > 1 ? Array.apply(null, arguments).slice(1) : null;
1420
1421
    return this.each(function(){
1422
        
1423
        // If this is an options object, set or update the options
1424
        if( typeof methodOrOptions === 'object' || !methodOrOptions )
1425
        {
1426
            // If this is the initial call for this element, instantiate a new Mivhak object
1427
            if( typeof $(this).data( 'mivhak' ) === 'undefined' ) {
1428
                var plugin = new Mivhak( this, methodOrOptions );
1429
                $(this).data( 'mivhak', plugin );
1430
            }
1431
            // Otherwise update existing settings (consequent calls will update, rather than recreate Mivhak)
1432
            else
1433
            {
1434
                $(this).data('mivhak').setOptions( methodOrOptions );
1435
                $(this).data('mivhak').applyOptions();
1436
            }
1437
        }
1438
        
1439
        // If this is a method call, run the method (if it exists)
1440
        else if( Mivhak.methodExists( methodOrOptions )  )
1441
        {
1442
            Mivhak.methods[methodOrOptions].apply($(this).data('mivhak'), args);
1443
        }
1444
    });
1445
};}( jQuery ));