function Array2DTracer() {
if (Tracer.apply(this, arguments)) {
Array2DTracer.prototype.init.call(this);
return true;
}
return false;
}
Array2DTracer.prototype = $.extend(true, Object.create(Tracer.prototype), {
constructor: Array2DTracer,
init: function () {
this.$table = this.capsule.$table = $('
');
this.$container.append(this.$table);
},
_notify: function (x, y, v) {
this.manager.pushStep(this.capsule, {
type: 'notify',
x: x,
y: y,
v: v
});
return this;
},
_denotify: function (x, y) {
this.manager.pushStep(this.capsule, {
type: 'denotify',
x: x,
y: y
});
return this;
},
_select: function (sx, sy, ex, ey) {
this.pushSelectingStep('select', null, arguments);
return this;
},
_selectRow: function (x, sy, ey) {
this.pushSelectingStep('select', 'row', arguments);
return this;
},
_selectCol: function (y, sx, ex) {
this.pushSelectingStep('select', 'col', arguments);
return this;
},
_deselect: function (sx, sy, ex, ey) {
this.pushSelectingStep('deselect', null, arguments);
return this;
},
_deselectRow: function (x, sy, ey) {
this.pushSelectingStep('deselect', 'row', arguments);
return this;
},
_deselectCol: function (y, sx, ex) {
this.pushSelectingStep('deselect', 'col', arguments);
return this;
},
_separate: function (x, y) {
this.manager.pushStep(this.capsule, {
type: 'separate',
x: x,
y: y
});
return this;
},
_separateRow: function (x) {
this._separate(x, -1);
return this;
},
_separateCol: function (y) {
this._separate(-1, y);
return this;
},
_deseparate: function (x, y) {
this.manager.pushStep(this.capsule, {
type: 'deseparate',
x: x,
y: y
});
return this;
},
_deseparateRow: function (x) {
this._deseparate(x, -1);
return this;
},
_deseparateCol: function (y) {
this._deseparate(-1, y);
return this;
},
pushSelectingStep: function () {
var args = Array.prototype.slice.call(arguments);
var type = args.shift();
var mode = args.shift();
args = Array.prototype.slice.call(args.shift());
var coord;
switch (mode) {
case 'row':
coord = {
x: args[0],
sy: args[1],
ey: args[2]
};
break;
case 'col':
coord = {
y: args[0],
sx: args[1],
ex: args[2]
};
break;
default:
if (args[2] === undefined && args[3] === undefined) {
coord = {
x: args[0],
y: args[1]
};
} else {
coord = {
sx: args[0],
sy: args[1],
ex: args[2],
ey: args[3]
};
}
}
var step = {
type: type
};
$.extend(step, coord);
this.manager.pushStep(this.capsule, step);
},
processStep: function (step, options) {
switch (step.type) {
case 'notify':
if (step.v === 0 || step.v) {
var $row = this.$table.find('.mtbl-row').eq(step.x);
var $col = $row.find('.mtbl-col').eq(step.y);
$col.text(TracerUtil.refineByType(step.v));
}
case 'denotify':
case 'select':
case 'deselect':
var colorClass = step.type == 'select' || step.type == 'deselect' ? this.colorClass.selected : this.colorClass.notified;
var addClass = step.type == 'select' || step.type == 'notify';
var sx = step.sx;
var sy = step.sy;
var ex = step.ex;
var ey = step.ey;
if (sx === undefined) sx = step.x;
if (sy === undefined) sy = step.y;
if (ex === undefined) ex = step.x;
if (ey === undefined) ey = step.y;
this.paintColor(sx, sy, ex, ey, colorClass, addClass);
break;
case 'separate':
this.deseparate(step.x, step.y);
this.separate(step.x, step.y);
break;
case 'deseparate':
this.deseparate(step.x, step.y);
break;
default:
Tracer.prototype.processStep.call(this, step, options);
}
},
setData: function (D) {
this.viewX = this.viewY = 0;
this.paddingH = 6;
this.paddingV = 3;
this.fontSize = 16;
if (Tracer.prototype.setData.apply(this, arguments)) {
this.$table.find('.mtbl-row').each(function (i) {
$(this).find('.mtbl-col').each(function (j) {
$(this).text(TracerUtil.refineByType(D[i][j]));
});
});
return true;
}
this.$table.empty();
for (var i = 0; i < D.length; i++) {
var $row = $('
');
this.$table.append($row);
for (var j = 0; j < D[i].length; j++) {
var $col = $('
')
.css(this.getCellCss())
.text(TracerUtil.refineByType(D[i][j]));
$row.append($col);
}
}
this.resize();
return false;
},
resize: function () {
Tracer.prototype.resize.call(this);
this.refresh();
},
clear: function () {
Tracer.prototype.clear.call(this);
this.clearColor();
this.deseparateAll();
},
getCellCss: function () {
return {
padding: this.paddingV.toFixed(1) + 'px ' + this.paddingH.toFixed(1) + 'px',
'font-size': this.fontSize.toFixed(1) + 'px'
};
},
refresh: function () {
Tracer.prototype.refresh.call(this);
var $parent = this.$table.parent();
var top = $parent.height() / 2 - this.$table.height() / 2 + this.viewY;
var left = $parent.width() / 2 - this.$table.width() / 2 + this.viewX;
this.$table.css('margin-top', top);
this.$table.css('margin-left', left);
},
mousedown: function (e) {
Tracer.prototype.mousedown.call(this, e);
this.dragX = e.pageX;
this.dragY = e.pageY;
this.dragging = true;
},
mousemove: function (e) {
Tracer.prototype.mousemove.call(this, e);
if (this.dragging) {
this.viewX += e.pageX - this.dragX;
this.viewY += e.pageY - this.dragY;
this.dragX = e.pageX;
this.dragY = e.pageY;
this.refresh();
}
},
mouseup: function (e) {
Tracer.prototype.mouseup.call(this, e);
this.dragging = false;
},
mousewheel: function (e) {
Tracer.prototype.mousewheel.call(this, e);
e.preventDefault();
e = e.originalEvent;
var delta = (e.wheelDelta !== undefined && e.wheelDelta) ||
(e.detail !== undefined && -e.detail);
var weight = 1.01;
var ratio = delta > 0 ? 1 / weight : weight;
if (this.fontSize < 4 && ratio < 1) return;
if (this.fontSize > 40 && ratio > 1) return;
this.paddingV *= ratio;
this.paddingH *= ratio;
this.fontSize *= ratio;
this.$table.find('.mtbl-col').css(this.getCellCss());
this.refresh();
},
paintColor: function (sx, sy, ex, ey, colorClass, addClass) {
for (var i = sx; i <= ex; i++) {
var $row = this.$table.find('.mtbl-row').eq(i);
for (var j = sy; j <= ey; j++) {
var $col = $row.find('.mtbl-col').eq(j);
if (addClass) $col.addClass(colorClass);
else $col.removeClass(colorClass);
}
}
},
clearColor: function () {
this.$table.find('.mtbl-col').removeClass(Object.keys(this.colorClass).join(' '));
},
colorClass: {
selected: 'selected',
notified: 'notified'
},
separate: function (x, y) {
this.$table.find('.mtbl-row').each(function (i) {
var $row = $(this);
if (i == x) {
$row.after($('
').attr('data-row', i))
}
$row.find('.mtbl-col').each(function (j) {
var $col = $(this);
if (j == y) {
$col.after($('
').attr('data-col', j));
}
});
});
},
deseparate: function (x, y) {
this.$table.find('[data-row=' + x + ']').remove();
this.$table.find('[data-col=' + y + ']').remove();
},
deseparateAll: function () {
this.$table.find('.mtbl-empty-row, .mtbl-empty-col').remove();
}
});
var Array2D = {
random: function (N, M, min, max) {
if (!N) N = 10;
if (!M) M = 10;
if (min === undefined) min = 1;
if (max === undefined) max = 9;
var D = [];
for (var i = 0; i < N; i++) {
D.push([]);
for (var j = 0; j < M; j++) {
D[i].push((Math.random() * (max - min + 1) | 0) + min);
}
}
return D;
},
randomSorted: function (N, M, min, max) {
return this.random(N, M, min, max).map(function (arr) {
return arr.sort(function (a, b) {
return a - b;
});
});
}
};