// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Copyright 2006 Google Inc. // All Rights Reserved. /** * @fileoverview Class to encapsulate an editable field. Always uses an * iframe to contain the editable area, never inherits the style of the * surrounding page, and is always a fixed height. * * @see ../demos/editor/editor.html * @see ../demos/editor/field_basic.html */ goog.provide('goog.editor.Field'); goog.provide('goog.editor.Field.EventType'); goog.require('goog.array'); goog.require('goog.async.Delay'); goog.require('goog.debug.Logger'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.dom.classes'); goog.require('goog.editor.BrowserFeature'); goog.require('goog.editor.Command'); goog.require('goog.editor.Plugin'); goog.require('goog.editor.icontent'); goog.require('goog.editor.icontent.FieldFormatInfo'); goog.require('goog.editor.icontent.FieldStyleInfo'); goog.require('goog.editor.node'); goog.require('goog.editor.range'); goog.require('goog.events'); goog.require('goog.events.BrowserEvent'); goog.require('goog.events.EventHandler'); goog.require('goog.events.EventType'); goog.require('goog.events.KeyCodes'); goog.require('goog.functions'); goog.require('goog.string'); goog.require('goog.string.Unicode'); goog.require('goog.style'); goog.require('goog.userAgent'); /** * This class encapsulates an editable field. * * event: load Fires when the field is loaded * event: unload Fires when the field is unloaded (made not editable) * * event: beforechange Fires before the content of the field might change * * event: delayedchange Fires a short time after field has changed. If multiple * change events happen really close to each other only * the last one will trigger the delayedchange event. * * event: beforefocus Fires before the field becomes active * event: focus Fires when the field becomes active. Fires after the blur event * event: blur Fires when the field becomes inactive * * TODO: figure out if blur or beforefocus fires first in IE and make FF match * * @param {string} id An identifer for the field. This is used to find the * field and the element associated with this field. * @param {Document=} opt_doc The document that the element with the given * id can be found in. If not provided, the default document is used. * @constructor * @extends {goog.events.EventTarget} */ goog.editor.Field = function(id, opt_doc) { goog.events.EventTarget.call(this); /** * The id for this editable field, which must match the id of the element * associated with this field. * @type {string} */ this.id = id; /** * The hash code for this field. Should be equal to the id. * @type {string} * @private */ this.hashCode_ = id; /** * Dom helper for the editable node. * @type {goog.dom.DomHelper} * @protected */ this.editableDomHelper = null; /** * Map of class id to registered plugin. * @type {Object} * @private */ this.plugins_ = {}; /** * Plugins registered on this field, indexed by the goog.editor.Plugin.Op * that they support. * @type {Object.<Array>} * @private */ this.indexedPlugins_ = {}; for (var op in goog.editor.Plugin.OPCODE) { this.indexedPlugins_[op] = []; } /** * Additional styles to install for the editable field. * @type {string} * @protected */ this.cssStyles = ''; // Work around bug https://bugs.webkit.org/show_bug.cgi?id=19086 in affected // versions of Webkit by specifying 1px right margin on all immediate children // of the editable field. This works in most (but not all) cases. // Note. The fix uses a CSS rule which may be quite expensive, especially // in BLENDED mode. Currently this is the only known wokraround. // TODO: The bug was fixed in community Webkit but not included in // Safari yet. Verify that the next Safari release after 525.18 is // unaffected. if (goog.userAgent.WEBKIT && goog.userAgent.isVersion('525.13') && goog.string.compareVersions(goog.userAgent.VERSION, '525.18') <= 0) { this.workaroundClassName_ = goog.getCssName('tr-webkit-workaround'); this.cssStyles = '.' + this.workaroundClassName_ + '>*{padding-right:1}'; } // The field will not listen to change events until it has finished loading this.stoppedEvents_ = {}; this.stopEvent(goog.editor.Field.EventType.CHANGE); this.stopEvent(goog.editor.Field.EventType.DELAYEDCHANGE); this.isModified_ = false; this.isEverModified_ = false; this.delayedChangeTimer_ = new goog.async.Delay(this.dispatchDelayedChange_, goog.editor.Field.DELAYED_CHANGE_FREQUENCY, this); this.debouncedEvents_ = {}; for (var key in goog.editor.Field.EventType) { this.debouncedEvents_[goog.editor.Field.EventType[key]] = 0; } if (goog.editor.BrowserFeature.USE_MUTATION_EVENTS) { this.changeTimerGecko_ = new goog.async.Delay(this.handleChange, goog.editor.Field.CHANGE_FREQUENCY, this); } /** * @type {goog.events.EventHandler} * @protected */ this.eventRegister = new goog.events.EventHandler(this); // Wrappers around this field, to be disposed when the field is disposed. this.wrappers_ = []; this.loadState_ = goog.editor.Field.LoadState_.UNEDITABLE; var doc = opt_doc || document; this.originalDomHelper = goog.dom.getDomHelper(doc); this.originalElement = this.originalDomHelper.getElement(this.id); // Default to the same window as the field is in. this.appWindow_ = this.originalDomHelper.getWindow(); }; goog.inherits(goog.editor.Field, goog.events.EventTarget); /** * The editable dom node. * @type {Element} * TODO: Make this private! */ goog.editor.Field.prototype.field = null; /** * The original node that is being made editable, or null if it has * not yet been found. * @type {Element} * @protected */ goog.editor.Field.prototype.originalElement = null; /** * Logging object. * @type {goog.debug.Logger} * @protected */ goog.editor.Field.prototype.logger = goog.debug.Logger.getLogger('goog.editor.Field'); /** * Event types that can be stopped/started. * @enum {string} */ goog.editor.Field.EventType = { /** * Dispatched when the command state of the selection may have changed. This * event should be listened to for updating toolbar state. */ COMMAND_VALUE_CHANGE: 'cvc', /** * Dispatched when the field is loaded and ready to use. */ LOAD: 'load', /** * Dispatched when the field is fully unloaded and uneditable. */ UNLOAD: 'unload', /** * Dispatched before the field contents are changed. */ BEFORECHANGE: 'beforechange', /** * Dispatched when the field contents change, in FF only. * Used for internal resizing, please do not use. */ CHANGE: 'change', /** * Dispatched on a slight delay after changes are made. * Use for autosave, or other times your app needs to know * that the field contents changed. */ DELAYEDCHANGE: 'delayedchange', /** * Dispatched before focus in moved into the field. */ BEFOREFOCUS: 'beforefocus', /** * Dispatched when focus is moved into the field. */ FOCUS: 'focus', /** * Dispatched when the field is blurred. */ BLUR: 'blur', /** * Dispach before tab is handled by the field. This is a legacy way * of controlling tab behavior. Use trog.plugins.AbstractTabHandler now. */ BEFORETAB: 'beforetab', /** * Dispatched when the selection changes. * Use handleSelectionChange from plugin API instead of listening * directly to this event. */ SELECTIONCHANGE: 'selectionchange' }; /** * The load state of the field. * @enum {number} * @private */ goog.editor.Field.LoadState_ = { UNEDITABLE: 0, LOADING: 1, EDITABLE: 2 }; /** * The amount of time that a debounce blocks an event. * TODO: As of 9/30/07, this is only used for blocking * a keyup event after a keydown. We might need to tweak this for other * types of events. Maybe have a per-event debounce time? * @type {number} * @private */ goog.editor.Field.DEBOUNCE_TIME_MS_ = 500; /** * There is at most one "active" field at a time. By "active" field, we mean * a field that has focus and is being used. * @type {?string} * @private */ goog.editor.Field.activeFieldId_ = null; /** * Whether this field is in "modal interaction" mode. This usually * means that it's being edited by a dialog. * @type {boolean} * @private */ goog.editor.Field.prototype.inModalMode_ = false; /** * The window where dialogs and bubbles should be rendered. * @type {Window} * @private */ goog.editor.Field.prototype.appWindow_; /** * The dom helper for the node to be made editable. * @type {goog.dom.DomHelper} * @protected */ goog.editor.Field.prototype.originalDomHelper; /** * Sets the active field id. * @param {?string} fieldId The active field id. */ goog.editor.Field.setActiveFieldId = function(fieldId) { goog.editor.Field.activeFieldId_ = fieldId; }; /** * @return {?string} The id of the active field. */ goog.editor.Field.getActiveFieldId = function() { return goog.editor.Field.activeFieldId_; }; /** * @return {boolean} Whether we're in modal interaction mode. When this * returns true, another plugin is interacting with the field contents * in a synchronous way, and expects you not to make changes to * the field's DOM structure or selection. */ goog.editor.Field.prototype.inModalMode = function() { return this.inModalMode_; }; /** * @param {boolean} inModalMode Sets whether we're in modal interaction mode. */ goog.editor.Field.prototype.setModalMode = function(inModalMode) { this.inModalMode_ = inModalMode; }; /** * Returns a string usable as a hash code for this field. For field's * that were created with an id, the hash code is guaranteed to be the id. * TODO: I think we can get rid of this. Seems only used from editor. * @return {string} The hash code for this editable field. */ goog.editor.Field.prototype.getHashCode = function() { return this.hashCode_; }; /** * Returns the editable DOM element or null if this field * is not editable. *
On IE or Safari this is the element with contentEditable=true * (in whitebox mode, the iFrame body). *
On Gecko this is the iFrame body * TODO: How do we word this for subclass version? * @return {Element} The editable DOM element, defined as above. */ goog.editor.Field.prototype.getElement = function() { return this.field; }; /** * Returns original DOM element that is being made editable by Trogedit or * null if that element has not yet been found in the appropriate document. * @return {Element} The original element. */ goog.editor.Field.prototype.getOriginalElement = function() { return this.originalElement; }; /** * Registers a keyboard event listener on the field. This is necessary for * Gecko since the fields are contained in an iFrame and there is no way to * auto-propagate key events up to the main window. * @param {string|Array.<string>} type Event type to listen for or array of * event types, for example goog.events.EventType.KEYDOWN. * @param {Function} listener Function to be used as the listener. * @param {boolean=} opt_capture Whether to use capture phase (optional, * defaults to false). * @param {Object=} opt_handler Object in whose scope to call the listener. */ goog.editor.Field.prototype.addListener = function(type, listener, opt_capture, opt_handler) { var elem = this.getElement(); // Opera won't fire events on  in whitebox mode because we make //  contentEditable to work around some visual issues. // So, if the parent node is contentEditable, listen to events on it instead. if (!goog.editor.BrowserFeature.FOCUSES_EDITABLE_BODY_ON_HTML_CLICK && elem.parentNode.contentEditable) { elem = elem.parentNode; } // On Gecko, keyboard events only reliably fire on the document element. if (elem && goog.editor.BrowserFeature.USE_DOCUMENT_FOR_KEY_EVENTS) { elem = elem.ownerDocument; } this.eventRegister.listen(elem, type, listener, opt_capture, opt_handler); }; /** * Returns the registered plugin with the given classId. * @param {string} classId classId of the plugin. * @return {goog.editor.Plugin} Registered plugin with the given classId. */ goog.editor.Field.prototype.getPluginByClassId = function(classId) { return this.plugins_[classId]; }; /** * Registers the plugin with the editable field. * @param {goog.editor.Plugin} plugin The plugin to register. */ goog.editor.Field.prototype.registerPlugin = function(plugin) { var classId = plugin.getTrogClassId(); if (this.plugins_[classId]) { this.logger.severe('Cannot register the same class of plugin twice.'); } this.plugins_[classId] = plugin; // Only key events and execute should have these has* functions with a custom // handler array since they need to be very careful about performance. // The rest of the plugin hooks should be event-based. for (var op in goog.editor.Plugin.OPCODE) { var opcode = goog.editor.Plugin.OPCODE[op]; if (plugin[opcode]) { this.indexedPlugins_[op].push(plugin); } } plugin.registerFieldObject(this); // By default we enable all plugins for fields that are currently loaded. if (this.isLoaded()) { plugin.enable(this); } }; /** * Unregisters the plugin with this field. * @param {goog.editor.Plugin} plugin The plugin to unregister. */ goog.editor.Field.prototype.unregisterPlugin = function(plugin) { var classId = plugin.getTrogClassId(); if (!this.plugins_[classId]) { this.logger.severe('Cannot unregister a plugin that isn\'t registered.'); } delete this.plugins_[classId]; for (var op in goog.editor.Plugin.OPCODE) { var opcode = goog.editor.Plugin.OPCODE[op]; if (plugin[opcode]) { goog.array.remove(this.indexedPlugins_[op], plugin); } } plugin.unregisterFieldObject(this); }; /** * Sets the value that will replace the style attribute of this field's * element when the field is made non-editable. This method is called with the * current value of the style attribute when the field is made editable. * @param {string} cssText The value of the style attribute. */ goog.editor.Field.prototype.setInitialStyle = function(cssText) { this.cssText = cssText; }; /** * Reset the properties on the original field element to how it was before * it was made editable. */ goog.editor.Field.prototype.resetOriginalElemProperties = function() { var field = this.getOriginalElement(); field.removeAttribute('contentEditable'); field.removeAttribute('g_editable'); if (!this.id) { field.removeAttribute('id'); } else { field.id = this.id; } field.className = this.savedClassName_ || ''; var cssText = this.cssText; if (!cssText) { field.removeAttribute('style'); } else { goog.dom.setProperties(field, {'style' : cssText}); } if (goog.isString(this.originalFieldLineHeight_)) { goog.style.setStyle(field, 'lineHeight', this.originalFieldLineHeight_); this.originalFieldLineHeight_ = null; } }; /** * Checks the modified state of the field. * Note: Changes that take place while the goog.editor.Field.EventType.CHANGE * event is stopped do not effect the modified state. * @param {boolean=} opt_useIsEverModified Set to true to check if the field * has ever been modified since it was created, otherwise checks if the field * has been modified since the last goog.editor.Field.EventType.DELAYEDCHANGE * event was dispatched. * @return {boolean} Whether the field has been modified. */ goog.editor.Field.prototype.isModified = function(opt_useIsEverModified) { return opt_useIsEverModified ? this.isEverModified_ : this.isModified_; }; /** * Number of milliseconds after a change when the change event should be fired. * @type {number} */ goog.editor.Field.CHANGE_FREQUENCY = 15; /** * Number of milliseconds between delayed change events. * @type {number} */ goog.editor.Field.DELAYED_CHANGE_FREQUENCY = 250; /** * @return {boolean} Whether the field is implemented as an iframe. */ goog.editor.Field.prototype.usesIframe = goog.functions.TRUE; /** * @return {boolean} Whether the field should be rendered with a fixed * height, or should expand to fit its contents. */ goog.editor.Field.prototype.isFixedHeight = goog.functions.TRUE; /** * Map of keyCodes (not charCodes) that cause changes in the field contents. * @type {Object} * @private */ goog.editor.Field.KEYS_CAUSING_CHANGES_ = { 46: true, // DEL 8: true // BACKSPACE }; if (!goog.userAgent.IE) { // Only IE doesn't change the field by default upon tab. // TODO: This really isn't right now that we have tab plugins. goog.editor.Field.KEYS_CAUSING_CHANGES_[9] = true; // TAB } /** * Map of keyCodes (not charCodes) that when used in conjunction with the * Ctrl key cause changes in the field contents. These are the keys that are * not handled by basic formatting trogedit plugins. * @type {Object} * @private */ goog.editor.Field.CTRL_KEYS_CAUSING_CHANGES_ = { 86: true, // V 88: true // X }; if (goog.userAgent.IE) { // In IE input from IME (Input Method Editor) does not generate keypress // event so we have to rely on the keydown event. This way we have // false positives while the user is using keyboard to select the // character to input, but it is still better than the false negatives // that ignores user's final input at all. goog.editor.Field.KEYS_CAUSING_CHANGES_[229] = true; // from IME; } /** * Returns true if the keypress generates a change in contents. * @param {goog.events.BrowserEvent} e The event. * @param {boolean} testAllKeys True to test for all types of generating keys. * False to test for only the keys found in * goog.editor.Field.KEYS_CAUSING_CHANGES_. * @return {boolean} Whether the keypress generates a change in contents. * @private */ goog.editor.Field.isGeneratingKey_ = function(e, testAllKeys) { if (goog.editor.Field.isSpecialGeneratingKey_(e)) { return true; } return !!(testAllKeys && !(e.ctrlKey || e.metaKey) && (!goog.userAgent.GECKO || e.charCode)); }; /** * Returns true if the keypress generates a change in the contents. * due to a special key listed in goog.editor.Field.KEYS_CAUSING_CHANGES_ * @param {goog.events.BrowserEvent} e The event. * @return {boolean} Whether the keypress generated a change in the contents. * @private */ goog.editor.Field.isSpecialGeneratingKey_ = function(e) { var testCtrlKeys = (e.ctrlKey || e.metaKey) && e.keyCode in goog.editor.Field.CTRL_KEYS_CAUSING_CHANGES_; var testRegularKeys = !(e.ctrlKey || e.metaKey) && e.keyCode in goog.editor.Field.KEYS_CAUSING_CHANGES_; return testCtrlKeys || testRegularKeys; }; /** * Sets the application window. * @param {Window} appWindow The window where dialogs and bubbles should be * rendered. */ goog.editor.Field.prototype.setAppWindow = function(appWindow) { this.appWindow_ = appWindow; }; /** * Returns the "application" window, where dialogs and bubbles * should be rendered. * @return {Window} The window. */ goog.editor.Field.prototype.getAppWindow = function() { return this.appWindow_; }; /** * Sets the zIndex that the field should be based off of. * TODO: Get rid of this completely. Here for Sites. * Should this be set directly on UI plugins? * * @param {number} zindex The base zIndex of the editor. */ goog.editor.Field.prototype.setBaseZindex = function(zindex) { this.baseZindex_ = zindex; }; /** * Returns the zindex of the base level of the field. * * @return {number} The base zindex of the editor. */ goog.editor.Field.prototype.getBaseZindex = function() { return this.baseZindex_ || 0; }; /** * Sets up the field object and window util of this field, and enables this * editable field with all registered plugins. * This is essential to the initialization of the field. * It must be called when the field becomes fully loaded and editable. * @param {Element} field The field property. * @protected */ goog.editor.Field.prototype.setupFieldObject = function(field) { this.loadState_ = goog.editor.Field.LoadState_.EDITABLE; this.field = field; this.editableDomHelper = goog.dom.getDomHelper(field); this.isModified_ = false; this.isEverModified_ = false; field.setAttribute('g_editable', 'true'); }; /** * Help make the field not editable by setting internal data structures to null, * and disabling this field with all registered plugins. * @private */ goog.editor.Field.prototype.tearDownFieldObject_ = function() { for (var classId in this.plugins_) { var plugin = this.plugins_[classId]; if (!plugin.activeOnUneditableFields()) { plugin.disable(this); } } this.loadState_ = goog.editor.Field.LoadState_.UNEDITABLE; this.field = null; this.editableDomHelper = null; }; /** * Initialize listeners on the field. * @private */ goog.editor.Field.prototype.setupChangeListeners_ = function() { if (goog.userAgent.OPERA && this.usesIframe()) { // We can't use addListener here because we need to listen on // the document, not the editable element. this.eventRegister.listen(this.getEditableDomHelper().getDocument(), goog.events.EventType.FOCUS, this.dispatchFocusAndBeforeFocus_); this.eventRegister.listen(this.getEditableDomHelper().getDocument(), goog.events.EventType.BLUR, this.dispatchBlur); } else { if (goog.editor.BrowserFeature.SUPPORTS_FOCUSIN) { this.addListener(goog.events.EventType.FOCUS, this.dispatchFocus_); this.addListener(goog.events.EventType.FOCUSIN, this.dispatchBeforeFocus_); } else { this.addListener(goog.events.EventType.FOCUS, this.dispatchFocusAndBeforeFocus_); } this.addListener(goog.events.EventType.BLUR, this.dispatchBlur, goog.editor.BrowserFeature.USE_MUTATION_EVENTS); } if (goog.editor.BrowserFeature.USE_MUTATION_EVENTS) { // Ways to detect changes in Mozilla: // // keypress - check event.charCode (only typable characters has a // charCode), but also keyboard commands lile Ctrl+C will // return a charCode. // dragdrop - fires when the user drops something. This does not necessary // lead to a change but we cannot detect if it will or not // // Known Issues: We cannot detect cut and paste using menus // We cannot detect when someone moves something out of the // field using drag and drop. // this.setupMutationEventHandlersGecko(); } else { // Ways to detect that a change is about to happen in other browsers. // (IE and Safari have these events. Opera appears to work, but we haven't // researched it.) // // onbeforepaste // onbeforecut // ondrop - happens when the user drops something on the editable text // field the value at this time does not contain the dropped text // ondragleave - when the user drags something from the current document. // This might not cause a change if the action was copy // instead of move // onkeypress - IE only fires keypress events if the key will generate // output. It will not trigger for delete and backspace // onkeydown - For delete and backspace // // known issues: IE triggers beforepaste just by opening the edit menu // delete at the end should not cause beforechange // backspace at the beginning should not cause beforechange // see above in ondragleave // TODO: Why don't we dispatchBeforeChange from the // handleDrop event for all browsers? this.addListener(['beforecut', 'beforepaste', 'drop', 'dragend'], this.dispatchBeforeChange); this.addListener(['cut', 'paste'], this.dispatchChange); this.addListener('drop', this.handleDrop_); } // TODO: Figure out why we use dragend vs dragdrop and // document this better. var dropEventName = goog.userAgent.WEBKIT ? 'dragend' : 'dragdrop'; this.addListener(dropEventName, this.handleDrop_); this.addListener(goog.events.EventType.KEYDOWN, this.handleKeyDown_); this.addListener(goog.events.EventType.KEYPRESS, this.handleKeyPress_); this.addListener(goog.events.EventType.KEYUP, this.handleKeyUp_); var selectionChange = goog.bind(this.dispatchSelectionChangeEvent, this); this.selectionChangeTimer_ = new goog.async.Delay(selectionChange, goog.editor.Field.SELECTION_CHANGE_FREQUENCY_); if (goog.editor.BrowserFeature.FOLLOWS_EDITABLE_LINKS) { this.addListener( goog.events.EventType.CLICK, goog.editor.Field.cancelLinkClick_); } this.addListener(goog.events.EventType.MOUSEDOWN, this.handleMouseDown_); this.addListener(goog.events.EventType.MOUSEUP, this.handleMouseUp_); }; /** * Frequency to check for selection changes. * @type {number} * @private */ goog.editor.Field.SELECTION_CHANGE_FREQUENCY_ = 250; /** * Stops all listeners and timers. * @private */ goog.editor.Field.prototype.clearListeners_ = function() { if (this.eventRegister) { this.eventRegister.removeAll(); } if (this.changeTimerGecko_) { this.changeTimerGecko_.stop(); } this.delayedChangeTimer_.stop(); }; /** * Removes all listeners and destroys the eventhandler object. * @override */ goog.editor.Field.prototype.disposeInternal = function() { if (this.isLoading() || this.isLoaded()) { this.logger.warning('Disposing a field that is in use.'); } if (this.getOriginalElement()) { this.execCommand(goog.editor.Command.CLEAR_LOREM); } this.tearDownFieldObject_(); this.clearListeners_(); this.originalDomHelper = null; if (this.eventRegister) { this.eventRegister.dispose(); this.eventRegister = null; } this.removeAllWrappers(); if (goog.editor.Field.getActiveFieldId() == this.id) { goog.editor.Field.setActiveFieldId(null); } for (var classId in this.plugins_) { var plugin = this.plugins_[classId]; if (plugin.isAutoDispose()) { plugin.dispose(); } } delete(this.plugins_); goog.editor.Field.superClass_.disposeInternal.call(this); }; /** * Attach an wrapper to this field, to be thrown out when the field * is disposed. * @param {goog.Disposable} wrapper The wrapper to attach. */ goog.editor.Field.prototype.attachWrapper = function(wrapper) { this.wrappers_.push(wrapper); }; /** * Removes all wrappers and destroys them. */ goog.editor.Field.prototype.removeAllWrappers = function() { var wrapper; while (wrapper = this.wrappers_.pop()) { wrapper.dispose(); } }; /** * List of mutation events in Gecko browsers. * @type {Array.<string>} * @protected */ goog.editor.Field.MUTATION_EVENTS_GECKO = [ 'DOMNodeInserted', 'DOMNodeRemoved', 'DOMNodeRemovedFromDocument', 'DOMNodeInsertedIntoDocument', 'DOMCharacterDataModified' ]; /** * Mutation events tell us when something has changed for mozilla. * @protected */ goog.editor.Field.prototype.setupMutationEventHandlersGecko = function() { if (goog.editor.BrowserFeature.HAS_DOM_SUBTREE_MODIFIED_EVENT) { this.eventRegister.listen(this.getElement(), 'DOMSubtreeModified', this.handleMutationEventGecko_); } else { var doc = this.getEditableDomHelper().getDocument(); this.eventRegister.listen(doc, goog.editor.Field.MUTATION_EVENTS_GECKO, this.handleMutationEventGecko_, true); // DOMAttrModified fires for a lot of events we want to ignore. This goes // through a different handler so that we can ignore many of these. this.eventRegister.listen(doc, 'DOMAttrModified', goog.bind(this.handleDomAttrChange, this, this.handleMutationEventGecko_), true); } }; /** * Handle before change key events and fire the beforetab event if appropriate. * This needs to happen on keydown in IE and keypress in FF. * @param {goog.events.BrowserEvent} e The browser event. * @return {boolean} Whether to still perform the default key action. Only set * to true if the actual event has already been canceled. * @private */ goog.editor.Field.prototype.handleBeforeChangeKeyEvent_ = function(e) { // There are two reasons to block a key: var block = // #1: to intercept a tab // TODO: possibly don't allow clients to intercept tabs outside of LIs and // maybe tables as well? (e.keyCode == goog.events.KeyCodes.TAB && !this.dispatchBeforeTab_(e)) || // #2: to block a Firefox-specific bug where Macs try to navigate // back a page when you hit command+left arrow or comamnd-right arrow. // See https://bugzilla.mozilla.org/show_bug.cgi?id=341886 // TODO: Get Firefox to fix this. (goog.userAgent.GECKO && e.metaKey && (e.keyCode == goog.events.KeyCodes.LEFT || e.keyCode == goog.events.KeyCodes.RIGHT)); if (block) { e.preventDefault(); return false; } else { // In Gecko we have both keyCode and charCode. charCode is for human // readable characters like a, b and c. However pressing ctrl+c and so on // also causes charCode to be set. // TODO: Del at end of field or backspace at beginning should be // ignored. this.gotGeneratingKey_ = e.charCode || goog.editor.Field.isGeneratingKey_(e, goog.userAgent.GECKO); if (this.gotGeneratingKey_) { this.dispatchBeforeChange(); // TODO: Should we return the value of the above? } } return true; }; /** * Keycodes that result in a selectionchange event (e.g. the cursor moving). * @enum {number} * @private */ goog.editor.Field.SELECTION_CHANGE_KEYCODES_ = { 8: 1, // backspace 9: 1, // tab 13: 1, // enter 33: 1, // page up 34: 1, // page down 35: 1, // end 36: 1, // home 37: 1, // left 38: 1, // up 39: 1, // right 40: 1, // down 46: 1 // delete }; /** * Map of keyCodes (not charCodes) that when used in conjunction with the * Ctrl key cause selection changes in the field contents. These are the keys * that are not handled by the basic formatting trogedit plugins. Note that * combinations like Ctrl-left etc are already handled in * SELECTION_CHANGE_KEYCODES_ * @type {Object} * @private */ goog.editor.Field.CTRL_KEYS_CAUSING_SELECTION_CHANGES_ = { 65: true, // A 86: true, // V 88: true // X }; /** * Map of keyCodes (not charCodes) that might need to be handled as a keyboard * shortcut (even when ctrl/meta key is not pressed) by some plugin. Currently * it is a small list. If it grows too big we can optimize it by using ranges * or extending it from SELECTION_CHANGE_KEYCODES_ * @type {Object} * @private */ goog.editor.Field.POTENTIAL_SHORTCUT_KEYCODES_ = { 8: 1, // backspace 9: 1, // tab 13: 1, // enter 27: 1, // esc 33: 1, // page up 34: 1, // page down 37: 1, // left 38: 1, // up 39: 1, // right 40: 1 // down }; /** * Calls all the plugins of the given operation, in sequence, with the * given arguments. This is short-circuiting: once one plugin cancels * the event, no more plugins will be invoked. * @param {goog.editor.Plugin.Op} op A plugin op. * @param {...*} var_args The arguments to the plugin. * @return {boolean} True if one of the plugins cancel the event, false * otherwise. * @private */ goog.editor.Field.prototype.invokeShortCircuitingOp_ = function(op, var_args) { var plugins = this.indexedPlugins_[op]; var argList = goog.array.slice(arguments, 1); for (var i = 0; i < plugins.length; ++i) { // If the plugin returns true, that means it handled the event and // we shouldn't propagate to the other plugins. var plugin = plugins[i]; if ((plugin.isEnabled(this) || goog.editor.Plugin.IRREPRESSIBLE_OPS[op]) && plugin[goog.editor.Plugin.OPCODE[op]].apply(plugin, argList)) { // Only one plugin is allowed to handle the event. If for some reason // a plugin wants to handle it and still allow other plugins to handle // it, it shouldn't return true. return true; } } return false; }; /** * Invoke this operation on all plugins with the given arguments. * @param {goog.editor.Plugin.Op} op A plugin op. * @param {...*} var_args The arguments to the plugin. * @private */ goog.editor.Field.prototype.invokeOp_ = function(op, var_args) { var plugins = this.indexedPlugins_[op]; var argList = goog.array.slice(arguments, 1); for (var i = 0; i < plugins.length; ++i) { var plugin = plugins[i]; if (plugin.isEnabled(this) || goog.editor.Plugin.IRREPRESSIBLE_OPS[op]) { plugin[goog.editor.Plugin.OPCODE[op]].apply(plugin, argList); } } }; /** * Reduce this argument over all plugins. The result of each plugin invocation * will be passed to the next plugin invocation. See goog.array.reduce. * @param {goog.editor.Plugin.Op} op A plugin op. * @param {string} arg The argument to reduce. For now, we assume it's a * string, but we should widen this later if there are reducing * plugins that don't operate on strings. * @param {...*} var_args Any extra arguments to pass to the plugin. These args * will not be reduced. * @return {string} The reduced argument. * @private */ goog.editor.Field.prototype.reduceOp_ = function(op, arg, var_args) { var plugins = this.indexedPlugins_[op]; var argList = goog.array.slice(arguments, 1); for (var i = 0; i < plugins.length; ++i) { var plugin = plugins[i]; if (plugin.isEnabled(this) || goog.editor.Plugin.IRREPRESSIBLE_OPS[op]) { argList[0] = plugin[goog.editor.Plugin.OPCODE[op]].apply( plugin, argList); } } return argList[0]; }; /** * Prepare the given contents, then inject them into the editable field. * @param {?string} contents The contents to prepare. * @param {Element} field The field element. * @protected */ goog.editor.Field.prototype.injectContents = function(contents, field) { var styles = {}; var newHtml = this.getInjectableContents(contents, styles); goog.style.setStyle(field, styles); field.innerHTML = newHtml; }; /** * Returns prepared contents that can be injected into the editable field. * @param {?string} contents The contents to prepare. * @param {Object} styles A map that will be populated with styles that should * be applied to the field element together with the contents. * @return {string} The prepared contents. */ goog.editor.Field.prototype.getInjectableContents = function(contents, styles) { return this.reduceOp_( goog.editor.Plugin.Op.PREPARE_CONTENTS_HTML, contents || '', styles); }; /** * Handles keydown on the field. * @param {goog.events.BrowserEvent} e The browser event. * @private */ goog.editor.Field.prototype.handleKeyDown_ = function(e) { if (!goog.editor.BrowserFeature.USE_MUTATION_EVENTS) { if (!this.handleBeforeChangeKeyEvent_(e)) { return; } } if (!this.invokeShortCircuitingOp_(goog.editor.Plugin.Op.KEYDOWN, e) && goog.editor.BrowserFeature.USES_KEYDOWN) { this.handleKeyboardShortcut_(e); } }; /** * Handles keypress on the field. * @param {goog.events.BrowserEvent} e The browser event. * @private */ goog.editor.Field.prototype.handleKeyPress_ = function(e) { if (goog.editor.BrowserFeature.USE_MUTATION_EVENTS) { if (!this.handleBeforeChangeKeyEvent_(e)) { return; } } else { // In IE only keys that generate output trigger keypress // In Mozilla charCode is set for keys generating content. this.gotGeneratingKey_ = true; this.dispatchBeforeChange(); } if (!this.invokeShortCircuitingOp_(goog.editor.Plugin.Op.KEYPRESS, e) && !goog.editor.BrowserFeature.USES_KEYDOWN) { this.handleKeyboardShortcut_(e); } }; /** * Handles keyup on the field. * @param {goog.events.BrowserEvent} e The browser event. * @private */ goog.editor.Field.prototype.handleKeyUp_ = function(e) { if (!goog.editor.BrowserFeature.USE_MUTATION_EVENTS && (this.gotGeneratingKey_ || goog.editor.Field.isSpecialGeneratingKey_(e))) { // The special keys won't have set the gotGeneratingKey flag, so we check // for them explicitly this.handleChange(); } this.invokeShortCircuitingOp_(goog.editor.Plugin.Op.KEYUP, e); if (this.isEventStopped(goog.editor.Field.EventType.SELECTIONCHANGE)) { return; } if (goog.editor.Field.SELECTION_CHANGE_KEYCODES_[e.keyCode] || ((e.ctrlKey || e.metaKey) && goog.editor.Field.CTRL_KEYS_CAUSING_SELECTION_CHANGES_[e.keyCode])) { this.selectionChangeTimer_.start(); } }; /** * Handles keyboard shortcuts on the field. Note that we bake this into our * handleKeyPress/handleKeyDown rather than using goog.events.KeyHandler or * goog.ui.KeyboardShortcutHandler for performance reasons. Since these * are handled on every key stroke, we do not want to be going out to the * event system every time. * @param {goog.events.BrowserEvent} e The browser event. * @private */ goog.editor.Field.prototype.handleKeyboardShortcut_ = function(e) { // Alt key is used for i18n languages to enter certain characters. like // control + alt + z (used for IMEs) and control + alt + s for Polish. // So we don't invoke handleKeyboardShortcut at all for alt keys. if (e.altKey) { return; } var isModifierPressed = goog.userAgent.MAC ? e.metaKey : e.ctrlKey; if (isModifierPressed || goog.editor.Field.POTENTIAL_SHORTCUT_KEYCODES_[e.keyCode]) { // TODO: goog.events.KeyHandler uses much more complicated logic // to determine key. Consider changing to what they do. var key = e.charCode || e.keyCode; if (key == 17) { // Ctrl key // In IE and Webkit pressing Ctrl key itself results in this event. return; } var stringKey = String.fromCharCode(key).toLowerCase(); if (this.invokeShortCircuitingOp_(goog.editor.Plugin.Op.SHORTCUT, e, stringKey, isModifierPressed)) { e.preventDefault(); // We don't call stopPropagation as some other handler outside of // trogedit might need it. } } }; /** * Executes an editing command as per the registered plugins. * @param {string} command The command to execute. * @param {...Object|undefined} var_args Any additional parameters needed to * execute the command. * @return {Object|boolean} False if the command wasn't handled, * otherwise, the result of the command. */ goog.editor.Field.prototype.execCommand = function(command, var_args) { var args = arguments; var result; var plugins = this.indexedPlugins_[goog.editor.Plugin.Op.EXEC_COMMAND]; for (var i = 0; i < plugins.length; ++i) { // If the plugin supports the command, that means it handled the // event and we shouldn't propagate to the other plugins. var plugin = plugins[i]; if (plugin.isEnabled(this) && plugin.isSupportedCommand(command)) { result = plugin.execCommand.apply(plugin, args); break; } } return result; }; /** * Gets the value of command(s). * @param {string|Array.<string>} commands String name(s) of the command. * @return {string|Object} Value of each command. Returns false (or array of * falses) if designMode is off or the field is otherwise uneditable, * and there are no activeOnUneditable plugins for the command. */ goog.editor.Field.prototype.queryCommandValue = function(commands) { var isEditable = this.isLoaded() && this.isSelectionEditable(); if (goog.isString(commands)) { return this.queryCommandValueInternal_(commands, isEditable); } else { var state = {}; for (var i = 0; i < commands.length; i++) { state[commands[i]] = this.queryCommandValueInternal_(commands[i], isEditable); } return state; } }; /** * Gets the value of this command. * @param {string} command The command to check. * @param {boolean} isEditable Whether the field is currently editable. * @return {string|boolean|null} The state of this command. Null if not handled. * False if the field is uneditable and there are no handlers for * uneditable commands. * @private */ goog.editor.Field.prototype.queryCommandValueInternal_ = function(command, isEditable) { var plugins = this.indexedPlugins_[goog.editor.Plugin.Op.QUERY_COMMAND]; for (var i = 0; i < plugins.length; ++i) { var plugin = plugins[i]; if (plugin.isEnabled(this) && plugin.isSupportedCommand(command) && (isEditable || plugin.activeOnUneditableFields())) { return plugin.queryCommandValue(command); } } return isEditable ? null : false; }; /** * Fires a change event only if the attribute change effects the editiable * field. We ignore events that are internal browser events (ie scrollbar * state change) * @param {Function} handler The function to call if this is not an internal * browser event. * @param {goog.events.BrowserEvent} e The browser event. * @protected */ goog.editor.Field.prototype.handleDomAttrChange = function(handler, e) { if (this.isEventStopped(goog.editor.Field.EventType.CHANGE)) { return; } e = e.getBrowserEvent(); // For XUL elements, since we don't care what they are doing try { if (e.originalTarget.prefix || e.originalTarget.nodeName == 'scrollbar') { return; } } catch (ex1) { // Some XUL nodes don't like you reading their properties. If we got // the exception, this implies a XUL node so we can return. return; } // Check if prev and new values are different, sometimes this fires when // nothing has really changed. if (e.prevValue == e.newValue) { return; } handler.call(this, e); }; /** * Handle a mutation event. * @param {goog.events.BrowserEvent|Event} e The browser event. * @private */ goog.editor.Field.prototype.handleMutationEventGecko_ = function(e) { if (this.isEventStopped(goog.editor.Field.EventType.CHANGE)) { return; } e = e.getBrowserEvent ? e.getBrowserEvent() : e; // For people with firebug, firebug sets this property on elements it is // inserting into the dom. if (e.target.firebugIgnore) { return; } this.isModified_ = true; this.isEverModified_ = true; this.changeTimerGecko_.start(); }; /** * Handle drop events. Deal with focus/selection issues and set the document * as changed. * @param {goog.events.BrowserEvent} e The browser event. * @private */ goog.editor.Field.prototype.handleDrop_ = function(e) { if (goog.userAgent.IE) { // TODO: This should really be done in the loremipsum plugin. this.execCommand(goog.editor.Command.CLEAR_LOREM, true); } // TODO: I just moved this code to this location, but I wonder why // it is only done for this case. Investigate. if (goog.editor.BrowserFeature.USE_MUTATION_EVENTS) { this.dispatchFocusAndBeforeFocus_(); } this.dispatchChange(); }; /** * @return {HTMLIFrameElement} The iframe that's body is editable. * @protected */ goog.editor.Field.prototype.getEditableIframe = function() { var dh; if (this.usesIframe() && (dh = this.getEditableDomHelper())) { // If the iframe has been destroyed, the dh could still exist since the // node may not be gc'ed, but fetching the window can fail. var win = dh.getWindow(); return /** @type {HTMLIFrameElement} */ (win && win.frameElement); } return null; }; /** * @return {goog.dom.DomHelper?} The dom helper for the editable node. */ goog.editor.Field.prototype.getEditableDomHelper = function() { return this.editableDomHelper; }; /** * @return {goog.dom.AbstractRange?} Closure range object wrapping the selection * in this field or null if this field is not currently editable. */ goog.editor.Field.prototype.getRange = function() { var win = this.editableDomHelper && this.editableDomHelper.getWindow(); return win && goog.dom.Range.createFromWindow(win); }; /** * Dispatch a selection change event, optionally caused by the given browser * event. * @param {goog.events.BrowserEvent=} opt_e Optional browser event causing this * event. */ goog.editor.Field.prototype.dispatchSelectionChangeEvent = function(opt_e) { if (this.isEventStopped(goog.editor.Field.EventType.SELECTIONCHANGE)) { return; } // The selection is editable only if the selection is inside the // editable field. var range = this.getRange(); var rangeContainer = range && range.getContainerElement(); this.isSelectionEditable_ = !!rangeContainer && goog.dom.contains(this.getElement(), rangeContainer); this.dispatchCommandValueChange(); this.dispatchEvent({type: goog.editor.Field.EventType.SELECTIONCHANGE, originalType: opt_e && opt_e.type}); this.invokeShortCircuitingOp_(goog.editor.Plugin.Op.SELECTION, opt_e); }; /** * This dispatches the beforechange event on the editable field */ goog.editor.Field.prototype.dispatchBeforeChange = function() { if (this.isEventStopped(goog.editor.Field.EventType.BEFORECHANGE)) { return; } this.dispatchEvent(goog.editor.Field.EventType.BEFORECHANGE); }; /** * This dispatches the beforetab event on the editable field. If this event is * cancelled, then the default tab behavior is prevented. * @param {goog.events.BrowserEvent} e The tab event. * @private * @return {boolean} The result of dispatchEvent. */ goog.editor.Field.prototype.dispatchBeforeTab_ = function(e) { return this.dispatchEvent({type: goog.editor.Field.EventType.BEFORETAB, shiftKey: e.shiftKey, altKey: e.altKey, ctrlKey: e.ctrlKey}); }; /** * Temporarily ignore change events. If the time has already been set, it will * fire immediately now. Further setting of the timer is stopped and * dispatching of events is stopped until startChangeEvents is called. * @param {boolean=} opt_stopChange Whether to ignore base change events. * @param {boolean=} opt_stopDelayedChange Whether to ignore delayed change * events. */ goog.editor.Field.prototype.stopChangeEvents = function(opt_stopChange, opt_stopDelayedChange) { if (opt_stopChange) { if (this.changeTimerGecko_) { this.changeTimerGecko_.fireIfActive(); } this.stopEvent(goog.editor.Field.EventType.CHANGE); } if (opt_stopDelayedChange) { this.clearDelayedChange(); this.stopEvent(goog.editor.Field.EventType.DELAYEDCHANGE); } }; /** * Start change events again and fire once if desired. * @param {boolean=} opt_fireChange Whether to fire the change event * immediately. * @param {boolean=} opt_fireDelayedChange Whether to fire the delayed change * event immediately. */ goog.editor.Field.prototype.startChangeEvents = function(opt_fireChange, opt_fireDelayedChange) { if (!opt_fireChange && this.changeTimerGecko_) { // In the case where change events were stopped and we're not firing // them on start, the user was trying to suppress all change or delayed // change events. Clear the change timer now while the events are still // stopped so that it's firing doesn't fire a stopped change event, or // queue up a delayed change event that we were trying to stop. this.changeTimerGecko_.fireIfActive(); } this.startEvent(goog.editor.Field.EventType.CHANGE); this.startEvent(goog.editor.Field.EventType.DELAYEDCHANGE); if (opt_fireChange) { this.handleChange(); } if (opt_fireDelayedChange) { this.dispatchDelayedChange_(); } }; /** * Stops the event of the given type from being dispatched. * @param {goog.editor.Field.EventType} eventType type of event to stop. */ goog.editor.Field.prototype.stopEvent = function(eventType) { this.stoppedEvents_[eventType] = 1; }; /** * Re-starts the event of the given type being dispatched, if it had * previously been stopped with stopEvent(). * @param {goog.editor.Field.EventType} eventType type of event to start. */ goog.editor.Field.prototype.startEvent = function(eventType) { // Toggling this bit on/off instead of deleting it/re-adding it // saves array allocations. this.stoppedEvents_[eventType] = 0; }; /** * Block an event for a short amount of time. Intended * for the situation where an event pair fires in quick succession * (e.g., mousedown/mouseup, keydown/keyup, focus/blur), * and we want the second event in the pair to get "debounced." * * WARNING: This should never be used to solve race conditions or for * mission-critical actions. It should only be used for UI improvements, * where it's okay if the behavior is non-deterministic. * * @param {goog.editor.Field.EventType} eventType type of event to debounce. */ goog.editor.Field.prototype.debounceEvent = function(eventType) { this.debouncedEvents_[eventType] = goog.now(); }; /** * Checks if the event of the given type has stopped being dispatched * @param {goog.editor.Field.EventType} eventType type of event to check. * @return {boolean} true if the event has been stopped with stopEvent(). * @protected */ goog.editor.Field.prototype.isEventStopped = function(eventType) { return !!this.stoppedEvents_[eventType] || (this.debouncedEvents_[eventType] && (goog.now() - this.debouncedEvents_[eventType] <= goog.editor.Field.DEBOUNCE_TIME_MS_)); }; /** * Calls a function to manipulate the dom of this field. This method should be * used whenever Trogedit clients need to modify the dom of the field, so that * delayed change events are handled appropriately. Extra delayed change events * will cause undesired states to be added to the undo-redo stack. This method * will always fire at most one delayed change event, depending on the value of * {@code opt_preventDelayedChange}. * * @param {function()} func The function to call that will manipulate the dom. * @param {boolean=} opt_preventDelayedChange Whether delayed change should be * prevented after calling {@code func}. Defaults to always firing * delayed change. * @param {Object=} opt_handler Object in whose scope to call the listener. */ goog.editor.Field.prototype.manipulateDom = function(func, opt_preventDelayedChange, opt_handler) { this.stopChangeEvents(true, true); // We don't want any problems with the passed in function permanently // stopping change events. That would break Trogedit. try { func.call(opt_handler); } finally { // If the field isn't loaded then change and delayed change events will be // started as part of the onload behavior. if (this.isLoaded()) { // We assume that func always modified the dom and so fire a single change // event. Delayed change is only fired if not prevented by the user. if (opt_preventDelayedChange) { this.startEvent(goog.editor.Field.EventType.CHANGE); this.handleChange(); this.startEvent(goog.editor.Field.EventType.DELAYEDCHANGE); } else { this.dispatchChange(); } } } }; /** * Dispatches a command value change event. * @param {Array.<string>=} opt_commands Commands whose state has * changed. */ goog.editor.Field.prototype.dispatchCommandValueChange = function(opt_commands) { if (opt_commands) { this.dispatchEvent({type: goog.editor.Field.EventType.COMMAND_VALUE_CHANGE, commands: opt_commands}); } else { this.dispatchEvent(goog.editor.Field.EventType.COMMAND_VALUE_CHANGE); } }; /** * Dispatches the appropriate set of change events. This only fires * synchronous change events in blended-mode, iframe-using mozilla. It just * starts the appropriate timer for goog.editor.Field.EventType.DELAYEDCHANGE. * This also starts up change events again if they were stopped. * * @param {boolean=} opt_noDelay True if * goog.editor.Field.EventType.DELAYEDCHANGE should be fired syncronously. */ goog.editor.Field.prototype.dispatchChange = function(opt_noDelay) { this.startChangeEvents(true, opt_noDelay) }; /** * Handle a change in the Editable Field. Marks the field has modified, * dispatches the change event on the editable field (moz only), starts the * timer for the delayed change event. Note that these actions only occur if * the proper events are not stopped. */ goog.editor.Field.prototype.handleChange = function() { if (this.isEventStopped(goog.editor.Field.EventType.CHANGE)) { return; } // Clear the changeTimerGecko_ if it's active, since any manual call to // handle change is equiavlent to changeTimerGecko_.fire(). if (this.changeTimerGecko_) { this.changeTimerGecko_.stop(); } this.isModified_ = true; this.isEverModified_ = true; if (this.isEventStopped(goog.editor.Field.EventType.DELAYEDCHANGE)) { return; } this.delayedChangeTimer_.start(); }; /** * Dispatch a delayed change event. * @private */ goog.editor.Field.prototype.dispatchDelayedChange_ = function() { if (this.isEventStopped(goog.editor.Field.EventType.DELAYEDCHANGE)) { return; } // Clear the delayedChangeTimer_ if it's active, since any manual call to // dispatchDelayedChange_ is equivalent to delayedChangeTimer_.fire(). this.delayedChangeTimer_.stop(); this.isModified_ = false; this.dispatchEvent(goog.editor.Field.EventType.DELAYEDCHANGE); }; /** * Don't wait for the timer and just fire the delayed change event if it's * pending. */ goog.editor.Field.prototype.clearDelayedChange = function() { // The changeTimerGecko_ will queue up a delayed change so to fully clear // delayed change we must also clear this timer. if (this.changeTimerGecko_) { this.changeTimerGecko_.fireIfActive(); } this.delayedChangeTimer_.fireIfActive(); }; /** * Dispatch beforefocus and focus for FF. Note that both of these actually * happen in the document's "focus" event. Unfortunately, we don't actually * have a way of getting in before the focus event in FF (boo! hiss!). * In IE, we use onfocusin for before focus and onfocus for focus. * @private */ goog.editor.Field.prototype.dispatchFocusAndBeforeFocus_ = function() { this.dispatchBeforeFocus_(); this.dispatchFocus_(); }; /** * Dispatches a before focus event. * @private */ goog.editor.Field.prototype.dispatchBeforeFocus_ = function() { if (this.isEventStopped(goog.editor.Field.EventType.BEFOREFOCUS)) { return; } this.execCommand(goog.editor.Command.CLEAR_LOREM, true); this.dispatchEvent(goog.editor.Field.EventType.BEFOREFOCUS); }; /** * Dispatches a focus event. * @private */ goog.editor.Field.prototype.dispatchFocus_ = function() { if (this.isEventStopped(goog.editor.Field.EventType.FOCUS)) { return; } goog.editor.Field.setActiveFieldId(this.id); this.isSelectionEditable_ = true; this.dispatchEvent(goog.editor.Field.EventType.FOCUS); if (goog.editor.BrowserFeature. PUTS_CURSOR_BEFORE_FIRST_BLOCK_ELEMENT_ON_FOCUS) { // If the cursor is at the beginning of the field, make sure that it is // in the first user-visible line break, e.g., // no selection:

...

 -->

|cursor|...

 //
|cursor|
...

 -->

|cursor|...

 // |cursor|
...
 -->
|cursor|...
 var field = this.getElement(); var range = this.getRange(); if (range) { var focusNode = range.getFocusNode(); if (range.getFocusOffset() == 0 && (!focusNode || focusNode == field || focusNode.tagName == goog.dom.TagName.BODY)) { goog.editor.range.selectNodeStart(field); } } } if (!goog.editor.BrowserFeature.CLEARS_SELECTION_WHEN_FOCUS_LEAVES && this.usesIframe()) { var parent = this.getEditableDomHelper().getWindow().parent; parent.getSelection().removeAllRanges(); } }; /** * Dispatches a blur event. * @protected */ goog.editor.Field.prototype.dispatchBlur = function() { if (this.isEventStopped(goog.editor.Field.EventType.BLUR)) { return; } // Another field may have already been registered as active, so only // clear out the active field id if we still think this field is active. if (goog.editor.Field.getActiveFieldId() == this.id) { goog.editor.Field.setActiveFieldId(null); } this.isSelectionEditable_ = false; this.dispatchEvent(goog.editor.Field.EventType.BLUR); }; /** * @return {boolean} Whether the selection is editable. */ goog.editor.Field.prototype.isSelectionEditable = function() { return this.isSelectionEditable_; }; /** * Event handler for clicks in browsers that will follow a link when the user * clicks, even if it's editable. We stop the click manually * @param {goog.events.BrowserEvent} e The event. * @private */ goog.editor.Field.cancelLinkClick_ = function(e) { if (goog.dom.getAncestorByTagNameAndClass(e.target, goog.dom.TagName.A)) { e.preventDefault(); } }; /** * Handle mouse down inside the editable field. * @param {goog.events.BrowserEvent} e The event. * @private */ goog.editor.Field.prototype.handleMouseDown_ = function(e) { // If the user clicks on an object (like an image) in the field // and the activeField is not set, set it. if (!goog.editor.Field.getActiveFieldId()) { goog.editor.Field.setActiveFieldId(this.id); } // Open links in a new window if the user control + clicks. if (goog.userAgent.IE) { var targetElement = e.target; if (targetElement && targetElement.tagName == goog.dom.TagName.A && e.ctrlKey) { this.originalDomHelper.getWindow().open(targetElement.href); } } }; /** * Handle mouse up inside the editable field. * @param {goog.events.BrowserEvent} e The event. * @private */ goog.editor.Field.prototype.handleMouseUp_ = function(e) { /* * We fire a selection change event immediately for listeners that depend on * the native browser event object (e). On IE, a listener that tries to * retrieve the selection with goog.dom.Range may see an out-of-date * selection range. */ this.dispatchSelectionChangeEvent(e); if (goog.userAgent.IE) { /* * Fire a second selection change event for listeners that need an * up-to-date selection range. This event will not include a native * event object. */ this.selectionChangeTimer_.start(); } }; /** * Retrieve the HTML contents of a field. * * Do NOT just get the innerHTML of a field directly--there's a lot of * processing that needs to happen. * @return {string} The scrubbed contents of the field. */ goog.editor.Field.prototype.getCleanContents = function() { if (this.queryCommandValue(goog.editor.Command.USING_LOREM)) { return goog.string.Unicode.NBSP; } if (!this.isLoaded()) { // The field is uneditable, so it's ok to read contents directly. var elem = this.getOriginalElement(); if (!elem) { this.logger.shout("Couldn't get the field element to read the contents"); } return elem.innerHTML; } var fieldCopy = this.getFieldCopy(); // Allow the plugins to handle their cleanup. this.invokeOp_(goog.editor.Plugin.Op.CLEAN_CONTENTS_DOM, fieldCopy); return this.reduceOp_( goog.editor.Plugin.Op.CLEAN_CONTENTS_HTML, fieldCopy.innerHTML); }; /** * Get the copy of the editable field element, which has the innerHTML set * correctly. * @return {Element} The copy of the editable field. * @protected */ goog.editor.Field.prototype.getFieldCopy = function() { var field = this.getElement(); // Deep cloneNode strips some script tag contents in IE, so we do this. var fieldCopy = /** @type {Element} */(field.cloneNode(false)); // For some reason, when IE sets innerHtml of the cloned node, it strips // script tags that fall at the beginning of an element. Appending a // non-breaking space prevents this. var html = field.innerHTML; if (goog.userAgent.IE && html.match(/^\s*


Before Change
else:
paths = [path]
heap = []
for path in paths:
it = (
proto.FromString(buf)
for buf in python_io.tf_record_iterator(path, options))
heap.append(_ComparableSortedRecordIterator(it, key))
heapq.heapify(heap)
i = 0
while heap:
i += 1
if max_records is not None and i > max_records:
return
nextit = heap[0]
yield nextit.value
nextit.advance()
// Because nextit.advance() mutates the first entry in the heap, the heap
// invariant may not be satisfied at this point. If the iterator is exhausted
// we should just pop it off. Otherwise, we call heapreplace and replace the
// item with itself, since this triggers the check to efficiently fix the
// heap invariant in this case where just the top element may not satisfy it.
if nextit.exhausted:
heapq.heappop(heap)
else:
heapq.heapreplace(heap, nextit)
def write_tfrecords(protos, output_path, options=None):
Writes protos to output_path.
This function writes serialized strings of each proto in protos to output_path
After Change
for buf in python_io.tf_record_iterator(path, options))
keyed_iterables.append(((key(elem), elem) for elem in protos))
for i, (_, value) in enumerate(heapq.merge(*keyed_iterables)):
if max_records is not None and i >= max_records:
return
yield value
def write_tfrecords(protos, output_path, options=None):
Writes protos to output_path.
This function writes serialized strings of each proto in protos to output_path


