// ========================================================================== // Project: SproutCore - JavaScript Application Framework // Copyright: ©2006-2011 Strobe Inc. and contributors. // Portions ©2008-2011 Apple Inc. All rights reserved. // License: Licensed under MIT license (see license.js) // ==========================================================================

// standard browser cursor definitions // TODO: remove extra constants in next version // TODO: consider adding theme cursors for custom behaviors like drag & drop SC.SYSTEM_CURSOR = SC.DEFAULT_CURSOR = 'default'; SC.AUTO_CURSOR = 'auto'; SC.CROSSHAIR_CURSOR = 'crosshair'; SC.HAND_CURSOR = SC.POINTER_CURSOR = 'pointer'; SC.MOVE_CURSOR = 'move'; SC.E_RESIZE_CURSOR = 'e-resize'; SC.NE_RESIZE_CURSOR = 'ne-resize'; SC.NW_RESIZE_CURSOR = 'nw-resize'; SC.N_RESIZE_CURSOR = 'n-resize'; SC.SE_RESIZE_CURSOR = 'se-resize'; SC.SW_RESIZE_CURSOR = 'sw-resize'; SC.S_RESIZE_CURSOR = 's-resize'; SC.W_RESIZE_CURSOR = 'w-resize'; SC.IBEAM_CURSOR = SC.TEXT_CURSOR = 'text'; SC.WAIT_CURSOR = 'wait'; SC.HELP_CURSOR = 'help';

/**

@class SC.Cursor

A Cursor object is used to synchronize the cursor used by multiple views at
the same time. For example, thumb views within a split view acquire a cursor
instance from the split view and set it as their cursor. The split view is
able to update its cursor object to reflect the state of the split view.
Because cursor objects are implemented internally with CSS, this is a very
efficient way to update the same cursor for a group of view objects.

Note: This object creates an anonymous CSS class to represent the cursor.
The anonymous CSS class is automatically added by SproutCore to views that
have the cursor object set as "their" cursor. Thus, all objects attached to
the same cursor object will have their cursors updated simultaneously with a
single DOM call.

@extends SC.Object

*/ SC.Cursor = SC.Object.extend( /** @scope SC.Cursor.prototype */ {

/** @private */
init: function () {
  sc_super();

  // create a unique style rule and add it to the shared cursor style sheet
  var cursorStyle = this.get('cursorStyle') || SC.DEFAULT_CURSOR,
    ss = this.constructor.sharedStyleSheet(),
    guid = SC.guidFor(this);

  if (ss.insertRule) { // WC3
    ss.insertRule(
      '.' + guid + ' {cursor: ' + cursorStyle + ';}',
      ss.cssRules ? ss.cssRules.length : 0
    );
  } else if (ss.addRule) { // IE
    ss.addRule('.' + guid, 'cursor: ' + cursorStyle);
  }

  this.cursorStyle = cursorStyle;
  this.className = guid; // used by cursor clients...
  return this;
},

/**
  This property is the connection between cursors and views. The default
  SC.View behavior is to add this className to a view's layer if it has
  its cursor property defined.

  @readOnly
  @type String the css class name updated by this cursor
*/
className: null,

/**
  @type String the cursor value, can be 'url("path/to/cursor")'
*/
cursorStyle: SC.DEFAULT_CURSOR,

/** @private */
cursorStyleDidChange: function () {
  var cursorStyle, rule, selector, ss, rules, idx, len;
  cursorStyle = this.get('cursorStyle') || SC.DEFAULT_CURSOR;
  rule = this._rule;
  if (rule) {
    rule.style.cursor = cursorStyle; // fast path
    return;
  }

  // slow path, taken only once
  selector = '.' + this.get('className');
  ss = this.constructor.sharedStyleSheet();
  rules = (ss.cssRules ? ss.cssRules : ss.rules) || [];

  // find our rule, cache it, and update the cursor style property
  for (idx = 0, len = rules.length; idx < len; ++idx) {
    rule = rules[idx];
    if (rule.selectorText === selector) {
      this._rule = rule; // cache for next time
      rule.style.cursor = cursorStyle; // update the cursor
      break;
    }
  }
}.observes('cursorStyle')

// TODO implement destroy

});

/** @private */ SC.Cursor.sharedStyleSheet = function () {

var ssEl,
  head,
  ss = this._styleSheet;

if (!ss) {
  // create the stylesheet object the hard way (works everywhere)
  ssEl = document.createElement('style');
  head = document.getElementsByTagName('head')[0];
  if (!head) head = document.documentElement; // fix for Opera
  head.appendChild(ssEl);

  // Get the actual stylesheet object, not the DOM element.  We expect it to
  // be the last stylesheet in the document, but test to make sure no other
  // stylesheet has appeared.
  for (var i = document.styleSheets.length - 1; i >= 0; i--) {
    ss = document.styleSheets[i];

    if (ss.ownerNode === ssEl) {
      // We've found the proper stylesheet.
      this._styleSheet = ss;
      break;
    }
  }
}

return ss;

};