1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 // standard browser cursor definitions 9 // TODO: remove extra constants in next version 10 // TODO: consider adding theme cursors for custom behaviors like drag & drop 11 SC.SYSTEM_CURSOR = SC.DEFAULT_CURSOR = 'default'; 12 SC.AUTO_CURSOR = 'auto'; 13 SC.CROSSHAIR_CURSOR = 'crosshair'; 14 SC.HAND_CURSOR = SC.POINTER_CURSOR = 'pointer'; 15 SC.MOVE_CURSOR = 'move'; 16 SC.E_RESIZE_CURSOR = 'e-resize'; 17 SC.NE_RESIZE_CURSOR = 'ne-resize'; 18 SC.NW_RESIZE_CURSOR = 'nw-resize'; 19 SC.N_RESIZE_CURSOR = 'n-resize'; 20 SC.SE_RESIZE_CURSOR = 'se-resize'; 21 SC.SW_RESIZE_CURSOR = 'sw-resize'; 22 SC.S_RESIZE_CURSOR = 's-resize'; 23 SC.W_RESIZE_CURSOR = 'w-resize'; 24 SC.IBEAM_CURSOR = SC.TEXT_CURSOR = 'text'; 25 SC.WAIT_CURSOR = 'wait'; 26 SC.HELP_CURSOR = 'help'; 27 28 /** 29 @class SC.Cursor 30 31 A Cursor object is used to synchronize the cursor used by multiple views at 32 the same time. For example, thumb views within a split view acquire a cursor 33 instance from the split view and set it as their cursor. The split view is 34 able to update its cursor object to reflect the state of the split view. 35 Because cursor objects are implemented internally with CSS, this is a very 36 efficient way to update the same cursor for a group of view objects. 37 38 Note: This object creates an anonymous CSS class to represent the cursor. 39 The anonymous CSS class is automatically added by SproutCore to views that 40 have the cursor object set as "their" cursor. Thus, all objects attached to 41 the same cursor object will have their cursors updated simultaneously with a 42 single DOM call. 43 44 @extends SC.Object 45 */ 46 SC.Cursor = SC.Object.extend( 47 /** @scope SC.Cursor.prototype */ { 48 49 /** @private */ 50 init: function () { 51 sc_super(); 52 53 // create a unique style rule and add it to the shared cursor style sheet 54 var cursorStyle = this.get('cursorStyle') || SC.DEFAULT_CURSOR, 55 ss = this.constructor.sharedStyleSheet(), 56 guid = SC.guidFor(this); 57 58 if (ss.insertRule) { // WC3 59 ss.insertRule( 60 '.' + guid + ' {cursor: ' + cursorStyle + ';}', 61 ss.cssRules ? ss.cssRules.length : 0 62 ); 63 } else if (ss.addRule) { // IE 64 ss.addRule('.' + guid, 'cursor: ' + cursorStyle); 65 } 66 67 this.cursorStyle = cursorStyle; 68 this.className = guid; // used by cursor clients... 69 return this; 70 }, 71 72 /** 73 This property is the connection between cursors and views. The default 74 SC.View behavior is to add this className to a view's layer if it has 75 its cursor property defined. 76 77 @readOnly 78 @type String the css class name updated by this cursor 79 */ 80 className: null, 81 82 /** 83 @type String the cursor value, can be 'url("path/to/cursor")' 84 */ 85 cursorStyle: SC.DEFAULT_CURSOR, 86 87 /** @private */ 88 cursorStyleDidChange: function () { 89 var cursorStyle, rule, selector, ss, rules, idx, len; 90 cursorStyle = this.get('cursorStyle') || SC.DEFAULT_CURSOR; 91 rule = this._rule; 92 if (rule) { 93 rule.style.cursor = cursorStyle; // fast path 94 return; 95 } 96 97 // slow path, taken only once 98 selector = '.' + this.get('className'); 99 ss = this.constructor.sharedStyleSheet(); 100 rules = (ss.cssRules ? ss.cssRules : ss.rules) || []; 101 102 // find our rule, cache it, and update the cursor style property 103 for (idx = 0, len = rules.length; idx < len; ++idx) { 104 rule = rules[idx]; 105 if (rule.selectorText === selector) { 106 this._rule = rule; // cache for next time 107 rule.style.cursor = cursorStyle; // update the cursor 108 break; 109 } 110 } 111 }.observes('cursorStyle') 112 113 // TODO implement destroy 114 115 }); 116 117 118 /** @private */ 119 SC.Cursor.sharedStyleSheet = function () { 120 var ssEl, 121 head, 122 ss = this._styleSheet; 123 124 if (!ss) { 125 // create the stylesheet object the hard way (works everywhere) 126 ssEl = document.createElement('style'); 127 head = document.getElementsByTagName('head')[0]; 128 if (!head) head = document.documentElement; // fix for Opera 129 head.appendChild(ssEl); 130 131 // Get the actual stylesheet object, not the DOM element. We expect it to 132 // be the last stylesheet in the document, but test to make sure no other 133 // stylesheet has appeared. 134 for (var i = document.styleSheets.length - 1; i >= 0; i--) { 135 ss = document.styleSheets[i]; 136 137 if (ss.ownerNode === ssEl) { 138 // We've found the proper stylesheet. 139 this._styleSheet = ss; 140 break; 141 } 142 } 143 } 144 145 return ss; 146 }; 147