WHISK TAKER
Savvy mum completely transformed her kitchen without help for only £60 with Wilko paint and bargain vinyl
by Becky PembertonA CREATIVE mum-of-one has revealed how she completely transformed her kitchen without help for only £60.
Hannah Williams, 25, from Essex, decided to bite the bullet and decorate her kitchen after being fed up of looking at her dull, brown cupboard doors.
Hannah, a junior project manager, said: "My partner Jordan, 28, and I moved into our flat in February 2018 and from day one I really wanted to redecorate the kitchen.
"I kept nagging Jordan about it but he kept pushing it back so, when he was away in Ireland one weekend, I decided to just do it myself as I was tired of it look so dull.
"I'd seen articles online about people doing up their kitchens on a budget, so thought I'd give it a go.
"I did the majority of it by myself over the May bank holiday weekend - my dad helped a little bit here and there too."
BREAKDOWN OF WHAT HANNAH SPENT
- B&Q - GoodHome multi surface paint for cupboard doors, £20
- Amazon - Door handles, £11.20
- Vinyl Warehouse - Worktop vinyl, £17.95
- Wilko's - Whispering sage wall paint 2.5L, £12
Total £61.15
Hannah, who was pregnant at the time, made the kitchen look completely different in just four days and kept the renovation cheap by getting items from places like Amazon, B&Q, and Wilko's.
The now mum of one added: "I didn't realise you could just paint over the cupboard doors, I thought you had to get new ones completely.
"Once I realised, I went to B&Q and bought some of their GoodHome multi surface paint for cupboard doors for just £20 - a hell of a lot cheaper than getting them refitted!"

