หน้าเว็บ

วันพฤหัสบดีที่ 24 เมษายน พ.ศ. 2557

TextHighlighter javascript


TextHighlighter.js
/**
 * @author jittagorn pitakmetagoon
 * create 24/04/2014
 * source code : http://na5cent.blogspot.com/2014/04/texthighlighter-javascript.html
 */
window.TextHighlighter = window.TextHighlighter || (function() {

    function forEachProperty(obj, callback, ctx_opt) {
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                var val = callback.call(ctx_opt, obj[prop], prop, obj);
                if (val === false) {
                    return false;
                }
            }
        }

        return true;
    }

    function forEachIndex(array, callback, ctx_opt) {
        var length = array.length;
        for (var i = 0; i < length; i++) {
            var val = callback.call(ctx_opt, array[i], i, array, length);
            if (val === false) {
                return false;
            }
        }

        return true;
    }

    /**
     * @class Strings
     */
    var Strings = {
        
        split: function(text, splitor) {
            var result = [];
            var list = text.split(splitor);
            forEachIndex(list, function(txt) {
                !!txt && result.push(txt);
            });

            return result;
        },
        
        escapse: function(text) {
            return text.replace(/\&/g, '&amp;')
                    .replace(/\>/g, '&gt;')
                    .replace(/\</g, '&lt;');
        }
    };

    function toArray(obj) {
        var array = [];
        forEachProperty(obj, function(value) {
            array.push(value);
        });

        return array;
    }

    function isEquals(a, b) {
        if (a.equals) {
            return a.equals(b);
        }

        return a === b;
    }

    function removeElementInArray(element, array) {
        forEachIndex(array, function(value, index) {
            if (isEquals(element, value)) {
                array.splice(index, 1);
            }
        });
    }

    /**
     * @class Period
     * for keep period information
     */
    var Period = function(start, end) {
        this.start_ = start;
        this.end_ = end;
    };

    Period.prototype.getStart = function() {
        return this.start_;
    };

    Period.prototype.getEnd = function() {
        return this.end_;
    };

    Period.prototype.setStart = function(start) {
        this.start_ = start;
    };

    Period.prototype.setEnd = function(end) {
        this.end_ = end;
    };

    Period.prototype.equals = function(object) {
        if (!(object instanceof Period)) {
            return false;
        }

        if (object.getStart() === this.start_ && object.getEnd() === this.end_) {
            return true;
        }

        return false;
    };

    Period.prototype.toString = function() {
        return 'period {' + this.start_ + ', ' + this.end_ + '}';
    };


    /**
     * @class PeriodIntegrator
     * for integrate period in timeline
     * 
     * document : http://na5cent.blogspot.com/2013/12/integrate-period-algorithm-java.html
     */
    var PeriodIntegrator = function() {

        var periodSet_ = {};
        var periodList_;

        this.addPeriod = function(period) {
            var key = period.toString();
            periodSet_[key] = period;

            return this;
        };

        this.addAllPeriods = function(periodList) {
            forEachIndex(periodList, addPeriod, this);
            return this;
        };

        function sortPeriods() {
            periodList_ = toArray(periodSet_).sort(function(p1, p2) {
                if (p1.getStart() === p2.getStart()) {
                    return p1.getEnd() - p2.getEnd();
                }

                return p1.getStart() - p2.getStart();
            });
        }

        function forEachPeriodIndex(callback) {
            var length = periodList_.length;
            if (length < 2) {
                return;
            }

            for (var i = 1; i < length; i++) {
                callback.call(null, i, length);
            }
        }

        function changeOverlap() {
            forEachPeriodIndex(function(i) {
                var before = periodList_[i - 1];
                var current = periodList_[i];

                if (current.getStart() < before.getEnd()) {
                    current.setStart(before.getEnd());
                }
            });
        }

        function removeIncorrect() {
            forEachPeriodIndex(function(i, length) {
                var periodI = periodList_[i];
                for (var j = i + 1; j < length; j++) {
                    var periodJ = periodList_[j];

                    var isDefined = periodI && periodJ;
                    if (isDefined && (isIncorrect(periodJ) || isSubPeriod(periodJ, periodI))) {
                        removeElementInArray(periodJ, periodList_);
                        length--;
                    }
                }
            });
        }

        function isIncorrect(period) {
            return period.getStart() >= period.getEnd();
        }

        function isSubPeriod(period1, period2) {
            return period1.getStart() >= period2.getStart() && period1.getEnd() <= period2.getEnd();
        }

        function integratePeriods() {
            forEachPeriodIndex(function(i, length) {
                var before = periodList_[i - 1];
                var current = periodList_[i];

                var isDefined = current && before;
                if (isDefined && current.getStart() === before.getEnd()) {
                    current.setStart(before.getStart());
                    removeElementInArray(before, periodList_);
                    i--;
                    length--;
                }
            });
        }

        this.integrate = function() {
            sortPeriods();
            changeOverlap();
            removeIncorrect();
            integratePeriods();

            return periodList_;
        };
    };

    /**
     * @class TextHighlighter
     */
    var TextHighlighter = function(options_opt) {
        options_opt = options_opt || {};

        this.styleClass_ = options_opt.styleClass || 'ns-text-highlighter';
        this.totalHighlight_ = 0;
    };

    /**
     * @private
     */
    TextHighlighter.prototype.wrapKeywordByHighlightBox = function(keyword) {
        return '<span class="' + this.styleClass_ + '">' + keyword + '</span>';
    };

    TextHighlighter.prototype.getTotalHighlight = function() {
        return this.totalHighlight_;
    };

    /**
     * @private
     */
    TextHighlighter.prototype.findPeriodsOfSentenceByKeyword = function(sentence, keyword) {
        var integrator = new PeriodIntegrator();

        forEachIndex(Strings.split(keyword, ' '), function(kword) {
            var length = kword.length;
            var start = 0;
            var end = 0;
            while (true) {
                var indexOf = sentence.indexOf(kword, end);
                if (indexOf === -1) {
                    break;
                }

                start = indexOf;
                end = indexOf + length;

                integrator.addPeriod(new Period(start, end));
            }
        });

        return integrator.integrate();
    };

    /**
     * @param {String} sentence
     * @param {String} keyword
     * @returns {String} highlighted sentence
     */
    TextHighlighter.prototype.highlight = function(sentence, keyword) {
        sentence = Strings.escapse(sentence);
        var periods = this.findPeriodsOfSentenceByKeyword(sentence.toLowerCase(), keyword.toLowerCase());
        var size = 0;

        forEachIndex(periods, function(period) {
            var start = period.getStart() + size;
            var end = period.getEnd() + size;

            var infront = sentence.substring(0, start);
            //----------------------------------------------------------------
            var word = sentence.substring(start, end);
            var highlighted = this.wrapKeywordByHighlightBox(word);
            //----------------------------------------------------------------
            var behind = sentence.substring(end);

            sentence = infront + highlighted + behind;
            size = size + highlighted.length - word.length;

            this.totalHighlight_++;
        }, this);

        return sentence;
    };

    return TextHighlighter;

})();
ตัวอย่างการใช้งาน
<style type="text/css">
    .ns-text-highlighter{
        background-color : #FFFBA0;
    }
