API Docs for: 1.4.4
Show:

File: editor\ugsChordBuilder.editorUi.js

/**
 * Doing
 * @class editorUi
 * @constructor
 * @namespace ugsChordBuilder
 */
ugsChordBuilder.editorUi = function() {

	var _ids = {
		// top-most container, the "master drawing surface"
		container: 'cdBldEditorSurface',
		// stacked canvas elements
		cursorCanvas: 'cdBldCursorCanvas',
		diagramCanvas: 'cdBldDiagramCanvas',
		// chord definition meta: name & fret
		startingFret: 'cdBldStartingFret',
		chordName: 'cdBldChordName',
		// container wrapping sliding toolbox pallet
		toolbox: 'cdBldToolbox',
		// toolbox buttons:
		dotsBtn: 'cdBldDotsBtn',
		fingersBtn: 'cdBldFingersBtn',
		slideUpBtn: 'toolboxSlideUpBtn',
		slideDownBtn: 'toolboxSlideDownBtn',
		// display on "Hand" toolbox button
		btnFingerName: 'cdBldBtnFingerName',
		btnHandPic: 'cdBldBtnDiagram',
		// container for generated chordPro "define" tag (colorized)
		output: 'cdBldOutput',
		// show/hide definition output & container where we'll stuff the colorized definition
		showOutputBtn: 'cdBldShowOutputBtn',
		outputBox: 'cdBldOutputBox',
		cancelBtn: 'cdBldCancelBtn',
		saveBtn: 'cdBldSaveBtn'
		// openBtn: 'cdBldOpenBtn'
	};

	var _cursorCanvas = null,
		_eleDotsBtn = null,
		_eleFingerBtn = null;

	var _startingFret = 1;
	var _currentName = '';

	var _isDotToolActive = true;
	var _finger = 0;

	/**
	 * A "reverse Enum" dictionary of finger number to description
	 * @attribute _fingerNames
	 * @type {JSON}
	 */
	var _fingerNames = {
		0: 'None',
		1: 'Index finger',
		2: 'Middle finger',
		3: 'Ring finger',
		4: 'Pinkie'
	};

	/**
	 * Returns FALSE if canvas is not supported
	 * @method init
	 * @return {bool}
	 */
	this.init = function() {
		_cursorCanvas = document.getElementById(_ids.cursorCanvas);
		if (!_cursorCanvas.getContext) {
			return false;
		}

		var cursorContext = _cursorCanvas.getContext('2d');
		var diagramContext = document.getElementById(_ids.diagramCanvas).getContext('2d');

		var ele = document.getElementById(_ids.startingFret);
		addStartingFretOptions(ele);
		ele.addEventListener('change', onFretChange, false);

		document.getElementById(_ids.chordName).addEventListener('keyup', onNameChange, false);

		_eleDotsBtn = document.getElementById(_ids.dotsBtn);
		_eleFingerBtn = document.getElementById(_ids.fingersBtn);
		_eleDotsBtn.addEventListener('click', toggleTool, false);
		_eleFingerBtn.addEventListener('click', toggleTool, false);

		document.getElementById(_ids.showOutputBtn).addEventListener('click', showOutputBox, false);
		document.getElementById(_ids.cancelBtn).addEventListener('click', onCancelClick, false);
		document.getElementById(_ids.saveBtn).addEventListener('click', onSaveClick, false);

		document.getElementById(_ids.slideUpBtn).addEventListener('click', slide, false);
		document.getElementById(_ids.slideDownBtn).addEventListener('click', slide, false);

		updateFinger();

		document.getElementById(_ids.container).addEventListener('mousemove', onMouseMove, false);
		_cursorCanvas.addEventListener('click', onMouseClick, false);

		ugsChordBuilder.chordCanvas.init(diagramContext, _cursorCanvas);
		ugsChordBuilder.cursorCanvas.init(cursorContext);

		redraw();
		exportDefinition();

		ugsChordBuilder.chooserList.init(setChord);

		return true;
	};

	/**
	 * Successively clicking the Finger tool cycles through index to pinky, then none, and so on.
	 * This method automatically increments the "currently active finger" (odd sentence, that)
	 * and updates the toolbox UI.
	 * Note: changing the cursor is not handled here.
	 * @method updateFinger
	 * @private
	 * @return {void}
	 */
	var updateFinger = function() {
		_finger++;
		if (_finger > 4) {
			_finger = 0;
		}
		document.getElementById(_ids.btnFingerName).innerHTML = _fingerNames[_finger] + ' (' + _finger + ')';
		document.getElementById(_ids.btnHandPic).className = 'fingerToolImage finger' + _finger;
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var showOutputBox = function(evt) {
		setClass(document.getElementById(_ids.outputBox), 'collapseOutput', !evt.target.checked);
	};

	/**
	 * Cancel button's click event handler
	 * @method onCancelClick
	 */
	var onCancelClick = function(evt) {
		evt.preventDefault();
		reset();
		ugsChordBuilder.chooserList.show(true);
	};

	/**
	 * Save button's click event handler
	 * @method onSaveClick
	 */
	var onSaveClick = function(evt) {
		evt.preventDefault();
		var d = {
			name: _currentName,
			startingFret: _startingFret,
			dots: ugsChordBuilder.fretDots.getDots(),
			definition: ugsChordBuilder.export.getDefinition(_currentName, _startingFret)
		};
		if (!ugsChordBuilder.chooserList.save(d)) {
			var e = document.getElementById(_ids.chordName);
			e.focus();
			e.select();
		}
	};

	/**
	 * DANGER!! this is really a public method! A reference to this is passed to the
	 * scrapper during its initialization. Used to load ths Chord Builder
	 * @method setChord
	 * @param {Chord} chord
	 */
	var setChord = function(chord) {
		var isNew = chord == null;
		if (isNew) {
			reset();
			return;
		}

		var maxFret = findMaxFret(chord.dots);
		var startingFret = (maxFret > ugsChordBuilder.settings.fretBoard.numFrets) ? maxFret - ugsChordBuilder.settings.fretBoard.numFrets + 1 : 1;
		reset(chord.name, startingFret, convertDots(startingFret, chord.dots), false);
	};

	/**
	 * Converts standard scriptasaurus Dot array to chordBuilder dot array (fret changes)
	 * @method convertDots
	 * @param {int} startingFret
	 * @param  {array} builderDots
	 * @return {array}
	 */
	var convertDots = function(startingFret, builderDots) {
		var offset = startingFret - 1;
		var ugsDots = [];
		for (var i = 0; i < builderDots.length; i++) {
			var fret = builderDots[i].fret - offset;
			ugsDots.push(new ugsChordBuilder.entities.Dot(builderDots[i].string, (fret < 0 ? 0 : fret), builderDots[i].finger));
		}
		return ugsDots;
	};

	/**
	 * Loops over dots to find the largest fret value
	 * @method findMaxFret
	 * @param  {array} dots
	 * @return {ing}
	 */
	var findMaxFret = function(dots) {
		var max = 0;
		for (var i = 0; i < dots.length; i++) {
			if (dots[i].fret > max) {
				max = dots[i].fret;
			}
		}
		return max;
	};

	/**
	 * Updates the "standard form-like inputs"
	 * @method resetInputs
	 * @param {string} name
	 * @param {int} startingFret
	 * @param {bool} isNew
	 * @return {void}
	 */
	var resetInputs = function(name, startingFret, isNew) {
		_currentName = (name && name.length > 0) ? name : 'CHORDNAME';
		document.getElementById(_ids.chordName).value = _currentName;

		_startingFret = startingFret ? startingFret : 1;
		document.getElementById(_ids.startingFret).value = _startingFret;

		document.getElementById(_ids.showOutputBtn).checked = false;
		setClass(document.getElementById(_ids.outputBox), 'collapseOutput', true);

		document.getElementById(_ids.saveBtn).value = isNew ? 'Add' : 'Update';
	};

	/**
	 * Updates the Toolbox's current tool to be "Add Dot"; sets properties required for cursor, etc.
	 * @method resetCurrentTool
	 */
	var resetCurrentTool = function() {
		// restore current drawing tool (this is lame-o)
		// -----------------------------------------------
		_isDotToolActive = true;
		_finger = 0;
		updateFinger();
		setClass(_eleDotsBtn, 'selected', _isDotToolActive);
		setClass(_eleFingerBtn, 'selected', !_isDotToolActive);
		setClass(document.getElementById(_ids.toolbox), 'open', !_isDotToolActive);
		ugsChordBuilder.cursorCanvas.setCursor(_isDotToolActive, _finger);
	};

	/**
	 * Does a complete UI reset (if no values provided in params), otherwise this is kinda a "set"
	 * @method reset
	 * @param {string} name Chord name
	 * @param {int} startingFret
	 * @param {array} dots
	 * @param {bool} isNew Used to set the button text
	 */
	var reset = function(name, startingFret, dots, isNew) {
		isNew = arguments.length > 3 ? isNew : true;
		// fire cleanup on other classes...
		// -----------------------------------------------
		ugsChordBuilder.fretDots.reset();

		// easy elements back to default...
		// -----------------------------------------------
		resetInputs(name, startingFret, isNew);

		resetCurrentTool();

		if (dots && dots.length > 0) {
			for (var i = 0; i < dots.length; i++) {
				ugsChordBuilder.fretDots.toggleDot(dots[i]);
			}
		}

		// ok, probably done
		// -----------------------------------------------
		redraw();
		exportDefinition();
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var toggleTool = function(evt) {
		evt.preventDefault();
		var useDotTool = evt.currentTarget.href.indexOf('#dots') >= 0;
		if (useDotTool == _isDotToolActive) {
			if (!useDotTool) {
				updateFinger();
				ugsChordBuilder.cursorCanvas.setCursor(_isDotToolActive, _finger);
			}
			return;
		}

		setClass(_eleDotsBtn, 'selected', useDotTool);
		setClass(_eleFingerBtn, 'selected', !useDotTool);
		setClass(document.getElementById(_ids.toolbox), 'open', !useDotTool);

		_isDotToolActive = useDotTool;
		ugsChordBuilder.cursorCanvas.setCursor(_isDotToolActive, _finger);
	};

	/**
	 * Yet another poor man's jQuery envying add/remove CSS class method.
	 * @method
	 * @private
	 * @return {void}
	 */
	var setClass = function(element, className, isSet) {
		var hasClass = element.className.indexOf(className) >= 0;
		if (isSet && !hasClass) {
			// add
			element.className += ' ' + className;
		}
		else if (!isSet && hasClass) {
			// remove
			element.className = element.className.replace(className, '').replace(/\s+/g, ' ');
		}
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var addStartingFretOptions = function(ele) {
		var s = '';
		var lastValue = ugsChordBuilder.settings.fretBoard.maxFret - ugsChordBuilder.settings.fretBoard.numFrets + 1;
		for (var i = 1; i <= lastValue; i++) {
			s += '<option value="' + i + '">' + i + '</option>';
		}
		ele.innerHTML = s;
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var onNameChange = function(evt) {
		_currentName = this.value;
		exportDefinition();
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var onFretChange = function(evt) {
		_startingFret = parseInt(this.value, 10);
		exportDefinition();
		redraw();
	};

	/**
	 * Needs to watch for closed chords!
	 * @method
	 * @private
	 * @return {void}
	 */
	var slide = function(evt) {
		evt.preventDefault();
		var moveAllowed = false;
		var numSteps = evt.target.getAttribute('data-direction') == 'up' ? -1 : +1;
		if (ugsChordBuilder.fretDots.slide(numSteps)) {
			moveAllowed = true;
		}
		else {
			var newStart = _startingFret + numSteps;
			var lastValue = ugsChordBuilder.settings.fretBoard.maxFret - ugsChordBuilder.settings.fretBoard.numFrets + 1;
			if ((newStart >= 1) && (newStart <= lastValue)) {
				_startingFret = newStart;
				document.getElementById(_ids.startingFret).value = newStart;
				moveAllowed = true;
			}
		}

		if (moveAllowed) {
			redraw();
			exportDefinition();
		}
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var onMouseMove = function(evt) {
		ugsChordBuilder.cursorCanvas.draw(getPosition(this, evt));
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var onMouseClick = function(evt) {
		var pos = getPosition(_cursorCanvas, evt);
		var dot = ugsChordBuilder.tracking.toDot(pos);
		if (!dot) {
			return;
		}

		if (_isDotToolActive) {
			ugsChordBuilder.fretDots.toggleDot(dot);
			redraw(pos);
			exportDefinition();
		}
		else if (ugsChordBuilder.fretDots.toggleFinger(dot, _finger)) {
			redraw(pos);
			exportDefinition();
		}
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var getPosition = function(canvas, evt) {
		var rect = canvas.getBoundingClientRect();
		return new ugsChordBuilder.entities.Position(
			evt.clientX - rect.left,
			evt.clientY - rect.top
		);
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var redraw = function(pos) {
		pos = pos || new ugsChordBuilder.entities.Position(0, 0);
		ugsChordBuilder.chordCanvas.draw(pos, _startingFret);
	};

	/**
	 * @method
	 * @private
	 * @return {void}
	 */
	var exportDefinition = function() {
		document.getElementById(_ids.output).innerHTML = ugsChordBuilder.export.getDefinitionHtml(_currentName, _startingFret);
	};

	this.reload = function() {
		reset();
		ugsChordBuilder.chooserList.reset();
		ugsChordBuilder.chooserList.show(true);
	};

};