And this thrifty mum creates incredible playhouse under the stairs for her daughter using bargain items from B&Q for under £100.


--- layout: post title: Simple Team Workflow for Git date: 2013-12-12 21:23:52.000000000 -05:00 type: post published: true status: publish categories: - GitHub - Pair Programming tags: [] meta: _edit_last: '2' bwps_enable_ssl: '' _css_code: "div#social a img {\r\n padding: 0px;\r\n margin: 10px 3px 0 0;\r\n}" author: login<EMAIL_ADDRESS>email<EMAIL_ADDRESS>display_name: Aimee Knight first_name: Aimee last_name: Knight ---
Simply, my thoughts recorded for future iteration.
1. $ git clone
Only needed on first cycle.
2. $ cd cloned_root_directory_name
3. $ git pull
If done immediately after 'git pull' this step isn't needed.
4. $ git checkout -b feature_branch_name
5. do some hacking
6. git add .
7. git commit -m "your message"
Repeat steps 5-7 as needed.
8.
10. $ git rebase feature_branch_name
This is where things could potentially get ugly. To prevent complicated conflict's it is recommended to repeat this often.
11. $ git push origin master
Reminder, this is meant to be a simple workflow for a small team. It is highly advisable to check if there have been any pushes to the remote master since you completed step 8.