</style>

<div id="content">
ในป่าแห่งหนึ่่ง  สัตว์ทุกตัว  ทุกชนิดต้องการก่อตั้งโรงเรียนขึ้นมา  โดยการออกหลักสูตรนั้น  สัตว์ทุกชนิดช่วยกันออก  โดยการเลือกผู้นำหัวกะทิที่เก่งที่สุดของสัตว์แต่ละชนิดของพวกตน  มาเขียนหลักสูตรช่วยกัน  เช่น ปลาบอกว่าต้องมีวิชาว่ายน้ำ ลิงบอกว่าต้องมีวิชาไต่ราวหรือปีนต้นไม้  ช้างบอกว่า ต้องมีวิชาลากท่อนซุงหรือโค่นต้นไม้  เสือบอกว่าต้องมีวิชาล่าเหยื่อ  นกบอกว่าต้องมีวิชาบินเหินเวหา  ตัวตุ่นบอกว่าต้องมีวิชาขุดโพรงเพื่อทำที่อยู่อาศัย  ฯลฯ  จากนั้นจึงได้นำหลักสูตรต่างๆ ที่สัตว์ทุกชนิดเสนอมาตั้งเป็นหลักสูตรกลาง  เพื่อให้สัตว์ทุกชนิดได้เรียนในทุกๆวิชาที่กล่าวมาทั้งหมด  เพราะอยากให้สัตว์ทุกตัวในป่ามีคุณภาพ และมีความสามารถเท่าๆกันหมด  เพราะคิดว่าทุกตัวได้เรียนในสิ่งเดียวกัน  จะต้องมีความสามารถทำได้เหมือนกัน  
       ผลออกมาคือ  ช้างสอบตกวิชาบิน  วิชาปีนต้นไม้  วิชาขุดโพรง ...  แต่สอบผ่านวิชาลากท่อนซุงหรือวิชาโค่นต้นไม้  เสือสอบตกวิชาบิน  วิชาไต่ราว  เสืออาจจะพอปีนต้นไม้ได้ แต่ก็ไม่ได้ถนัด เพราะสอบได้คะแนนไม่ถึงเกณฑ์ตามที่ลิงได้ตั้งเอาไว้  เลยสอบตก  ปลาสอบตกวิชาบิน  ... สอบผ่านวิชาว่ายน้ำ  แถมได้คะแนนเต็มซ่ะด้วย  นกสอบตกวิชาว่ายน้ำ..  แต่สอบผ่านวิชาบินเหินเวหา  ฯลฯ   สัตว์หัวกะทิทุกชนิดพากันสงสัยว่าทำไมลักสูตรที่ตนออกมานั้น  มันจึงไม่มีประสิทธิภาพ  ทำไมสัตว์ทุกตัวถึงทำได้ไม่เหมือนกัน  ทั้งๆที่พวกเขาก็เรียนวิชาเดียวกัน  ทุกวิชาเหมือนกันทั้งหมด
</div>
var highlighter = new TextHighlighter();
var content = document.getElementById('content'); 
var html = content.innerHTML;
content.innerHTML = highlighter.highlight(html, 'สัตว์ วิชา สอบ สอบได้ สัตว์ทุกตัว');
การประยุกต์ใช้
slide search ทางซ้ายมือของ blog นี้ครับ  เอาไว้ highlight keyword search

ไม่มีความคิดเห็น:

แสดงความคิดเห็น