/**
* Hilo
* Copyright 2015 alibaba.com
* Licensed under the MIT License
*/
/**
*
*
* @class Text class provide basic text-display function, use DOMElement for complex text-display.
* @augments View
* @mixes CacheMixin
* @borrows CacheMixin#cache as #cache
* @borrows CacheMixin#updateCache as #updateCache
* @borrows CacheMixin#setCacheDirty as #setCacheDirty
* @param {Object} properties Properties parameters for the object. Includes all writable properties.
* @module hilo/view/Text
* @requires hilo/core/Class
* @requires hilo/core/Hilo
* @requires hilo/view/View
* @requires hilo/view/CacheMixin
* @property {String} text Text to display.
* @property {String} color Color of the text.
* @property {String} textAlign Horizontal alignment way of the text. May be one of the following value:'start', 'end', 'left', 'right', and 'center'. Note:Need to specify the width property of the text to take effect
* @property {String} textVAlign Vertical alignment way of the text. May be one of the following value:'top', 'middle', 'bottom'. Note:Need to specify the height property of the text to take effect.
* @property {Boolean} outline Draw the outline of the text or fill the text.
* @property {Number} lineSpacing The spacing between lines. Measured in px, default value is 0.
* @property {Number} maxWidth The max length of the text, default value is 200.
* @property {String} font Text's CSS font style, readonly! Use setFont function to set text font.
* @property {Number} textWidth Width of the text, readonly! Works only on canvas mode.
* @property {Number} textHeight Height of the text, readonly! Works only on canvas mode.
*/
var Text = Class.create(/** @lends Text.prototype */{
Extends: View,
Mixes:CacheMixin,
constructor: function(properties){
properties = properties || {};
this.id = this.id || properties.id || Hilo.getUid('Text');
Text.superclass.constructor.call(this, properties);
// if(!properties.width) this.width = 200; //default width
if(!properties.font) this.font = '12px arial'; //default font style
this._fontHeight = Text.measureFontHeight(this.font);
},
text: '',
color: '#000',
textAlign: null,
textVAlign: null,
outline: false,
lineSpacing: 0,
maxWidth: 200,
font: null, //ready-only
textWidth: 0, //read-only
textHeight: 0, //read-only
/**
* Set text CSS font style.
* @param {String} font Text CSS font style to set.
* @returns {Text} the Text object, chained call supported.
*/
setFont: function(font){
var me = this;
if(me.font !== font){
me.font = font;
me._fontHeight = Text.measureFontHeight(font);
}
return me;
},
/**
* Overwrite render function.
* @private
*/
render: function(renderer, delta){
var me = this;
if(renderer.renderType === 'canvas'){
if(this.drawable){
renderer.draw(me);
}
else{
me._draw(renderer.context);
}
}
else if(renderer.renderType === 'dom'){
var drawable = me.drawable;
var domElement = drawable.domElement;
var style = domElement.style;
style.font = me.font;
style.textAlign = me.textAlign;
style.color = me.color;
style.width = me.width + 'px';
style.height = me.height + 'px';
style.lineHeight = (me._fontHeight + me.lineSpacing) + 'px';
domElement.innerHTML = me.text;
renderer.draw(this);
}
else{
//TODO:自动更新cache TODO:auto update cache
me.cache();
renderer.draw(me);
}
},
/**
* Draw text under the assigned render context.
* @private
*/
_draw: function(context){
var me = this, text = me.text.toString();
if(!text) return;
//set drawing style
context.font = me.font;
context.textAlign = me.textAlign;
context.textBaseline = 'top';
//find and draw all explicit lines
var lines = text.split(/\r\n|\r|\n|
/);
var width = 0, height = 0;
var lineHeight = me._fontHeight + me.lineSpacing;
var i, line, w, len, wlen;
var drawLines = [];
for(i = 0, len = lines.length; i < len; i++){
line = lines[i];
w = context.measureText(line).width;
//check if the line need to split
if(w <= me.maxWidth){
drawLines.push({text:line, y:height});
// me._drawTextLine(context, line, height);
if(width < w) width = w;
height += lineHeight;
continue;
}
var str = '', oldWidth = 0, newWidth, j, word;
for(j = 0, wlen = line.length; j < wlen; j++){
word = line[j];
newWidth = context.measureText(str + word).width;
if(newWidth > me.maxWidth){
drawLines.push({text:str, y:height});
// me._drawTextLine(context, str, height);
if(width < oldWidth) width = oldWidth;
height += lineHeight;
str = word;
}else{
oldWidth = newWidth;
str += word;
}
if(j == wlen - 1){
drawLines.push({text:str, y:height});
// me._drawTextLine(context, str, height);
if(str !== word && width < newWidth) width = newWidth;
height += lineHeight;
}
}
}
me.textWidth = width;
me.textHeight = height;
if(!me.width) me.width = width;
if(!me.height) me.height = height;
//vertical alignment
var startY = 0;
switch(me.textVAlign){
case 'middle':
startY = me.height - me.textHeight >> 1;
break;
case 'bottom':
startY = me.height - me.textHeight;
break;
}
//draw background
var bg = me.background;
if(bg){
context.fillStyle = bg;
context.fillRect(0, 0, me.width, me.height);
}
if(me.outline) context.strokeStyle = me.color;
else context.fillStyle = me.color;
//draw text lines
for(i = 0; i < drawLines.length; i++){
line = drawLines[i];
me._drawTextLine(context, line.text, startY + line.y);
}
},
/**
* Draw a line of text under the assigned render context.
* @private
*/
_drawTextLine: function(context, text, y){
var me = this, x = 0, width = me.width;
switch(me.textAlign){
case 'center':
x = width >> 1;
break;
case 'right':
case 'end':
x = width;
break;
}
if(me.outline) context.strokeText(text, x, y);
else context.fillText(text, x, y);
},
Statics: /** @lends Text */{
/**
* Measure the line height of the assigned text font style.
* @param {String} font Font style to measure.
* @return {Number} Return line height of the assigned font style.
*/
measureFontHeight: function(font){
var docElement = document.documentElement, fontHeight;
var elem = Hilo.createElement('div', {style:{font:font, position:'absolute'}, innerHTML:'M'});
docElement.appendChild(elem);
fontHeight = elem.offsetHeight;
docElement.removeChild(elem);
return fontHeight;
}
}
});