Pochettino among five favourites for Arsenal job after Emery sacking
by Buzz ContributorFollowing Unai Emery’s departure from Arsenal, former Tottenham boss Mauricio Pochettino is one of the early favourites.
Mauricio Pochettino could make a shock move across north London and replace the recently sacked Unai Emery at Arsenal.
Pochettino was recently dismissed himself by the Gunners’ fierce rivals Tottenham and promptly replaced by Jose Mourinho.
However, the Argentine remains one of the best managers in the world and is among the five early favourites for the permanent job at Arsenal.
Along with Wolves boss Nuno Espirito Santo, interim coach Freddie Ljungberg, Manchester City assistant Mikel Arteta and ex-Juventus manager Massimiliano Allegri, Pochettino is one of the leading contenders, according to Paddy Power.
Despite his link with Tottenham , the 47-year-old has certainly shown he can transform a club after taking Spurs from top four contenders to Champions League finalists over his five and a half year stint, and he could certainly do the same with Arsenal.
However, while managing Spurs, Pochettino insisted he would never cross the north London divide to the Emirates.
“I am never going to be manager of Barcelona or Arsenal because I am so identified with Tottenham and Espanyol.”
However, Sky Sports pundit Jamie Redknapp reckons Pochettino would consider managing Arsenal, saying: “If I was a decision maker at Arsenal, I'd go for Mauricio Pochettino in a heartbeat.
“I’m not sure I believe Poch would let his time with Tottenham stop him from taking over.”
Ex-Arsenal striker Paul Merson also backed Pochettino to replace Emery, saying: “Arsenal should go for Mauricio Pochettino. He’s not won anything, but he improved Tottenham over five years, tenfold.
“He’s a top manager and he is about at the moment. I know he managed at Tottenham, but George Graham had been at Arsenal when he went the other way.
“You’ve got to get past that sort of stuff.
Latest Paddy Power odds:
Freddie Ljungberg 2/1
Nuno Espirito Santo 3/1
Massimiliano Allegri 10/3
Mikel Arteta 6/1
Mauricio Pochettino 6/1


