Caret.js-0.3.1/000077500000000000000000000000001266425773400132045ustar00rootroot00000000000000Caret.js-0.3.1/.gitignore000066400000000000000000000001211266425773400151660ustar00rootroot00000000000000node_modules/ build/ bower_components/ _SpecRunner.html .grunt/ *.map components/Caret.js-0.3.1/CHANGELOG.md000066400000000000000000000021131266425773400150120ustar00rootroot00000000000000### v0.2.1 * f66a1eb - can get offset at the benginning of a line * 4885ddd - fix wrong position of textarea ### v0.2.0 * 12119d2 - calculating in iframe's coordinate * 959436d - implement `position` api for contentEditable * d051ffc - fix html escaping while mirroring caret ### v0.1.0 * b1f8f53 - fix Mirror div does not reset its CSS * e88e40e - fix Bad positioning in long words * 37d4c5e - disable auto decovery iframe ### v0.0.7 * cf94271 - Added suport for .caret(pos, 0) - Nicolas Donna * 2de2b0f - Fixed error when checking the pos arg when setting the position - Nicolas Donna * 34ac7fa - catch error thrown in cross-domain iframe - jiyinyiyong * 01f1fa1 - add minified file in dist. ### v0.0.6 * 287b5d8 working in iframe ### v0.0.5 * aef0aa4 fix IE input position error * 4a4f7f7 fix contenteditable null value bug ### v0.0.4 * fix scrolling problem ### v0.0.2 * support `contentEditable` mode * fix ie bugs, and support IE > 6 to all mode ### 2013-08-07 * fix bug: error position at beginning of textarea * Bower * jasmine test ### 2013-03-31 * support IE browsers. Caret.js-0.3.1/Gruntfile.coffee000066400000000000000000000040161266425773400163150ustar00rootroot00000000000000module.exports = (grunt) -> grunt.initConfig pkg: grunt.file.readJSON 'package.json' bower_path: 'bower_components' jasmine: src: 'src/*.js' options: vendor: [ '<%= bower_path %>/jquery/dist/jquery.min.js', '<%= bower_path %>/jasmine-jquery/lib/jasmine-jquery.js' ] specs: 'spec/javascripts/*.js' # keepRunner: true uglify: options: banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' build: files: 'dist/<%= pkg.name %>.min.js': ['src/<%= pkg.name %>.js'] coffee: withMaps: options: bare: true sourceMap: true files: 'src/<%= pkg.name %>.js': 'src/<%= pkg.name %>.coffee' withoutMaps: options: bare: true sourceMap: false files: 'dist/<%= pkg.name %>.js': 'src/<%= pkg.name %>.coffee' watch: scripts: files: ['src/*.coffee'] tasks: ['coffee', 'umd'] umd: options: template: 'umd' deps: 'default': ['$'] amd: ['jquery'] cjs: ['jquery'] global: items: ['jQuery'] prefix: '' src: src: 'src/<%= pkg.name %>.js' dist: src: 'dist/<%= pkg.name %>.js' 'json-replace': options: space: " ", replace: version: "<%= pkg.version %>" 'update-version': files:[{ 'bower.json': 'bower.json', 'component.json': 'component.json' }] grunt.loadNpmTasks 'grunt-contrib-coffee' grunt.loadNpmTasks 'grunt-contrib-uglify' grunt.loadNpmTasks 'grunt-contrib-jasmine' grunt.loadNpmTasks 'grunt-json-replace' grunt.loadNpmTasks 'grunt-contrib-watch' grunt.loadNpmTasks 'grunt-umd' grunt.registerTask 'update-version', 'json-replace' grunt.registerTask 'default', ['coffee', 'umd', 'jasmine','update-version', 'uglify', 'watch'] grunt.registerTask 'test', ['coffee', 'umd', 'jasmine'] Caret.js-0.3.1/LICENSE-MIT000066400000000000000000000020471266425773400146430ustar00rootroot00000000000000Copyright (c) 2013 chord.luo@gmail.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Caret.js-0.3.1/README.md000066400000000000000000000024421266425773400144650ustar00rootroot00000000000000Caret.js ======== Get caret position or offset from inputor This is the core function that working in [At.js](http://ichord.github.com/At.js). Now, It just become an simple jquery plugin so that everybody can use it. And, of course, **At.js** is using this plugin too. * support iframe context Live Demo ========= http://ichord.github.com/Caret.js/ Usage ===== ```javascript // Get caret position $('#inputor').caret('position'); // => {left: 15, top: 30, height: 20} // Get caret offset $('#inputor').caret('offset'); // => {left: 300, top: 400, height: 20} var fixPos = 20 // Get position of the 20th char in the inputor. // not working in `contentEditable` mode $('#inputor').caret('position', fixPos); // Get offset of the 20th char. // not working in `contentEditable` mode $('#inputor').caret('offset', fixPos); // more // Get caret position from the first char in the inputor. $('#inputor').caret('pos'); // => 15 // Set caret position in the inputor $('#inputor').caret('pos', 15); // set iframe context // NOTE: Related to the iframe's cooridinate. // You might want to get the iframe's offset/position on your own $('#inputor').caret('offset', {iframe: theIframe}); $('#inputor').caret('position', {iframe: theIframe}); $('#inputor').caret('pos', 15, {iframe: theIframe}); ``` Caret.js-0.3.1/bower.json000066400000000000000000000004751266425773400152230ustar00rootroot00000000000000{ "name": "Caret.js", "version": "0.3.1", "main": "src/jquery.caret.js", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests", "spec", "index.html" ], "dependencies": { "jquery": ">=1.7.0" }, "devDependencies": { "jasmine-jquery": "~1.5.8" } }Caret.js-0.3.1/component.json000066400000000000000000000006311266425773400161010ustar00rootroot00000000000000{ "name": "Caret.js", "repo": "ichord/Caret.js", "description": "Add Github like mentions autocomplete to your application.", "version": "0.3.1", "keywords": [ "At.js", "caret", "ui" ], "dependencies": { "component/jquery": "*" }, "demo": "http://ichord.github.com/Caret.js", "main": "src/jquery.caret.js", "scripts": [ "src/jquery.caret.js" ], "license": "MIT" }Caret.js-0.3.1/index.html000066400000000000000000000141511266425773400152030ustar00rootroot00000000000000 Caret.js

