/**
 *	script: harmonica-tuning.js
 *	description: harmonica layout + scales
 *	license: MIT-style license.
 *	copyright: Copyright (c) 2009-2010 [Andriy Rakhnin] (http://rakhnin.com)
 *	author: Andriy Rakhnin (http://rakhnin.com)
 **/
var harmonicaTuningClass = function () {
        this.options = {
            type: 'diatonic',
            tuning: 'richter',
            compare: '',
            sharp: '#b',
            key: 0,
            scaleKey: 0,
            scale: false
        };
        this.notes = {
            '#b': ['C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B'],
            '#': ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'],
            'b': ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']
        };
        this.types = {
            'diatonic': {
                title: 'diatonic',
                defaultTuning: 'richter',
                notesPerHole: 2
            },
            'chromatic': {
                title: 'chromatic',
                defaultTuning: 'classic-12',
                notesPerHole: 4
            }
        };
        this.keys = {
            'diatonic': [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6],
            'chromatic': [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
        };
        this.scales = {
            'blues': {
                title: 'blues',
                rule: [0, 3, 5, 6, 7, 10]
            },
            'major': {
                title: 'major (ionian)',
                rule: [0, 2, 4, 5, 7, 9, 11]
            },
            'dorian': {
                title: 'dorian',
                rule: [0, 2, 3, 5, 7, 9, 10]
            },
            'phrygian': {
                title: 'phrygian',
                rule: [0, 1, 3, 5, 7, 8, 10]
            },
            'lydian': {
                title: 'lydian',
                rule: [0, 2, 4, 6, 7, 9, 11]
            },
            'mixolydian': {
                title: 'mixolydian',
                rule: [0, 2, 4, 5, 7, 9, 10]
            },
            'aeolian': {
                title: 'minor (aeolian)',
                rule: [0, 2, 3, 5, 7, 8, 10]
            },
            'locrian': {
                title: 'locrian',
                rule: [0, 1, 3, 5, 6, 8, 10]
            },
            'double-harmonic': {
                title: '2 harmonic (arabic)',
                rule: [0, 1, 4, 5, 7, 8, 11]
            },
            'harmonic-minor': {
                title: 'harmonic minor',
                rule: [0, 2, 3, 5, 7, 8, 11]
            },
            'minor-gypsy': {
                title: 'minor gypsy',
                rule: [0, 2, 3, 6, 7, 8, 10]
            },
            'hungarian-gypsy': {
                title: 'hungarian gypsy',
                rule: [0, 2, 3, 6, 7, 8, 11]
            },
            'spanish-gypsy': {
                title: 'spanish gypsy',
                rule: [0, 1, 4, 5, 7, 8, 10]
            },
            'whole-step': {
                title: 'whole step',
                rule: [0, 2, 4, 6, 8, 10]
            }
        };
        this.scaleKeys = {
            'diatonic': [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6],
            'chromatic': [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
        };
        this.tunings = {
            'richter': {
                'title': 'richter',
                'description': 'richter',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 4, 7, 7, 11, 12, 14, 16, 17, 19, 21, 24, 23, 28, 26, 31, 29, 36, 33]
            },
            'country': {
                'title': 'country',
                'description': 'country',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 4, 7, 7, 11, 12, 14, 16, 18, 19, 21, 24, 23, 28, 26, 31, 29, 36, 33]
            },
            'natural-minor': {
                'title': 'natural minor',
                'description': 'natural minor',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 3, 7, 7, 10, 12, 14, 15, 17, 19, 21, 24, 22, 27, 26, 31, 29, 36, 33]
            },
            'harmonic-minor': {
                'title': 'harmonic minor',
                'description': 'harmonic minor',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 3, 7, 7, 11, 12, 14, 15, 17, 19, 20, 24, 23, 27, 26, 31, 29, 36, 32]
            },
            'melody-maker': {
                'title': 'melody maker',
                'description': 'melody maker',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 4, 7, 9, 11, 12, 14, 16, 18, 19, 21, 24, 23, 28, 26, 31, 30, 36, 33]
            },
            'circular': {
                'title': 'circular',
                'description': 'circular',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 4, 5, 7, 9, 10, 12, 14, 16, 17, 19, 21, 22, 24, 26, 28, 29, 31, 33]
            },
            'soloist': {
                'title': 'soloist',
                'description': 'soloist',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 4, 5, 7, 9, 12, 11, 12, 14, 16, 17, 19, 21, 24, 23, 24, 26, 28, 29, 31, 33, 36, 35]
            },
            'sbs': {
                'title': 'steve backer special',
                'description': 'sbs',
                'link': null,
                'type': 'diatonic',
                'startOctave': 2,
                'tuning': [-12, -10, -8, -5, -5, -1, 0, 2, 4, 7, 7, 11, 12, 14, 16, 17, 19, 21, 24, 23, 28, 26, 31, 29, 36, 33, 40, 35]
            },
            'true-harp': {
                'title': 'jim\'s true',
                'description': 'jim\'s true',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 3, 5, 7, 9, 10, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 30, 31, 33]
            },
            'true-harmonic': {
                'title': 'true harmonic',
                'description': 'jim\'s true harmonic',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 3, 5, 6, 9, 10, 12, 13, 16, 17, 19, 20, 23, 24, 26, 27, 30, 31, 33]
            },
            'powerbender': {
                'title': 'powerbender',
                'description': 'innovative Brendan Power tuning',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 4, 7, 7, 11, 12, 14, 14, 16, 17, 19, 21, 23, 24, 26, 28, 31, 33, 36]
            },
            'diminished-harp': {
                'title': 'diminished',
                'description': 'diminished tuning',
                'link': null,
                'type': 'diatonic',
                'startOctave': 3,
                'tuning': [0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27, 29]
            },            
            'classic-8': {
                'title': 'chromatic 8',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 12, 13, 11, 12, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 24, 25, 23, 24]
            },
            'classic-10': {
                'title': 'chromatic 10',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 12, 13, 11, 12, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 24, 25, 23, 24, 24, 25, 26, 27, 28, 29, 29, 30]
            },
            'richter-chrom': {
                'title': 'richter',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [0, 1, 2, 3, 4, 5, 7, 8, 7, 8, 11, 12, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 24, 25, 23, 24, 28, 29, 26, 27, 31, 32, 29, 30, 36, 37, 33, 34]
            },
            'classic-12': {
                'title': 'chromatic 12',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 12, 13, 11, 12, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 24, 25, 23, 24, 24, 25, 26, 27, 28, 29, 29, 30, 31, 32, 33, 34, 36, 37, 35, 36]
            },
            'bebop': {
                'title': 'bebop',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 10, 11, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 23, 24, 24, 25, 26, 27, 28, 29, 29, 30, 31, 32, 33, 34, 34, 35, 35, 36]
            },
            'diminished': {
                'title': 'diminished',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 9, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 21, 22, 23, 24, 24, 25, 26, 27, 27, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36]
            },
            'true-chromatic': {
                'title': 'jim\'s true',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 23, 24, 24, 25, 26, 27, 28, 29, 30, 31, 31, 32, 33, 34, 35, 36, 37, 38, 38, 39, 40, 41]
            },
            'whole-tone': {
                'title': 'whole tone',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48]
            },
            'classic-14': {
                'title': 'chromatic 14',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [-5, -4, -3, -2, 0, 1, -1, 0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 12, 13, 11, 12, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 24, 25, 23, 24, 24, 25, 26, 27, 28, 29, 29, 30, 31, 32, 33, 34, 36, 37, 35, 36]
            },
            'classic-16': {
                'title': 'chromatic 16',
                'description': '',
                'link': null,
                'type': 'chromatic',
                'startOctave': 3,
                'tuning': [-12, -11, -10, -9, -8, -7, -7, -6, -5, -4, -3, -2, 0, 1, -1, 0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 12, 13, 11, 12, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 24, 25, 23, 24, 24, 25, 26, 27, 28, 29, 29, 30, 31, 32, 33, 34, 36, 37, 35, 36]
            }
        };
        this.harmonicaLayout = {};
        this.harmonicaRange = {
            'min': 1000,
            'max': -1000
        };
        this.tuningHolder = false;
        this.getOptions = function () {
            var _location = window.location.toString();
            var _pos = _location.indexOf("#");
            if (_pos != -1) {
                var _options = {};
                var _vars = _location.substring(_pos + 1, _location.length);
                var _varsArray = _vars.split('&');
                for (var i = 0; i < _varsArray.length; i++) {
                    var _var = _varsArray[i].split('=');
                    if (_var[1]) {
                        if (_var[0] == 'key' || _var[0] == 'scaleKey') {
                            _var[1] = parseInt(_var[1])
                        }
                        _options[_var[0]] = _var[1]
                    }
                }
                for (k in this.options) {
                    if (_options[k]) {
                        this.options[k] = _options[k]
                    }
                }
            }
        };
        this.init = function (tuningHolderId) {
            this.getOptions();
            this.mainHolder = __$(tuningHolderId);
            this.optionHolder = __el('div', {
                'class': 'harmonica-options'
            }, this.mainHolder);
            var tuningSubHolder = __el('div', {
                'class': 'harmonica-layout-sub'
            }, this.mainHolder);
            this.tuningHolder = __el('div', {
                'class': 'harmonica-layout'
            }, tuningSubHolder);
            var scaleTabSubHolder = __el('div', {
                'class': 'scale-tab-sub'
            }, this.mainHolder);
            this.scaleTabLayout = __el('div', {
                'class': 'scale-tab-layout tabs'
            }, scaleTabSubHolder);
            var scaleSubHolder = __el('div', {
                'class': 'scale-layout-sub'
            }, this.mainHolder);
            this.scaleLayout = __el('div', {
                'class': 'scale-layout'
            }, scaleSubHolder);
            this.drawOptions();
            this.drawHarmonica()
        };
        this.drawOptions = function () {
            this.clearOptions();
            var _row, _label, _select, _option, __params;
            _row = __el('div', {
                'class': 'row'
            }, this.optionHolder);
            _label = __el('label', {
                'html': 'Type:',
                'class': 'text'
            }, _row);
            _select = __el('select', {
                'class': 'text'
            }, _row);
            for (var _type in this.types) {
                __params = {
                    'value': _type,
                    'html': this.types[_type].title
                };
                if (_type == this.options.type) {
                    __params.selected = 'selected'
                }
                _option = __el('option', __params, _select)
            }
            _select._this = this;
            _select.onchange = this.onTypeChange;
            _row = __el('div', {
                'class': 'row'
            }, this.optionHolder);
            _label = __el('label', {
                'html': 'Tuning:',
                'class': 'text'
            }, _row);
            _select = __el('select', {
                'class': 'text'
            }, _row);
            for (var _tuning in this.tunings) {
                if (this.tunings[_tuning].type == this.options.type) {
                    __params = {
                        'value': _tuning,
                        'html': this.tunings[_tuning].title
                    };
                    if (_tuning == this.options.tuning) {
                        __params.selected = 'selected'
                    }
                    _option = __el('option', __params, _select)
                }
            }
            _select._this = this;
            _select.onchange = this.onTuningChange;
            _row = __el('div', {
                'class': 'row'
            }, this.optionHolder);
            _label = __el('label', {
                'html': 'Compare:',
                'class': 'text'
            }, _row);
            _select = __el('select', {
                'class': 'text'
            }, _row);
            var compareToList = {
                '': {
                    'type': this.options.type,
                    'title': ''
                }
            };
            compareToList = mergeObj(compareToList, this.tunings);
            for (var _tuning in compareToList) {
                if (compareToList[_tuning].type == this.options.type) {
                    __params = {
                        'value': _tuning,
                        'html': compareToList[_tuning].title
                    };
                    if (_tuning == this.options.compare) {
                        __params.selected = 'selected'
                    }
                    _option = __el('option', __params, _select)
                }
            }
            _select._this = this;
            _select.onchange = this.onCompareChange;
            _row = __el('div', {
                'class': 'row'
            }, this.optionHolder);
            _label = __el('label', {
                'html': 'Key:',
                'class': 'text'
            }, _row);
            _select = __el('select', {
                'class': 'text'
            }, _row);
            var _keys = this.keys[this.options.type];
            for (var k = 0; k < _keys.length; k++) {
                var _text = this.getNoteByCode(_keys[k]);
                __params = {
                    'value': _keys[k],
                    'html': _text
                };
                if (_keys[k] == this.options.key) {
                    __params.selected = 'selected'
                }
                _option = __el('option', __params, _select)
            }
            _select._this = this;
            _select.onchange = this.onKeyChange;
            _row = __el('div', {
                'class': 'row'
            }, this.optionHolder);
            _label = __el('label', {
                'html': 'Scale:',
                'class': 'text'
            }, _row);
            _select = __el('select', {
                'class': 'text'
            }, _row);
            __params = {
                'value': '',
                'html': ''
            };
            if (this.options.scale === false) {
                __params.selected = 'selected'
            }
            _option = __el('option', __params, _select);
            for (var _scale in this.scales) {
                __params = {
                    'value': _scale,
                    'html': this.scales[_scale].title
                };
                if (_scale == this.options.scale) {
                    __params.selected = 'selected'
                }
                _option = __el('option', __params, _select)
            }
            _select._this = this;
            _select.onchange = this.onScaleChange;
            _row = __el('div', {
                'class': 'row'
            }, this.optionHolder);
            if (this.options.scale === false) {
                _row.style.display = 'none'
            }
            _label = __el('label', {
                'html': 'Scale Key:',
                'class': 'text'
            }, _row);
            this.scaleKeySelect = _select = __el('select', {
                'class': 'text'
            }, _row);
            var _keys = this.scaleKeys[this.options.type];
            for (var k = 0; k < _keys.length; k++) {
                var _text = this.getNoteByCode(_keys[k]);
                __params = {
                    'value': _keys[k],
                    'html': _text
                };
                if (_keys[k] === this.options.scaleKey) {
                    __params.selected = 'selected'
                }
                _option = __el('option', __params, _select)
            }
            _select._this = this;
            _select.onchange = this.onScaleKeyChange
        };
        this.onTypeChange = function () {
            var _this = this;
            if (this._this) {
                _this = this._this
            }
            _this.options.type = this.options[this.selectedIndex].value;
            _this.options.tuning = _this.types[_this.options.type].defaultTuning;
            _this.options.compare = '';
            _this.drawOptions();
            _this.drawHarmonica()
        };
        this.onCompareChange = function () {
            var _this = this;
            if (this._this) {
                _this = this._this
            }
            _this.options.compare = this.options[this.selectedIndex].value;
            _this.drawOptions();
            _this.drawHarmonica()
        };
        this.onTuningChange = function () {
            var _this = this;
            if (this._this) {
                _this = this._this
            }
            _this.options.tuning = this.options[this.selectedIndex].value;
            _this.drawOptions();
            _this.drawHarmonica()
        };
        this.onKeyChange = function () {
            var _this = this;
            if (this._this) {
                _this = this._this
            }
            _this.options.key = parseInt(this.options[this.selectedIndex].value);
            _this.drawOptions();
            _this.drawHarmonica()
        };
        this.onScaleKeyChange = function () {
            var _this = this;
            if (this._this) {
                _this = this._this
            }
            if (this.options[this.selectedIndex].value == '') {
                _this.options.scaleKey = false
            } else {
                _this.options.scaleKey = parseInt(this.options[this.selectedIndex].value)
            }
            _this.drawOptions();
            _this.drawHarmonica()
        };
        this.onScaleChange = function () {
            var _this = this;
            if (this._this) {
                _this = this._this
            }
            _this.options.scale = this.options[this.selectedIndex].value;
            if (this.options[this.selectedIndex].value == '') {} else {}
            _this.drawOptions();
            _this.drawHarmonica()
        };
        this.drawHarmonica = function () {
            this.clearHarmonica();
            if (this.options.type == 'diatonic') {
                this.drawDiatonic()
            } else if (this.options.type == 'chromatic') {
                this.drawChromatic()
            }
            this.drawScale();
            this.trackEvent()
        };
        this.clearHarmonica = function () {
            if (this.tuningHolder) {
                this.tuningHolder.innerHTML = ''
            }
            this.harmonicaLayout = {};
            this.harmonicaRange = {
                'min': 1000,
                'max': -1000
            };
            this.clearScale()
        };
        this.clearScale = function () {
            if (this.scaleLayout) {
                this.scaleLayout.innerHTML = ''
            }
            if (this.scaleTabLayout) {
                this.scaleTabLayout.innerHTML = ''
            }
        };
        this.clearOptions = function () {
            if (this.optionHolder) {
                this.optionHolder.innerHTML = ''
            }
        };
        this.drawScale = function () {
            if (this.scales[this.options.scale]) {
                this.scaleTabLayout.parentNode.style.display = 'block';
                this.scaleLayout.parentNode.style.display = 'block';
                var _rule = this.scales[this.options.scale].rule;
                for (var i = 0; i < _rule.length; i++) {
                    this.drawHole(_rule[i], {
                        'class': 'hole'
                    }, this.scaleLayout, parseInt(this.options.scaleKey))
                }
                this.scaleLayout.style.width = _rule.length * 42 + "px";
                var _rule = this.scales[this.options.scale].rule;
                var _startScaleKey = _rule[0] + this.options.scaleKey;
                var _endScaleKey = _rule[_rule.length - 1] + this.options.scaleKey;
                while (_startScaleKey >= this.harmonicaRange['min']) {
                    _startScaleKey -= 12
                }
                _startScaleKey += 12;
                while (_endScaleKey <= this.harmonicaRange['max']) {
                    _endScaleKey += 12
                }
                _endScaleKey -= 12;
                var _scaleTabs = [];
                while (_endScaleKey > _startScaleKey) {
                    var diff = _startScaleKey - _rule[0];
                    for (var i = 0; i < _rule.length; i++) {
                        var _key = _rule[i] + diff;
                        if (this.harmonicaLayout[_key]) {
                            var _t = '';
                            if (this.harmonicaLayout[_key]['nd']) {
                                _t = 'nd'
                            } else if (this.harmonicaLayout[_key]['nb']) {
                                _t = 'nb'
                            } else if (this.harmonicaLayout[_key]['b']) {
                                _t = 'b'
                            } else if (this.harmonicaLayout[_key]['o']) {
                                _t = 'o'
                            }
                            if (this.harmonicaLayout[_key][_t]) {
                                _scaleTabs[_scaleTabs.length] = [this.harmonicaLayout[_key][_t]['hole'], this.harmonicaLayout[_key][_t]['type'], _key]
                            }
                        } else {
                            _scaleTabs[_scaleTabs.length] = ['?', '']
                        }
                    }
                    _scaleTabs[_scaleTabs.length] = ['', ''];
                    _startScaleKey += 12
                }
                if (_scaleTabs.length > 0) {
                    _scaleTabs[_scaleTabs.length - 1] = false
                }
                var _tabs = new harmonicaTabsClass();
                _tabs.init(this.scaleTabLayout);
                _tabs.addTabs(_scaleTabs);
                _tabs.drawTabs();
                var _els = __$$(this.scaleTabLayout, 'span');
                for (var i = 0; i < _els.length; i++) {
                    if (typeof _els[i]._key != "undefined") {
                        _els[i]._this = this;
                        _els[i]._tnote = this.getNoteByCode(_els[i]._key).toLowerCase();
                        _els[i].onmouseover = this.holeOver;
                        _els[i].onmouseout = this.holeOver
                    }
                }
            } else {
                this.scaleTabLayout.parentNode.style.display = 'none';
                this.scaleLayout.parentNode.style.display = 'none'
            }
        };
        this.getHarmonicaLayout = function (_key, _type, _hole, _htype) {
            _key = _key + this.options.key;
            if (!this.harmonicaLayout[_key]) {
                this.harmonicaLayout[_key] = {}
            }
            this.harmonicaRange['max'] = Math.max(this.harmonicaRange['max'], _key);
            this.harmonicaRange['min'] = Math.min(this.harmonicaRange['min'], _key);
            this.harmonicaLayout[_key][_type] = {
                'hole': _hole,
                'type': _htype
            }
        };
        this.drawDiatonic = function () {
            var _type = this.options.tuning;
            if (!this.tunings[_type]) {
                return false
            }
            var _holeCnt = Math.floor(this.tunings[_type]['tuning'].length / this.types[this.tunings[_type]['type']].notesPerHole);
            this.tuningHolder.style.width = _holeCnt * 42 + "px";
            var maxUpperBendDepth = 2;
            var maxLowerBendDepth = 2;
            for (var i = 0; i < _holeCnt; i++) {
                var diff = this.tunings[_type]['tuning'][2 * i] - this.tunings[_type]['tuning'][2 * i + 1];
                if (diff <= 0) {
                    maxLowerBendDepth = Math.max(maxLowerBendDepth, -1 * diff)
                } else {
                    maxUpperBendDepth = Math.max(maxUpperBendDepth, diff)
                }
            }
            var _key = 0;
            var _bends = "bbbbbbb";
            for (var j = maxUpperBendDepth - 1; j > 0; j--) {
                for (var i = 0; i < _holeCnt; i++) {
                    if (this.tunings[_type]['tuning'][2 * i] - j > this.tunings[_type]['tuning'][2 * i + 1]) {
                        _key = this.tunings[_type]['tuning'][2 * i] - j;
                        this.drawHole(_key, {
                            'class': 'hole bend'
                        });
                        this.getHarmonicaLayout(_key, 'b', i + 1, 'b' + _bends.substr(0, j))
                    } else if (this.tunings[_type]['tuning'][2 * i] < this.tunings[_type]['tuning'][2 * i + 1] && j == 1 && (i == _holeCnt - 1 || this.tunings[_type]['tuning'][2 * i + 1] + (j) < this.tunings[_type]['tuning'][2 * i + 2])) {
                        _key = this.tunings[_type]['tuning'][2 * i + 1] + (j);
                        this.drawHole(_key, {
                            'class': 'hole over'
                        });
                        this.getHarmonicaLayout(_key, 'o', i + 1, 'bob')
                    } else {
                        _key = false;
                        this.drawHole(_key, {
                            'class': 'hole'
                        })
                    }
                }
                __el('div', {
                    'class': 'clear'
                }, this.tuningHolder)
            }
            for (var i = 0; i < _holeCnt; i++) {
                _compareKey = null;
                if (this.options.compare != '') {
                    _compareKey = this.tunings[this.options.compare]['tuning'][2 * i]
                }
                _key = this.tunings[_type]['tuning'][2 * i];
                this.drawHole(_key, {
                    'class': 'hole'
                }, null, null, _compareKey);
                this.getHarmonicaLayout(_key, 'nb', i + 1, 'b')
            }
            __el('div', {
                'class': 'clear'
            }, this.tuningHolder);
            for (var i = 0; i < _holeCnt; i++) {
                this.drawHoleNumber(i + 1, {
                    'class': 'hole number'
                })
            }
            __el('div', {
                'class': 'clear'
            }, this.tuningHolder);
            for (var i = 0; i < _holeCnt; i++) {
                _compareKey = null;
                if (this.options.compare != '') {
                    _compareKey = this.tunings[this.options.compare]['tuning'][2 * i + 1]
                }
                _key = this.tunings[_type]['tuning'][2 * i + 1];
                this.drawHole(_key, {
                    'class': 'hole'
                }, null, null, _compareKey);
                this.getHarmonicaLayout(_key, 'nd', i + 1, 'd')
            }
            __el('div', {
                'class': 'clear'
            }, this.tuningHolder);
            for (var j = 1; j < maxLowerBendDepth; j++) {
                for (var i = 0; i < _holeCnt; i++) {
                    if (this.tunings[_type]['tuning'][2 * i] + j < this.tunings[_type]['tuning'][2 * i + 1]) {
                        _key = this.tunings[_type]['tuning'][2 * i + 1] - j;
                        this.drawHole(_key, {
                            'class': 'hole bend'
                        });
                        this.getHarmonicaLayout(_key, 'b', i + 1, 'd' + _bends.substr(0, j))
                    } else if (this.tunings[_type]['tuning'][2 * i] > this.tunings[_type]['tuning'][2 * i + 1] && (i == _holeCnt - 1 || (i < _holeCnt - 1 && this.tunings[_type]['tuning'][2 * i] + j < this.tunings[_type]['tuning'][2 * i + 3])) && j == 1) {
                        _key = this.tunings[_type]['tuning'][2 * i] + j;
                        this.drawHole(_key, {
                            'class': 'hole over'
                        });
                        this.getHarmonicaLayout(_key, 'o', i + 1, 'dob')
                    } else {
                        _key = false;
                        this.drawHole(_key, {
                            'class': 'hole'
                        })
                    }
                }
                __el('div', {
                    'class': 'clear'
                }, this.tuningHolder)
            }
            return true
        };
        this.drawChromatic = function () {
            var _type = this.options.tuning;
            if (!this.tunings[_type]) {
                return false
            }
            var _notesPerHole = this.types[this.tunings[_type]['type']].notesPerHole;
            var _holeCnt = Math.floor(this.tunings[_type]['tuning'].length / _notesPerHole);
            this.tuningHolder.style.width = _holeCnt * 42 + "px";
            for (var i = 0; i < _holeCnt; i++) {
                _compareKey = null;
                if (this.options.compare != '') {
                    _compareKey = this.tunings[this.options.compare]['tuning'][_notesPerHole * i + 1]
                }
                _key = this.tunings[_type]['tuning'][_notesPerHole * i + 1];
                this.drawHole(_key, {
                    'class': 'hole bend'
                }, null, null, _compareKey);
                this.getHarmonicaLayout(_key, 'b', i + 1, 'bob')
            }
            __el('div', {
                'class': 'clear'
            }, this.tuningHolder);
            for (var i = 0; i < _holeCnt; i++) {
                _compareKey = null;
                if (this.options.compare != '') {
                    _compareKey = this.tunings[this.options.compare]['tuning'][_notesPerHole * i]
                }
                _key = this.tunings[_type]['tuning'][_notesPerHole * i];
                this.drawHole(_key, {
                    'class': 'hole'
                }, null, null, _compareKey);
                this.getHarmonicaLayout(_key, 'nb', i + 1, 'b')
            }
            __el('div', {
                'class': 'clear'
            }, this.tuningHolder);
            for (var i = 0; i < _holeCnt; i++) {
                var _holeKey = i + 1;
                if (_holeCnt > 14) {
                    if (_holeCnt - _holeKey < 12) {
                        _holeKey = _holeKey - (_holeCnt - 12)
                    }
                }
                this.drawHoleNumber(_holeKey, {
                    'class': 'hole number'
                })
            }
            __el('div', {
                'class': 'clear'
            }, this.tuningHolder);
            for (var i = 0; i < _holeCnt; i++) {
                _compareKey = null;
                if (this.options.compare != '') {
                    _compareKey = this.tunings[this.options.compare]['tuning'][_notesPerHole * i + 2]
                }
                _key = this.tunings[_type]['tuning'][_notesPerHole * i + 2];
                this.drawHole(_key, {
                    'class': 'hole'
                }, null, null, _compareKey);
                this.getHarmonicaLayout(_key, 'nd', i + 1, 'd')
            }
            __el('div', {
                'class': 'clear'
            }, this.tuningHolder);
            for (var i = 0; i < _holeCnt; i++) {
                _compareKey = null;
                if (this.options.compare != '') {
                    _compareKey = this.tunings[this.options.compare]['tuning'][_notesPerHole * i + 3]
                }
                _key = this.tunings[_type]['tuning'][_notesPerHole * i + 3];
                this.drawHole(_key, {
                    'class': 'hole bend'
                }, null, null, _compareKey);
                this.getHarmonicaLayout(_key, 'o', i + 1, 'dob')
            }
            __el('div', {
                'class': 'clear'
            }, this.tuningHolder);
            return true
        };
        this.drawHole = function (_key, _params, _holder, _startKey, _compareKey) {
            var _text = '&nbsp;';
            var __text = _text;
            if (typeof _startKey == 'undefined' || _startKey == null) {
                _startKey = this.options.key
            }
            if (_key !== false) {
                var __key = parseInt(_key) + _startKey;
                var _inScale = this.inScale(__key);
                if (_inScale === 0) {
                    _params['class'] += ' scale-key'
                } else if (_inScale > 0) {
                    _params['class'] += ' scale'
                }
                _text = this.getNoteByCode(__key);
                __text = _text;
                if (typeof _compareKey != 'undefined' && this.options.compare != '') {
                    _compareKey = parseInt(_compareKey) + _startKey;
                    var diff = __key - _compareKey + (parseInt(this.tunings[this.options.compare]['startOctave']) - parseInt(this.tunings[this.options.tuning]['startOctave'])) * 12;
                    if (diff != 0) {
                        diff *= -1;
                        if (diff > 0) {
                            diff = "+" + diff
                        }
                        _text += '<span class="cdiff">' + (diff) + '</span><span class="cnote">' + this.getNoteByCode(_compareKey) + '</span>'
                    }
                }
            }
            var _options = {
                html: _text
            };
            if (_params) {
                for (_k in _params) {
                    _options[_k] = _params[_k]
                }
            }
            if (_key === false) {
                _options['class'] = 'empty'
            }
            if (!_holder) {
                _holder = this.tuningHolder
            }
            var __hole = __el('div', _options, _holder);
            if (!(_key === false)) {
                __hole._this = this;
                __hole._note = __text.toLowerCase();
                if (_holder.className == this.tuningHolder.className) {
                    __hole._key = __key
                } else {
                    __hole._snote = __hole._note
                }
                __hole.onmouseover = this.holeOver;
                __hole.onmouseout = this.holeOver
            }
        };
        this.holeOver = function () {
            if (!this._this) {
                return false
            }
            var _els = __$$(this._this.mainHolder, 'div', 'hole');
            for (var i = 0; i < _els.length; i++) {
                if ((typeof this._note != 'undefined' && this._note == _els[i]._note) || (typeof this._key != 'undefined' && this._key == _els[i]._key)) {
                    if (_els[i].className.indexOf("hover") != -1) {
                        _els[i].className = _els[i].className.replace("hover", "")
                    } else {
                        _els[i].className += " hover"
                    }
                }
            }
            var _els = __$$(this._this.scaleTabLayout, 'span');
            for (var i = 0; i < _els.length; i++) {
                if ((typeof this._key != 'undefined' && this._key == _els[i]._key) || (typeof this._snote != 'undefined' && this._snote == _els[i]._tnote)) {
                    if (_els[i].className.indexOf("hover") != -1) {
                        _els[i].className = _els[i].className.replace("hover", "")
                    } else {
                        _els[i].className += " hover"
                    }
                }
            }
            return true
        };
        this.getNoteByCode = function (_code) {
            _code = this.removePeriod(parseInt(_code));
            return this.notes[this.options.sharp][_code]
        };
        this.inScale = function (_el) {
            if (this.scales[this.options.scale] && this.options.scaleKey !== false) {
                for (var i = 0; i < this.scales[this.options.scale].rule.length; i++) {
                    if (this.removePeriod(_el) == this.removePeriod(this.scales[this.options.scale].rule[i] + this.options.scaleKey)) {
                        return i
                    }
                }
            }
            return false
        };
        this.removePeriod = function (_el) {
            var _return = _el % this.notes[this.options.sharp].length;
            if (_return < 0) {
                _return += this.notes[this.options.sharp].length
            }
            return _return
        };
        this.drawHoleNumber = function (_number, _params) {
            var _options = {
                html: _number
            };
            if (_params) {
                for (_k in _params) {
                    _options[_k] = _params[_k]
                }
            }
            __el('div', _options, this.tuningHolder)
        };
        this.trackEvent = function () {
            try {
                pageTracker._trackEvent(this.options.type, this.options.tuning, this.getNoteByCode(this.options.key))
            } catch (e) {}
            if (this.options.scale) {
                try {
                    pageTracker._trackEvent('scale', this.options.scale, this.getNoteByCode(this.options.scaleKey))
                } catch (e) {}
            }
        }
    };

function mergeObj(obj1, obj2) {
    for (var p in obj2) {
        try {
            if (obj2[p].constructor == Object) {
                obj1[p] = mergeObj(obj1[p], obj2[p])
            } else {
                obj1[p] = obj2[p]
            }
        } catch (e) {
            obj1[p] = obj2[p]
        }
    }
    return obj1
};