@* * Copyright 2018 HM Revenue & Customs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *@ @import views.html.templates.main_template @(profile: String, services: Seq[String])(implicit request: RequestHeader, messages: Messages) @main_template(title = messages("app.tab-title")) {
@messages("pages.services-in-profile.no-profiles")
}


{% load i18n %} {% if teacher.students.pending|length > 0 %}
{% trans "There are no pending registrations for this course." %}


Suttas of the Samyutta Nikaya
Anamataggasamyutta
II. Nidanaavagga (Nidaanavagga, Nidānavagga)
BD: On The Downbound
PTS: The Nidana Book
WP: Connected Discourses on Causation
1. Anamataggasamyutta, I.46
PTS: Kindred Sayings on the Incalculable Beginning, II.118
WP: Connected Discourses on Without Discoverable Beginning, I.651
I.
1. Tinakattham (Ti.naka.t.tham, Tiṇakaṭṭham), II.178
BD: Thatch'n Twigsmo translation
PTS: Grass and brushwood, II.118
WP: Grass and Wood, I.651
2. Pathavi Suttaṃ, I.179
PTS: Earth, II.119
WP: The Earth, I.652
3. Assu Suttaṃ, II.179
PTS: Tears, II.120
WP: Tears, I.652
ATI: Tears
4. Khīram Suttaṃ (Khiiram, Khiram), II.180
PTS: Milk, II.121
WP: Mother's Milk, I.653
5. Pabbata Suttaṃ, II.181
PTS: The Hill, II.121
WP: The Mountain, I.654
6. Sāsapa Suttaṃ (Saasapaa, Sasapa), II.182
PTS: Mustard-Seed, II.122
WP: The Mustard Seed, I.654
7. Sāvaka Suttaṃ (Saavakaa, Savaka), II.182
PTS: Disciples, II.122
WP: Disciples, I.655
8. Gaŋgā Suttaṃ (Ga.ngaa, Ganga), II.183
PTS: Ganges, II.123
WP: The River Ganges, I.655
9. Daṇḍa Suttaṃ (Da.n.do, Dando), II.184
PTS: The Stick, II.124
WP: The Stick, I.656
ATI: The Stick, Bhk. Thanissaro, trans.
10. Ekapuggala Suttaṃ, II.185
PTS: A Person, II.124
WP: Person, I.656
II.
11. Duggatam (Duggata.m, Duggataṃ), II.186
BD: Fall'n on Hard Times
PTS: Hard lot, II.125
WP: Unfortunate, I.657
ATI: Fallen on Hard Times, Bhk. Thanissaro, trans.
BD: Easy Street
PTS: Happy, II.126
WP: Happy, I.658
ATI: Happy, Bhk. Thanissaro, trans.
13. Timsamatta (Ti.msamattaa, Tiṃsamattā), II.187
PTS: Thirty Only, 126
WP: Thirty Bhikkhus, I.658
ATI: Thirty, Bhk. Thanissaro, trans.
14. Mātā Suttaṃ (Maataa, Mata), II.189
PTS: Mother, II.128
WP: 14-19 Mother, etc., I.659
ATI: Mother, Bhk. Thanissaro, trans.
15. Pītā Suttaṃ, II.189
PTS: Father, II.128
16. Bhata Suttaṃ, II.189
PTS: Brother, II.128
17. Bhagini Suttaṃ, II.189
PTS: Sister, II.128
18. Putto Suttaṃ, II.190
PTS: Son, II.128
19. Dhita Suttaṃ, II.190
PTS: Daughter, II.128
20. Vepullapabbatam Suttaṃ, II.190
PTS: Mount Vipulla, II.128
WP: Mount Vepulla, I.659