Caret.js

Textarea

ContentEditable

Hello, some bold and italic and bold text

iframe body

Caret.js-0.3.1/package.json000066400000000000000000000012211266425773400154660ustar00rootroot00000000000000{ "name": "jquery.caret", "version": "0.3.1", "description": "Get caret position and offset from inputor", "main": "dist/jquery.caret.js", "dependencies": { "grunt": "~0.4.1" }, "devDependencies": { "grunt-contrib-coffee": "~0.6.4", "grunt-contrib-jasmine": "~0.5.1", "grunt-contrib-uglify": "~0.2.0", "grunt-contrib-watch": "^0.6.1", "grunt-json-replace": "~0.1.2", "grunt-umd": "^2.2.1" }, "scripts": { "test": "grunt test --verbose" }, "repository": "", "keywords": [ "jquery", "caret", "offset", "position" ], "author": "Harold.luo ", "license": "MIT" } Caret.js-0.3.1/spec/000077500000000000000000000000001266425773400141365ustar00rootroot00000000000000Caret.js-0.3.1/spec/javascripts/000077500000000000000000000000001266425773400164675ustar00rootroot00000000000000Caret.js-0.3.1/spec/javascripts/caret.js000066400000000000000000000067271266425773400201370ustar00rootroot00000000000000describe('jquery.caret', function() { var $inputor; $inputor = null; var fixPos = 0; describe('InputCaret', function() { beforeEach(function() { var html = '' + ''; var fixture = setFixtures(html); $inputor = fixture.find('#inputor'); var fixPos = 20; }); it('Set/Get caret pos', function() { $inputor.caret('pos', 15); expect($inputor.caret('pos')).toBe(15); }); // TODO: I don't know how to test this functions yet. = =. // it("Set/Get caret position", function() { // $inputor.caret('position', 20); // pos = $inputor.caret('position'); // => {left: 15, top: 30, height: 20} // expect(pos).toBe({ left : 2, top : 2, height : 17 }); // }); // $('#inputor').caret('offset'); // => {left: 300, top: 400, height: 20} // $('#inputor').caret('offset', fixPos); }); describe('EditableCaret', function() { beforeEach(function() { var contentEditable = '' + '
' + 'Hello ' + 'World' + '! ' + '

' + '
' + '
    ' + '
  • Testing 1
  • ' + '
  • Testin 2
  • ' + '
' + '
--
' + '
' + '

' + '
'; var fixture = setFixtures(contentEditable); $inputor = fixture.find('#inputor'); }); it('sets the caret position at the top-level', function() { $inputor.caret('pos', 3); var selection = window.getSelection(); expect(selection.anchorNode.nodeValue).toBe('Hello '); expect(selection.anchorOffset).toBe(3); expect($inputor.caret('pos')).toBe(3); }); it('sets the caret position in a span', function() { $inputor.caret('pos', 8); var selection = window.getSelection(); expect(selection.anchorNode.nodeValue).toBe('World'); expect(selection.anchorOffset).toBe(2); expect($inputor.caret('pos')).toBe(8); }); it('sets the caret position in a list item', function() { $inputor.caret('pos', 16); var selection = window.getSelection(); expect(selection.anchorNode.nodeValue).toBe('Testing 1'); expect(selection.anchorOffset).toBe(3); expect($inputor.caret('pos')).toBe(16); }); it('sets the caret position at the end of a list item', function() { $inputor.caret('pos', 30); var selection = window.getSelection(); expect(selection.anchorNode.nodeValue).toBe('Testin 2'); expect(selection.anchorOffset).toBe(8); expect($inputor.caret('pos')).toBe(30); }); describe('Edge case from Gmail', function() { beforeEach(function() { var content = '' + '
' + 'Hello ' + 'just want' + '' + '
---
' + '
'; var fixture = setFixtures(content); $inputor = fixture.find('#inputor'); }); it('sets the caret position when two child nodes match the condition', function() { $inputor.caret('pos', 16); var selection = window.getSelection(); expect(selection.anchorNode.nodeValue).toBe('Hi'); expect(selection.anchorOffset).toBe(1); expect($inputor.caret('pos')).toBe(16); }); }); }); }); Caret.js-0.3.1/src/000077500000000000000000000000001266425773400137735ustar00rootroot00000000000000Caret.js-0.3.1/src/jquery.caret.coffee000066400000000000000000000226301266425773400175630ustar00rootroot00000000000000### Implement Github like autocomplete mentions http://ichord.github.com/At.js Copyright (c) 2013 chord.luo@gmail.com Licensed under the MIT license. ### ### 本插件操作 textarea 或者 input 内的插入符 只实现了获得插入符在文本框中的位置,我设置 插入符的位置. ### "use strict"; pluginName = 'caret' class EditableCaret constructor: (@$inputor) -> @domInputor = @$inputor[0] # NOTE: Duck type setPos: (pos) -> if sel = oWindow.getSelection() offset = 0 found = false do fn = (pos, parent=@domInputor) -> for node in parent.childNodes if found break if node.nodeType == 3 if offset + node.length >= pos found = true range = oDocument.createRange() range.setStart(node, pos - offset) sel.removeAllRanges() sel.addRange(range) break else offset += node.length else fn(pos, node) @domInputor getIEPosition: -> this.getPosition() getPosition: -> offset = this.getOffset() inputor_offset = @$inputor.offset() offset.left -= inputor_offset.left offset.top -= inputor_offset.top offset getOldIEPos: -> textRange = oDocument.selection.createRange() preCaretTextRange = oDocument.body.createTextRange() preCaretTextRange.moveToElementText(@domInputor) preCaretTextRange.setEndPoint("EndToEnd", textRange) preCaretTextRange.text.length getPos: -> if range = this.range() # Major Browser and IE > 10 clonedRange = range.cloneRange() clonedRange.selectNodeContents(@domInputor) clonedRange.setEnd(range.endContainer, range.endOffset) pos = clonedRange.toString().length clonedRange.detach() pos else if oDocument.selection #IE < 9 this.getOldIEPos() getOldIEOffset: -> range = oDocument.selection.createRange().duplicate() range.moveStart "character", -1 rect = range.getBoundingClientRect() { height: rect.bottom - rect.top, left: rect.left, top: rect.top } getOffset: (pos) -> if oWindow.getSelection and range = this.range() # endContainer would be the inputor in Firefox at the begnning of a line if range.endOffset - 1 > 0 and range.endContainer isnt @domInputor clonedRange = range.cloneRange() clonedRange.setStart(range.endContainer, range.endOffset - 1) clonedRange.setEnd(range.endContainer, range.endOffset) rect = clonedRange.getBoundingClientRect() offset = { height: rect.height, left: rect.left + rect.width, top: rect.top } clonedRange.detach() # At the begnning of the inputor, the offset height is 0 in Chrome and Safari # This work fine in all browers but except while the inputor break a line into two (wrapped line). # so we can't use it in all cases. if !offset or offset?.height == 0 clonedRange = range.cloneRange() shadowCaret = $ oDocument.createTextNode "|" clonedRange.insertNode shadowCaret[0] clonedRange.selectNode shadowCaret[0] rect = clonedRange.getBoundingClientRect() offset = {height: rect.height, left: rect.left, top: rect.top } shadowCaret.remove() clonedRange.detach() else if oDocument.selection # ie < 9 offset = this.getOldIEOffset() if offset offset.top += $(oWindow).scrollTop() offset.left += $(oWindow).scrollLeft() offset range: -> return unless oWindow.getSelection sel = oWindow.getSelection() if sel.rangeCount > 0 then sel.getRangeAt(0) else null class InputCaret constructor: (@$inputor) -> @domInputor = @$inputor[0] getIEPos: -> # https://github.com/ichord/Caret.js/wiki/Get-pos-of-caret-in-IE inputor = @domInputor range = oDocument.selection.createRange() pos = 0 # selection should in the inputor. if range and range.parentElement() is inputor normalizedValue = inputor.value.replace /\r\n/g, "\n" len = normalizedValue.length textInputRange = inputor.createTextRange() textInputRange.moveToBookmark range.getBookmark() endRange = inputor.createTextRange() endRange.collapse false if textInputRange.compareEndPoints("StartToEnd", endRange) > -1 pos = len else pos = -textInputRange.moveStart "character", -len pos getPos: -> if oDocument.selection then this.getIEPos() else @domInputor.selectionStart setPos: (pos) -> inputor = @domInputor if oDocument.selection #IE range = inputor.createTextRange() range.move "character", pos range.select() else if inputor.setSelectionRange inputor.setSelectionRange pos, pos inputor getIEOffset: (pos) -> textRange = @domInputor.createTextRange() pos ||= this.getPos() textRange.move('character', pos) x = textRange.boundingLeft y = textRange.boundingTop h = textRange.boundingHeight {left: x, top: y, height: h} getOffset: (pos) -> $inputor = @$inputor if oDocument.selection offset = this.getIEOffset(pos) offset.top += $(oWindow).scrollTop() + $inputor.scrollTop() offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft() offset else offset = $inputor.offset() position = this.getPosition(pos) offset = left: offset.left + position.left - $inputor.scrollLeft() top: offset.top + position.top - $inputor.scrollTop() height: position.height getPosition: (pos)-> $inputor = @$inputor format = (value) -> value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g,"
") if /firefox/i.test navigator.userAgent value = value.replace(/\s/g, ' ') value pos = this.getPos() if pos is undefined start_range = $inputor.val().slice(0, pos) end_range = $inputor.val().slice(pos) html = ""+format(start_range)+"" html += "|" html += ""+format(end_range)+"" mirror = new Mirror($inputor) at_rect = mirror.create(html).rect() getIEPosition: (pos) -> offset = this.getIEOffset pos inputorOffset = @$inputor.offset() x = offset.left - inputorOffset.left y = offset.top - inputorOffset.top h = offset.height {left: x, top: y, height: h} # @example # mirror = new Mirror($("textarea#inputor")) # html = "

We will get the rect of @icho

" # mirror.create(html).rect() class Mirror css_attr: [ "borderBottomWidth", "borderLeftWidth", "borderRightWidth", "borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle", "borderTopWidth", "boxSizing", "fontFamily", "fontSize", "fontWeight", "height", "letterSpacing", "lineHeight", "marginBottom", "marginLeft", "marginRight", "marginTop", "outlineWidth", "overflow", "overflowX", "overflowY", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textAlign", "textOverflow", "textTransform", "whiteSpace", "wordBreak", "wordWrap", ] constructor: (@$inputor) -> mirrorCss: -> css = position: 'absolute' left: -9999 top: 0 zIndex: -20000 if @$inputor.prop( 'tagName' ) == 'TEXTAREA' @css_attr.push( 'width' ) $.each @css_attr, (i,p) => css[p] = @$inputor.css p css create: (html) -> @$mirror = $('
') @$mirror.css this.mirrorCss() @$mirror.html(html) @$inputor.after(@$mirror) this # 获得标记的位置 # # @return [Object] 标记的坐标 # {left: 0, top: 0, bottom: 0} rect: -> $flag = @$mirror.find "#caret" pos = $flag.position() rect = {left: pos.left, top: pos.top, height: $flag.height() } @$mirror.remove() rect Utils = contentEditable: ($inputor)-> !!($inputor[0].contentEditable && $inputor[0].contentEditable == 'true') methods = pos: (pos) -> if pos or pos == 0 this.setPos pos else this.getPos() position: (pos) -> if oDocument.selection then this.getIEPosition pos else this.getPosition pos offset: (pos) -> offset = this.getOffset(pos) offset oDocument = null oWindow = null oFrame = null setContextBy = (settings) -> if iframe = settings?.iframe oFrame = iframe oWindow = iframe.contentWindow oDocument = iframe.contentDocument || oWindow.document else oFrame = undefined oWindow = window oDocument = document discoveryIframeOf = ($dom) -> oDocument = $dom[0].ownerDocument oWindow = oDocument.defaultView || oDocument.parentWindow try oFrame = oWindow.frameElement catch error # throws error in cross-domain iframes $.fn.caret = (method, value, settings) -> # http://stackoverflow.com/questions/16010204/get-reference-of-window-object-from-a-dom-element if methods[method] if $.isPlainObject(value) setContextBy value value = undefined else setContextBy settings caret = if Utils.contentEditable(this) then new EditableCaret(this) else new InputCaret(this) methods[method].apply caret, [value] else $.error "Method #{method} does not exist on jQuery.caret" $.fn.caret.EditableCaret = EditableCaret $.fn.caret.InputCaret = InputCaret $.fn.caret.Utils = Utils $.fn.caret.apis = methods