Athrun & Aaron
小园香径独徘徊
Home
About
Resume
Recent Posts
Swift for Tensorflow
July 1, 2020
可能是最容易理解的 KMP 教程
September 13, 2019
Build a cross-platform video player in 7 days
July 15, 2019
给程序员的一些持续学习的建议
June 21, 2019
线性回归的数学推导
January 15, 2018
Feels
给程序员的一些持续学习的建议
June 21, 2019
第一次体验飞机上的wifi
January 10, 2018
弄了个博客，发帖庆祝一下
January 9, 2018
ML
线性回归的数学推导
January 15, 2018
开篇文章，记录常见的latex语法
January 14, 2018
Tech
Build a cross-platform video player in 7 days
July 15, 2019
Video
Build a cross-platform video player in 7 days
July 15, 2019
algorithm
Swift for Tensorflow
July 1, 2020
可能是最容易理解的 KMP 教程
September 13, 2019


Sue James PR
About
Campaigns
Clients
Contact
Clients
Clients I've worked with
This page could also be a gallery where you can view the images from all the campaigns


Chuck Jokes
Unsplash
Music Genres
Jazz
Techno
House
Rock
Pop
Rap
Punk
Metal
Blues
Classic
Folk
Country
Reggae
Funk
Disco


What are Pseudo Classes?
A pseudo-class is used to define a special state of an element. For example, it can be used to:
Style an element when a user mouses over it.
Style visited and unvisited links differently.
Style an element when it gets focus.


It is important to note that it is an error to put any space between the
option, = and value.
Each setting has a compiled in default which may be modified in the
configuration file.
- anon_root
-
This option represents a directory which vsftpd will try to change into
after an anonymous login. Failure is silently ignored.
Default: (none)
- banned_email_file
-
This option is the name of a file containing a list of anonymous e-mail
passwords which are not permitted. This file is consulted if the option
deny_email_enable
is enabled.
Default: /etc/vsftpd.banned_emails
- banner_file
-
This option is the name of a file containing text to display when someone
connects to the server. If set, it overrides the banner string provided by
the
ftpd_banner
option.
Default: (none)
- ca_certs_file
-
This option is the name of a file to load Certificate Authority certs from, for
the purpose of validating client certs. Regrettably, the default SSL CA cert
paths are not used, because of vsftpd's use of restricted filesystem spaces
(chroot). (Added in v2.0.6).
Default: (none)
- chown_username
-
This is the name of the user who is given ownership of anonymously uploaded
files. This option is only relevant if another option,
chown_uploads,
is set.
Default: root
- chroot_list_file
-
The option is the name of a file containing a list of local users which
will be placed in a chroot() jail in their home directory. This option is
only relevant if the option
chroot_list_enable
is enabled. If the option
chroot_local_user
is enabled, then the list file becomes a list of users to NOT place in a
chroot() jail.
Default: /etc/vsftpd.chroot_list
- cmds_allowed
-
This options specifies a comma separated list of allowed FTP commands (post
login. USER, PASS and QUIT and others are always allowed pre-login). Other
commands are rejected. This is a powerful method of really locking down an
FTP server. Example: cmds_allowed=PASV,RETR,QUIT
Default: (none)
- cmds_denied
-
This options specifies a comma separated list of denied FTP commands (post
login. USER, PASS, QUIT and others are always allowed pre-login). If a command
appears on both this and
cmds_allowed
then the denial takes precedence. (Added in v2.1.0).
Default: (none)
- deny_file
-
This option can be used to set a pattern for filenames (and directory names
etc.) which should not be accessible in any way. The affected items are not
hidden, but any attempt to do anything to them (download, change into
directory, affect something within directory etc.) will be denied. This option
is very simple, and should not be used for serious access control - the
filesystem's permissions should be used in preference. However, this option
may be useful in certain virtual user setups. In particular aware that if
a filename is accessible by a variety of names (perhaps due to symbolic
links or hard links), then care must be taken to deny access to all the names.
Access will be denied to items if their name contains the string given by
hide_file, or if they match the regular expression specified by hide_file.
 Because of this,
you will need to carefully and exhaustively test any application of this
option. And you are recommended to use filesystem permissions for any
important security policies due to their greater reliability. Supported
regex syntax is any number of *, ? and unnested {,} operators. Regex
matching is only supported on the last component of a path, e.g. a/b/? is
supported but a/?/c is not.
Example: deny_file={*.mp3,*.mov,.private}
Default: (none)
- dsa_cert_file
-
This option specifies the location of the DSA certificate to use for SSL
encrypted connections.
Default: (none - an RSA certificate suffices)
- dsa_private_key_file
-
This option specifies the location of the DSA private key to use for SSL
encrypted connections. If this option is not set, the private key is expected
to be in the same file as the certificate.
Default: (none)
- email_password_file
-
This option can be used to provide an alternate file for usage by the
secure_email_list_enable
setting.
Default: /etc/vsftpd.email_passwords
- ftp_username
-
This is the name of the user we use for handling anonymous FTP. The home
directory of this user is the root of the anonymous FTP area.
Default: ftp
- ftpd_banner
-
This string option allows you to override the greeting banner displayed
by vsftpd when a connection first comes in.
Default: (none - default vsftpd banner is displayed)
- guest_username
-
See the boolean setting
guest_enable
for a description of what constitutes a guest login. This setting is the
real username which guest users are mapped to.
Default: ftp
- hide_file
-
This option can be used to set a pattern for filenames (and directory names
etc.) which should be hidden from directory listings. Despite being hidden,
the files / directories etc. are fully accessible to clients who know what
names to actually use. Items will be hidden if their names contain the string
given by hide_file, or if they match the regular expression specified by
hide_file.
See
deny_file
for details of exactly what regex syntax is supported.
Example: hide_file={*.mp3,.hidden,hide*,h?}
Default: (none)
- listen_address
-
If vsftpd is in standalone mode, the default listen address (of all local
interfaces) may be overridden by this setting. Provide a numeric IP address.
Default: (none)
- listen_address6
-
Like listen_address, but specifies a default listen address for the IPv6
listener (which is used if listen_ipv6 is set). Format is standard IPv6
address format.
Default: (none)
- local_root
-
This option represents a directory which vsftpd will try to change into
after a local (i.e. non-anonymous) login. Failure is silently ignored.
Default: (none)
- message_file
-
This option is the name of the file we look for when a new directory is
entered. The contents are displayed to the remote user. This option is
only relevant if the option
dirmessage_enable
is enabled.
Default: .message
- nopriv_user
-
This is the name of the user that is used by vsftpd when it wants to be
totally unprivileged. Note that this should be a dedicated user, rather
than nobody. The user nobody tends to be used for rather a lot of important
things on most machines.
Default: nobody
- pam_service_name
-
This string is the name of the PAM service vsftpd will use.
Default: ftp
- pasv_address
-
Use this option to override the IP address that vsftpd will advertise in
response to the PASV command. Provide a numeric IP address, unless
pasv_addr_resolve
is enabled, in which case you can provide a hostname which will be DNS
resolved for you at startup.
Default: (none - the address is taken from the incoming connected socket)
- rsa_cert_file
-
This option specifies the location of the RSA certificate to use for SSL
encrypted connections.
Default: /usr/share/ssl/certs/vsftpd.pem
- rsa_private_key_file
-
This option specifies the location of the RSA private key to use for SSL
encrypted connections. If this option is not set, the private key is expected
to be in the same file as the certificate.
Default: (none)
- secure_chroot_dir
-
This option should be the name of a directory which is empty. Also, the
directory should not be writable by the ftp user. This directory is used
as a secure chroot() jail at times vsftpd does not require filesystem access.
Default: /usr/share/empty
- ssl_ciphers
-
This option can be used to select which SSL ciphers vsftpd will allow for
encrypted SSL connections. See the
ciphers
man page for further details. Note that restricting ciphers can be a useful
security precaution as it prevents malicious remote parties forcing a cipher
which they have found problems with.
Default: DES-CBC3-SHA
- user_config_dir
-
This powerful option allows the override of any config option specified in
the manual page, on a per-user basis. Usage is simple, and is best illustrated
with an example. If you set
user_config_dir
to be
/etc/vsftpd_user_conf
and then log on as the user "chris", then vsftpd will apply the settings in
the file
/etc/vsftpd_user_conf/chris
for the duration of the session. The format of this file is as detailed in
this manual page! PLEASE NOTE that not all settings are effective on a
per-user basis. For example, many settings only prior to the user's session
being started. Examples of settings which will not affect any behviour on
a per-user basis include listen_address, banner_file, max_per_ip, max_clients,
xferlog_file, etc.
Default: (none)
- user_sub_token
-
This option is useful is conjunction with virtual users. It is used to
automatically generate a home directory for each virtual user, based on a
template. For example, if the home directory of the real user specified via
guest_username
is
/home/virtual/$USER,
and
user_sub_token
is set to
$USER,
then when virtual user fred logs in, he will end up (usually chroot()'ed) in
the directory
/home/virtual/fred.
This option also takes affect if
local_root
contains
user_sub_token.
Default: (none)
- userlist_file
-
This option is the name of the file loaded when the
userlist_enable
option is active.
Default: /etc/vsftpd.user_list
- vsftpd_log_file
-
This option is the name of the file to which we write the vsftpd style
log file. This log is only written if the option
xferlog_enable
is set, and
xferlog_std_format
is NOT set. Alternatively, it is written if you have set the option
dual_log_enable.
One further complication - if you have set
syslog_enable,
then this file is not written and output is sent to the system log instead.
Default: /var/log/vsftpd.log
- xferlog_file
-
This option is the name of the file to which we write the wu-ftpd style
transfer log. The transfer log is only written if the option
xferlog_enable
is set, along with
xferlog_std_format.
Alternatively, it is written if you have set the option
dual_log_enable.
Default: /var/log/xferlog


--- layout: post permalink: netlawman.html Tags: Array NameOfCoupon: Net Lawman Cash Back Summary: See our Net Lawman Coupon Code, Net Lawman Rebate, Net Lawman Promo Code, Net Lawman Discount, Net Lawman Coupon Cash Back Rebate here.  We have the best Net Lawman Rebate anywhere. thesis_keywords: Net Lawman , Net Lawman Coupon Code, Net Lawman Discount, Net Lawman Rebate product_name: Net Lawman hop_url: http://www.icouponblog.com/hop.php?id=197 ---
Do you need legal documents without paying lawyers a ton of money? Do you need legal advice that is affordable? You can get it from Net Lawman which has the largest collection of legal documents online. Net Lawman is the supplier of the most comprehensive set of legal documents. Check out Net Lawman for all your legal document needs and advise. Use our discount coupon to get cash rebate on Net Lawman.
Net Lawman has legal documents for every situation. For instance If you want to draw a contract agreement between two people for rental property, business or any other deal , you can find a legal document on Net Lawman. You can buy this at a very affordable price from Net Lawman online and use it. Or if you are contemplating divorce, but do not want to spend too much money on it, you can look for legal documents on Net Lawman. All documents on Net Lawman are in word format and easy to read, purchase and download. All Net Lawman documents are in English and easy to comprehend.
Save money and time with Net Lawman. It is the UK base company which is providing legal documentation services to many, very satisfied customers. If you need more legal advice or help, Net Lawman can offer you the services of a lawyer at a very reasonable price. A large number of satisfied customers speak very highly of their experience with Net Lawman. Check out Net Lawman today for a large variety of legal documents that you can buy at a very good price. Use our discount coupon to make a purchase at Net Lawman, right now!


Pat's World Famous Salmon Cookies
Enter new information for an existing location or a new location below
Shop Name
Minimum Customers Per Hour
Maximum Customers Per Hour
Average Cookies Per Customer
Update Store


Christian councillors, personalities felicitated
by Special CorrespondentMangaluru Bishop Most Rev Peter Paul Saldanha on Friday felicitated Christian Councillors elected to Mangaluru City Corporation and prominent Christian personalities appointed to important government posts during the Diocesan Pastoral Parishad meeting at Bishop House.
Those felicitated include Ronald Anil Fernandes, appointed as a member of the Karnataka Public Service Commission; Joylus D. Souza, the newly appointed chairman of Christian Development Board of Karnataka and MCC Councillors Lancelot Pinto, Naveen D. Souza, A.C. Vinayaraj and Jacintha Alfred.
Speaking on the occasion, Mr. Fernandes said Christians, particularly students from Mangaluru, rarely appear for competitive and civil services examinations.
They would not make a second attempt if they fail in the first attempt whereas multiple attempts are available in competitive examinations. While political power is shorter, the power of bureaucracy is long lasting and thus one could serve society for a longer period, Mr. Fernandes said.
M.P. Noronha Secretary of DPP, Vicar General, Chancellor, Superior Generals, Provincial Superiors, Members of DPP and others were present.


Welcome to the game!
Press 'Start' to launch the game. Press buttons after guessing to repeat back the pattern.
Once you press the wrong button, the game will be over. Good luck 🍀!


Adaptive Card Designer (preview)
Enter Full Screen
Select Host App:
New card
Copy JSON
Loading editor...


Survey Form
Hey campers, help freeCodeCamp to improve
Name*
Email*
How old are you?*
Which option best describes your current role?*
Choose one
Student
Full time job
Full time learner
Prefer not to say
Other
How likely is that you would recommend freeCodeCamp to a friend?*
Definitely
Maybe
Not sure
What do you like most in FCC*:
Choose one
Challenges
Projects
Community
Open Source
Things that should be improved in the future(Check all that apply):
Front-End Projects
Back-End Projects
Data Visualization
Challenges
Open Source Community
Gitter help rooms
Videos
Wiki
Forum
Additional Courses
Any comments or suggestions?
Submit


BREAKING: Mourinho Begins Tottenham Reign With A Win
Goals from Son Heung-min, Lucas Moura and Harry Kane ensured that Mourinho's team walked away with the three maximum points despite two late goals from West Ham through Michail Antonio and Angelo Ogbonna.
by SaharaReporters, New York
Portuguese Coach, Jose Mourinho, began his reign as head coach of English Premiership side Tottenham Hotspur on Saturday with a 3-2 win over fellow Londoners – West Ham United.
Goals from Son Heung-min, Lucas Moura and Harry Kane ensured that Mourinho's team walked away with the three maximum points despite two late goals from West Ham through Michail Antonio and Angelo Ogbonna.
The win ends a run of poor results for Spurs, who before this game sat in 14th position on the leagues table, forcing the management of the club to fire Mauricio Pochettino. See Also Sports Mourinho Named New Spurs Coach After Pochettino Sacking 0 Comments 3 Days Ago
After his appointment on Wednesday, Mourinho had said, “I am excited to be joining a club with such a great heritage and such passionate supporters.
“The quality in both the squad and the academy excites me. Working with these players is what has attracted me.”


Tournament Management System
Like playing volleyball or soccer?

Now you can do it with ease using
Tournament Management System!
Tournament Management System
Like playing volleyball or soccer?

Now you can do it with ease using
Tournament Management System!


{% include head.html %}
{% include navbar.html %}
{% include upcomingevents.html %} {% include hiking.html %} {% include blogposts.html %}
{% include footer.html %}


APT29 Evaluation: Detection Categories
The evaluation focuses on articulating how detections occur, rather than assigning scores to vendor capabilities.
For the evaluation, we categorize each detection and capture notes about how those detections occur. We organize detections according to each technique. Techniques may have more than one detection if the capability detects the technique in different ways, and detections we observe are included in the results. While we make every effort to capture different detections, vendor capabilities may be able to detect procedures in ways that we did not capture. For a detection to be included for a given technique, it must apply to that technique specifically (i.e. just because a detection applies to one technique in a Step or Sub-Step does not mean it applies to all techniques of that Step). For proof of detection in each category, we require that the proof be provided to us, but we may not include all detection details in public results, particularly when those details are sensitive.
To determine the appropriate category for a detection, we review the screenshot(s) provided, notes taken during the evaluation, results of follow-up questions to the vendor, and vendor feedback on draft results. We also independently test procedures in a separate lab environment and review open-source tool detections and forensic artifacts. This testing informs what is considered to be a detection for each technique.
After performing detection categorizations, we calibrate the categories across all vendors to look for discrepancies and ensure categories are applied consistently. The decision of what category to apply is ultimately based on human analysis and is therefore subject to discretion and biases inherent in all human analysis, although we do make efforts to hedge against these biases by structuring analysis as described above.
For the APT29 evaluation, there are 13 possible detection categories. These categories are divided into two types: “Main” and “Modifier.” Each detection receives one “Main” category designation and may optionally receive one or more “Modifier” category designations.
We break the two category types down as follows:
Main Detection Types
None
No data available that was automatically collected, processed, and was made available within the capability related to the behavior under test. If data is available that is not directly relevant to the procedure tested, this will be categorized as “None.” In these cases, a vendor may receive a categorization of “None” with additional notes and screenshots about that data.
| Examples |
|---|
| No data is collected related to the particular action being performed. |
| An alert fires at the same time that the red team performs the procedure, but it is not related to the technique under test. |
Telemetry
Minimally processed data collected by the capability showing that event(s) occurred specific to the behavior under test. (i.e. showing the procedure/command that was executed). Evidence must show definitively that behavior occurred and be related to the execution mechanism (did happen vs may have happened). Evidence must be related to what caused the behavior. There is no evidence of complex logic or an advanced rule leading to the data output, and no labeling occurred other than simple field labeling.
| Example |
|---|
| Command-line output is produced that shows a certain command was run on a workstation by a given username. |
MSSP
Data is presented from a managed security service provider (MSSP) or monitoring service based on human analysis and indication of an incident occurring. MSSP are inherently delayed due to the manual analysis necessary and will be marked as delayed to remain consistent with other delayed detections.
| Example |
|---|
| An email was received from an analyst describing the context of actions related to data exfiltration. |
General
Processed data specifying that malicious/abnormal event(s) occurred, with relation to the behavior under test. (i.e. cmd.exe /c copy cmd.exe sethc.exe, is abnormal/malicious activity or an identifier stating that "suspicious activity occurred". No or limited details are provided as to why the action was performed (tactic), or details for how the action was performed (technique).
| Examples |
|---|
| An alert describing "cmd.exe /c copy cmd.exe sethc.exe" as abnormal/malicious activity, but not stating it's related to Accessibility Features or a more specific description of what occurred. |
| A “Suspicious File” alert triggered upon initial execution of the executable file. |
| An alert stating that "suspicious activity occurred" related to an action but did not provide detail. |
Tactic
Processed data specifying ATT&CK Tactic or equivalent level of enrichment to the data collected by the capability. Gives the analyst information on the potential intent of the activity, or helps answer the question "why this would be done" (i.e. Persistence was set up or there was a sequence of Discovery commands).
| Examples |
|---|
| An alert called “Malicious Discovery” is triggered on a series of discovery techniques. The alert has a score indicating the alert is likely malicious. The alert does not identify the specific type of discovery performed. |
| An alert describing that persistence occurred but not specifying how persistence was achieved. |
Technique
Processed data specifying ATT&CK Technique or equivalent level of enrichment to the data collected by the capability. Gives the analyst information on how the action was performed, or helps answer the question "what was done" (i.e. Accessibility Features or Credential Dumping).
| Examples |
|---|
| An alert called "Credential Dumping" is triggered with enough detail to show what process originated the behavior against lsass.exe and/or provides detail on what type of credential dumping occurred. |
| An alert for "Lateral Movement with Service Execution" is triggered describing what service launched and what system was targeted. |
Modifier Detection Types
Alert
Data is presented as priority notification to the analyst as an indication of a suspicious or malicious event occurring for further investigation (e.g.: icon, queue, highlight, popup, etc.). Not a modifier of Telemetry.
| Examples |
|---|
| A visual notification occurred in a dashboard and/or alert queue that "Lateral Movement" occurred. |
| A recognizable identifier populated to a dashboard so that an analyst recognizes that a high severity event may have occurred. |
Delayed
Detection (alerts, telemetry, tactic, technique, etc.) is unavailable due to some factor that slows or defers its presentation to the user, for example subsequent or additional processing produce a detection for the activity. The Delayed category is not applied for normal automated data ingestion and routine processing taking minimal time for data to appear to the user, nor is it applied due to range or connectivity issues that are unrelated to the capability itself. The Delayed modifier will always be applied with modifiers describing more detail about the nature of the delay.
Delayed is subdivided into:
- Manual — Processing was triggered by human action and not initiated automatically. In the case of detections provided by a MSSP, human analysts reviewed and produced the outputs that were later presented to an analyst.
- Processing – Detection incurred a delay based on additional data processing to apply complex logic to the events where the results were later available to an analyst.
| Examples |
|---|
| The capability’s cloud service component uses machine learning algorithms that trigger a detection on credential dumping hours after the red team performs it. This detection would receive the Main detection category of Technique with a Modifier detection category of Delayed-Processing. |
| The capability sends data back to an analyst team, who manually analyze the raw data and create an alert called “Malicious Behavior Detected” that appears in the interface three hours after the red team performs the procedure. This detection will receive a Main detection category of MSSP and a Modifier detection category of Delayed-Manual. |
Host Interrogation
Data is manually pulled from an endpoint via the capability for analysis. This category represents possible behavior identified through manual analysis from data that is not automatically ingested and analyzed by the capability to show an analyst that event(s) occurred specific to the behavior under test. Though useful as a capability to some security teams, capturing data through these means may be difficult and/or depend on the skill level of the analyst to derive actionable information. Host Interrogation is a modifier that will only apply to the None category. It will be marked as delayed to remain consistent with other delayed detections.
| Example |
|---|
| There is a remote shell component to the capability that can be used to pull native OS logs from a system suspected of being compromised for further analysis. |
Residual Artifact
Data, such as a binary or process memory, that requires additional analysis to determine what capabilities or behaviors may have been used. This category represents possible behavior identified through manual analysis from data that is not automatically ingested and analyzed by the capability to show an analyst that event(s) occurred specific to the behavior under test. The collected data is more a byproduct of adversary actions and less indicative of adversary behavior. Though useful as a capability to some security teams, capturing data through these means may be difficult and/or depend on the skill level of the analyst to derive actionable information. Residual Artifact is a modifier that will only apply to the None category. It will be marked as delayed to remain consistent with other delayed detections.
| Examples |
|---|
| Process memory of svchost.exe was collected for later analysis because it was identified as a suspicious process. Later strings analysis showed that there may have been a keylogger present on the system. |
| PowerShell scripts are collected automatically upon execution. Later analysis of a script shows it contains the functionality to capture a screenshot of the user's desktop. |
Configuration Change
The configuration of the capability was changed since the start of the evaluation. This may be done to show additional data can be collected and/or processed. The Configuration Change modifier may be applied with additional modifiers describing the nature of the change.
Configuration Change is subdivided into:
- UX – Change was to the user experience and not to the capability's ability to detect behavior. Changes could include display of a certain type of data that was already collected but not visible to the user.
- Detection – Change was to the capability's ability to capture or process information that impacts its ability to detect adversary behavior. Changes could include collecting a new type of information by the sensor or new processing logic that was deployed.
| Examples |
|---|
| Data showing account creation is collected on the backend but not displayed to the end user by default. The vendor changes a backend setting to allow Telemetry on account creation to be displayed in the user interface, so a detection of Telemetry and Configuration Change-UX would be given for the Create Account technique. |
| The vendor toggles a setting that would display an additional label of “Discovery” when the foo, foo1, and foo2 discovery commands are executed. A detection of Tactic and Configuration Change-Detection would be given (as opposed to a detection of Telemetry that would have been given before the change). |
| A rule or detection logic is created and applied retroactively or is later retested to show functionality that exists in the capability. This would be labeled with a modifier Configuration Change-Detection. |
Innovative
Designation applied to innovative and useful ways to detect a technique under test. Not all techniques will have or can get this designation applied to vendor solutions. It is meant to highlight accurate and robust approaches that bring value and deeper insight to consumers. This modifier will be applied at the Evaluation Team's discretion and will take into account data collected, method of detection, accuracy of detection, context provided to the end user, and display of information.
Changes from the APT3 Evaluation
IOC
Enrichment
General Behavior
Specific Behavior
MSSP
Tainted
Host Interrogation and Residual Artifacts
Delayed
Configuration Change
Round Specific Resources
| APT3 Overview |
|---|
| APT29 Overview |
| Carbanak+FIN7 Overview |
| Wizard Spider and Sandworm Overview |


The Shitting Duck: Mechanisierung, Magie und Sozio-technische Systeme
Ever since the beginnings of the Enlightenment, the importance of automatons, mechanization, and algorithmic methods has steadily increased. While the first automatons were built with the intention to imitate life in one way or another, it was the second generation that played a major part in the industrial revolution: Instead of imitating life, they begin to replace it by taking over tasks which were previously carried out by human workers. With the second industrial revolution, mechanization becomes technology and previously unconnected individual machines become integrated in a larger socio-technical context consisting of a net of interwoven actions by humans and machines.
At the same time, this development convoys a sacred promise of salvation: Technology promises everlasting and rapid progress and through it the end of all mundane torments such as manual labor, illnesses, natural disasters, hunger, and so on. Magical rituals of complexity and rationality beyond the comprehension of the individual inaugurate engineers as our new priests. They are supposedly leading the way to prevent the foreseeable end of our physical world - all this in the face of our past experience that taught us the power of technology to destroy.
On the basis of these developments, this seminar examines the history and future of the term technology. We will attempt to understand technology as ideology, that is the ways in which technology reverses the relationship of high and applied arts, of physical and mental processes, of objects and ideas, and of enslaved and free man. In this process, technology is rendered as an object. It influences our life world while it remains at the same time magically shielded from our grasp. Is it possible to unriddle this reification, even to reverse it? First signs of yet another ideological shift appear on the horizon with the Web 2.0 and 3.0, which we are going to explore in the last part of the seminar.
The syllabus for this English-speaking post-graduate seminar, which I taught together with Martin Dege at Universität Konstanz during the summer semester 2014, can be found here.