text:
/*
DS3231.cpp: DS3231 Real-Time Clock library
Eric Ayars
4/1/11

Spliced in DateTime all-at-once reading (to avoid rollover) and unix time
from Jean-Claude Wippler and Limor Fried
Andy Wickert
5/15/11

Released into the public domain.
*/

#include <DS3231.h>

// These included for the DateTime class inclusion; will try to find a way to
// not need them in the future...
#include <avr/pgmspace.h>
// Changed the following to work on 1.0
//#include "WProgram.h"
#include <Arduino.h>

#define CLOCK_ADDRESS 0x68

#define SECONDS_FROM_1970_TO_2000 946684800

// Constructor
DS3231::DS3231() {
	// nothing to do for this constructor.
}

// Utilities from JeeLabs/Ladyada

////////////////////////////////////////////////////////////////////////////////
// utility code, some of this could be exposed in the DateTime API if needed

// DS3231 is smart enough to know this, but keeping it for now so I don't have
// to rewrite their code. -ADW
static const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };

// number of days since 2000/01/01, valid for 2001..2099
static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) {
    if (y >= 2000)
        y -= 2000;
    uint16_t days = d;
    for (uint8_t i = 1; i < m; ++i)
        days += pgm_read_byte(daysInMonth + i - 1);
    if (m > 2 && y % 4 == 0)
        ++days;
    return days + 365 * y + (y + 3) / 4 - 1;
}

static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) {
    return ((days * 24L + h) * 60 + m) * 60 + s;
}

/*****************************************
	Public Functions
 *****************************************/

/*******************************************************************************
 * TO GET ALL DATE/TIME INFORMATION AT ONCE AND AVOID THE CHANCE OF ROLLOVER
 * DateTime implementation spliced in here from Jean-Claude Wippler's (JeeLabs)
 * RTClib, as modified by Limor Fried (Ladyada); source code at:
 * https://github.com/adafruit/RTClib
 ******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
// DateTime implementation - ignores time zones and DST changes
// NOTE: also ignores leap seconds, see http://en.wikipedia.org/wiki/Leap_second

DateTime::DateTime (uint32_t t) {
  t -= SECONDS_FROM_1970_TO_2000;    // bring to 2000 timestamp from 1970

    ss = t % 60;
    t /= 60;
    mm = t % 60;
    t /= 60;
    hh = t % 24;
    uint16_t days = t / 24;
    uint8_t leap;
    for (yOff = 0; ; ++yOff) {
        leap = yOff % 4 == 0;
        if (days < 365 + leap)
            break;
        days -= 365 + leap;
    }
    for (m = 1; ; ++m) {
        uint8_t daysPerMonth = pgm_read_byte(daysInMonth + m - 1);
        if (leap && m == 2)
            ++daysPerMonth;
        if (days < daysPerMonth)
            break;
        days -= daysPerMonth;
    }
    d = days + 1;
}

DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) {
    if (year >= 2000)
        year -= 2000;
    yOff = year;
    m = month;
    d = day;
    hh = hour;
    mm = min;
    ss = sec;
}

static uint8_t conv2d(const char* p) {
    uint8_t v = 0;
    if ('0' <= *p && *p <= '9')
        v = *p - '0';
    return 10 * v + *++p - '0';
}

// UNIX time: IS CORRECT ONLY WHEN SET TO UTC!!!
uint32_t DateTime::unixtime(void) const {
  uint32_t t;
  uint16_t days = date2days(yOff, m, d);
  t = time2long(days, hh, mm, ss);
  t += SECONDS_FROM_1970_TO_2000;  // seconds from 1970 to 2000

  return t;
}

// Slightly modified from JeeLabs / Ladyada
// Get all date/time at once to avoid rollover (e.g., minute/second don't match)
static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); }
static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); }

DateTime RTClib::now() {
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0);	// This is the first register address (Seconds)
  			// We'll read from here on for 7 bytes: secs reg, minutes reg, hours, days, months and years.
  Wire.endTransmission();

  Wire.requestFrom(CLOCK_ADDRESS, 7);
  uint8_t ss = bcd2bin(Wire.read() & 0x7F);
  uint8_t mm = bcd2bin(Wire.read());
  uint8_t hh = bcd2bin(Wire.read());
  Wire.read();
  uint8_t d = bcd2bin(Wire.read());
  uint8_t m = bcd2bin(Wire.read());
  uint16_t y = bcd2bin(Wire.read()) + 2000;

  return DateTime (y, m, d, hh, mm, ss);
}

///// ERIC'S ORIGINAL CODE FOLLOWS /////

byte DS3231::getSecond() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x00);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte DS3231::getMinute() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x01);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte DS3231::getHour(bool& h12, bool& PM) {
	byte temp_buffer;
	byte hour;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x02);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();
	h12 = temp_buffer & 0b01000000;
	if (h12) {
		PM = temp_buffer & 0b00100000;
		hour = bcdToDec(temp_buffer & 0b00011111);
	} else {
		hour = bcdToDec(temp_buffer & 0b00111111);
	}
	return hour;
}

byte DS3231::getDoW() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x03);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte DS3231::getDate() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x04);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte DS3231::getMonth(bool& Century) {
	byte temp_buffer;
	byte hour;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x05);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();
	Century = temp_buffer & 0b10000000;
	return (bcdToDec(temp_buffer & 0b01111111)) ;
}

byte DS3231::getYear() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x06);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

void DS3231::setSecond(byte Second) {
	// Sets the seconds
	// This function also resets the Oscillator Stop Flag, which is set
	// whenever power is interrupted.
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x00);
	Wire.write(decToBcd(Second));
	Wire.endTransmission();
	// Clear OSF flag
	byte temp_buffer = readControlByte(1);
	writeControlByte((temp_buffer & 0b01111111), 1);
}

void DS3231::setMinute(byte Minute) {
	// Sets the minutes
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x01);
	Wire.write(decToBcd(Minute));
	Wire.endTransmission();
}

void DS3231::setHour(byte Hour) {
	// Sets the hour, without changing 12/24h mode.
	// The hour must be in 24h format.

	bool h12;

	// Start by figuring out what the 12/24 mode is
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x02);
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	h12 = (Wire.read() & 0b01000000);
	// if h12 is true, it's 12h mode; false is 24h.

	if (h12) {
		// 12 hour
		if (Hour > 12) {
			Hour = decToBcd(Hour-12) | 0b01100000;
		} else {
			Hour = decToBcd(Hour) & 0b11011111;
		}
	} else {
		// 24 hour
		Hour = decToBcd(Hour) & 0b10111111;
	}

	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x02);
	Wire.write(Hour);
	Wire.endTransmission();
}

void DS3231::setDoW(byte DoW) {
	// Sets the Day of Week
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x03);
	Wire.write(decToBcd(DoW));
	Wire.endTransmission();
}

void DS3231::setDate(byte Date) {
	// Sets the Date
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x04);
	Wire.write(decToBcd(Date));
	Wire.endTransmission();
}

void DS3231::setMonth(byte Month) {
	// Sets the month
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x05);
	Wire.write(decToBcd(Month));
	Wire.endTransmission();
}

void DS3231::setYear(byte Year) {
	// Sets the year
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x06);
	Wire.write(decToBcd(Year));
	Wire.endTransmission();
}

void DS3231::setClockMode(bool h12) {
	// sets the mode to 12-hour (true) or 24-hour (false).
	// One thing that bothers me about how I've written this is that
	// if the read and right happen at the right hourly millisecnd,
	// the clock will be set back an hour. Not sure how to do it better,
	// though, and as long as one doesn't set the mode frequently it's
	// a very minimal risk.
	// It's zero risk if you call this BEFORE setting the hour, since
	// the setHour() function doesn't change this mode.

	byte temp_buffer;

	// Start by reading byte 0x02.
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x02);
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();

	// Set the flag to the requested value:
	if (h12) {
		temp_buffer = temp_buffer | 0x01000000;
	} else {
		temp_buffer = temp_buffer & 0x10111111;
	}

	// Write the byte
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x02);
	Wire.write(temp_buffer);
	Wire.endTransmission();
}

float DS3231::getTemperature() {
	// Checks the internal thermometer on the DS3231 and returns the
	// temperature as a floating-point value.

  // Updated / modified a tiny bit from "Coding Badly" and "Tri-Again"
  // http://forum.arduino.cc/index.php/topic,22301.0.html

  byte tMSB, tLSB;
  float temp3231;

  // temp registers (11h-12h) get updated automatically every 64s
  Wire.beginTransmission(CLOCK_ADDRESS);
  Wire.write(0x11);
  Wire.endTransmission();
  Wire.requestFrom(CLOCK_ADDRESS, 2);

  // Should I do more "if available" checks here?
  if(Wire.available()) {
    tMSB = Wire.read(); //2's complement int portion
    tLSB = Wire.read(); //fraction portion

    temp3231 = ((((short)tMSB << 8) | (short)tLSB) >> 6) / 4.0;
  }
  else {
    temp3231 = -9999; // Some obvious error value
  }

  return temp3231;
}

void DS3231::getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM) {
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x07);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 4);

	temp_buffer	= Wire.read();	// Get A1M1 and A1 Seconds
	A1Second	= bcdToDec(temp_buffer & 0b01111111);
	// put A1M1 bit in position 0 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>7;

	temp_buffer		= Wire.read();	// Get A1M2 and A1 minutes
	A1Minute	= bcdToDec(temp_buffer & 0b01111111);
	// put A1M2 bit in position 1 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>6;

	temp_buffer	= Wire.read();	// Get A1M3 and A1 Hour
	// put A1M3 bit in position 2 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>5;
	// determine A1 12/24 mode
	A1h12		= temp_buffer & 0b01000000;
	if (A1h12) {
		A1PM	= temp_buffer & 0b00100000;			// determine am/pm
		A1Hour	= bcdToDec(temp_buffer & 0b00011111);	// 12-hour
	} else {
		A1Hour	= bcdToDec(temp_buffer & 0b00111111);	// 24-hour
	}

	temp_buffer	= Wire.read();	// Get A1M4 and A1 Day/Date
	// put A1M3 bit in position 3 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>4;
	// determine A1 day or date flag
	A1Dy		= (temp_buffer & 0b01000000)>>6;
	if (A1Dy) {
		// alarm is by day of week, not date.
		A1Day	= bcdToDec(temp_buffer & 0b00001111);
	} else {
		// alarm is by date, not day of week.
		A1Day	= bcdToDec(temp_buffer & 0b00111111);
	}
}

void DS3231::getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM) {
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x0b);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 3);
	temp_buffer	= Wire.read();	// Get A2M2 and A2 Minutes
	A2Minute	= bcdToDec(temp_buffer & 0b01111111);
	// put A2M2 bit in position 4 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>3;

	temp_buffer	= Wire.read();	// Get A2M3 and A2 Hour
	// put A2M3 bit in position 5 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>2;
	// determine A2 12/24 mode
	A2h12		= temp_buffer & 0b01000000;
	if (A2h12) {
		A2PM	= temp_buffer & 0b00100000;			// determine am/pm
		A2Hour	= bcdToDec(temp_buffer & 0b00011111);	// 12-hour
	} else {
		A2Hour	= bcdToDec(temp_buffer & 0b00111111);	// 24-hour
	}

	temp_buffer	= Wire.read();	// Get A2M4 and A1 Day/Date
	// put A2M4 bit in position 6 of DS3231_AlarmBits.
	AlarmBits	= AlarmBits | (temp_buffer & 0b10000000)>>1;
	// determine A2 day or date flag
	A2Dy		= (temp_buffer & 0b01000000)>>6;
	if (A2Dy) {
		// alarm is by day of week, not date.
		A2Day	= bcdToDec(temp_buffer & 0b00001111);
	} else {
		// alarm is by date, not day of week.
		A2Day	= bcdToDec(temp_buffer & 0b00111111);
	}
}

void DS3231::setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM) {
	//	Sets the alarm-1 date and time on the DS3231, using A1* information
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x07);	// A1 starts at 07h
	// Send A1 second and A1M1
	Wire.write(decToBcd(A1Second) | ((AlarmBits & 0b00000001) << 7));
	// Send A1 Minute and A1M2
	Wire.write(decToBcd(A1Minute) | ((AlarmBits & 0b00000010) << 6));
	// Figure out A1 hour
	if (A1h12) {
		// Start by converting existing time to h12 if it was given in 24h.
		if (A1Hour > 12) {
			// well, then, this obviously isn't a h12 time, is it?
			A1Hour = A1Hour - 12;
			A1PM = true;
		}
		if (A1PM) {
			// Afternoon
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A1Hour) | 0b01100000;
		} else {
			// Morning
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A1Hour) | 0b01000000;
		}
	} else {
		// Now for 24h
		temp_buffer = decToBcd(A1Hour);
	}
	temp_buffer = temp_buffer | ((AlarmBits & 0b00000100)<<5);
	// A1 hour is figured out, send it
	Wire.write(temp_buffer);
	// Figure out A1 day/date and A1M4
	temp_buffer = ((AlarmBits & 0b00001000)<<4) | decToBcd(A1Day);
	if (A1Dy) {
		// Set A1 Day/Date flag (Otherwise it's zero)
		temp_buffer = temp_buffer | 0b01000000;
	}
	Wire.write(temp_buffer);
	// All done!
	Wire.endTransmission();
}

void DS3231::setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM) {
	//	Sets the alarm-2 date and time on the DS3231, using A2* information
	byte temp_buffer;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x0b);	// A1 starts at 0bh
	// Send A2 Minute and A2M2
	Wire.write(decToBcd(A2Minute) | ((AlarmBits & 0b00010000) << 3));
	// Figure out A2 hour
	if (A2h12) {
		// Start by converting existing time to h12 if it was given in 24h.
		if (A2Hour > 12) {
			// well, then, this obviously isn't a h12 time, is it?
			A2Hour = A2Hour - 12;
			A2PM = true;
		}
		if (A2PM) {
			// Afternoon
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A2Hour) | 0b01100000;
		} else {
			// Morning
			// Convert the hour to BCD and add appropriate flags.
			temp_buffer = decToBcd(A2Hour) | 0b01000000;
		}
	} else {
		// Now for 24h
		temp_buffer = decToBcd(A2Hour);
	}
	// add in A2M3 bit
	temp_buffer = temp_buffer | ((AlarmBits & 0b00100000)<<2);
	// A2 hour is figured out, send it
	Wire.write(temp_buffer);
	// Figure out A2 day/date and A2M4
	temp_buffer = ((AlarmBits & 0b01000000)<<1) | decToBcd(A2Day);
	if (A2Dy) {
		// Set A2 Day/Date flag (Otherwise it's zero)
		temp_buffer = temp_buffer | 0b01000000;
	}
	Wire.write(temp_buffer);
	// All done!
	Wire.endTransmission();
}

void DS3231::turnOnAlarm(byte Alarm) {
	// turns on alarm number "Alarm". Defaults to 2 if Alarm is not 1.
	byte temp_buffer = readControlByte(0);
	// modify control byte
	if (Alarm == 1) {
		temp_buffer = temp_buffer | 0b00000101;
	} else {
		temp_buffer = temp_buffer | 0b00000110;
	}
	writeControlByte(temp_buffer, 0);
}

void DS3231::turnOffAlarm(byte Alarm) {
	// turns off alarm number "Alarm". Defaults to 2 if Alarm is not 1.
	// Leaves interrupt pin alone.
	byte temp_buffer = readControlByte(0);
	// modify control byte
	if (Alarm == 1) {
		temp_buffer = temp_buffer & 0b11111110;
	} else {
		temp_buffer = temp_buffer & 0b11111101;
	}
	writeControlByte(temp_buffer, 0);
}

bool DS3231::checkAlarmEnabled(byte Alarm) {
	// Checks whether the given alarm is enabled.
	byte result = 0x0;
	byte temp_buffer = readControlByte(0);
	if (Alarm == 1) {
		result = temp_buffer & 0b00000001;
	} else {
		result = temp_buffer & 0b00000010;
	}
	return result;
}

bool DS3231::checkIfAlarm(byte Alarm) {
	// Checks whether alarm 1 or alarm 2 flag is on, returns T/F accordingly.
	// Turns flag off, also.
	// defaults to checking alarm 2, unless Alarm == 1.
	byte result;
	byte temp_buffer = readControlByte(1);
	if (Alarm == 1) {
		// Did alarm 1 go off?
		result = temp_buffer & 0b00000001;
		// clear flag
		temp_buffer = temp_buffer & 0b11111110;
	} else {
		// Did alarm 2 go off?
		result = temp_buffer & 0b00000010;
		// clear flag
		temp_buffer = temp_buffer & 0b11111101;
	}
	writeControlByte(temp_buffer, 1);
	return result;
}

void DS3231::enableOscillator(bool TF, bool battery, byte frequency) {
	// turns oscillator on or off. True is on, false is off.
	// if battery is true, turns on even for battery-only operation,
	// otherwise turns off if Vcc is off.
	// frequency must be 0, 1, 2, or 3.
	// 0 = 1 Hz
	// 1 = 1.024 kHz
	// 2 = 4.096 kHz
	// 3 = 8.192 kHz (Default if frequency byte is out of range)
	if (frequency > 3) frequency = 3;
	// read control byte in, but zero out current state of RS2 and RS1.
	byte temp_buffer = readControlByte(0) & 0b11100111;
	if (battery) {
		// turn on BBSQW flag
		temp_buffer = temp_buffer | 0b01000000;
	} else {
		// turn off BBSQW flag
		temp_buffer = temp_buffer & 0b10111111;
	}
	if (TF) {
		// set ~EOSC to 0 and INTCN to zero.
		temp_buffer = temp_buffer & 0b01111011;
	} else {
		// set ~EOSC to 1, leave INTCN as is.
		temp_buffer = temp_buffer | 0b10000000;
	}
	// shift frequency into bits 3 and 4 and set.
	frequency = frequency << 3;
	temp_buffer = temp_buffer | frequency;
	// And write the control bits
	writeControlByte(temp_buffer, 0);
}

void DS3231::enable32kHz(bool TF) {
	// turn 32kHz pin on or off
	byte temp_buffer = readControlByte(1);
	if (TF) {
		// turn on 32kHz pin
		temp_buffer = temp_buffer | 0b00001000;
	} else {
		// turn off 32kHz pin
		temp_buffer = temp_buffer & 0b11110111;
	}
	writeControlByte(temp_buffer, 1);
}

bool DS3231::oscillatorCheck() {
	// Returns false if the oscillator has been off for some reason.
	// If this is the case, the time is probably not correct.
	byte temp_buffer = readControlByte(1);
	bool result = true;
	if (temp_buffer & 0b10000000) {
		// Oscillator Stop Flag (OSF) is set, so return false.
		result = false;
	}
	return result;
}

/*****************************************
	Private Functions
 *****************************************/

byte DS3231::decToBcd(byte val) {
// Convert normal decimal numbers to binary coded decimal
	return ( (val/10*16) + (val%10) );
}

byte DS3231::bcdToDec(byte val) {
// Convert binary coded decimal to normal decimal numbers
	return ( (val/16*10) + (val%16) );
}

byte DS3231::readControlByte(bool which) {
	// Read selected control byte
	// first byte (0) is 0x0e, second (1) is 0x0f
	Wire.beginTransmission(CLOCK_ADDRESS);
	if (which) {
		// second control byte
		Wire.write(0x0f);
	} else {
		// first control byte
		Wire.write(0x0e);
	}
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return Wire.read();
}

void DS3231::writeControlByte(byte control, bool which) {
	// Write the selected control byte.
	// which=false -> 0x0e, true->0x0f.
	Wire.beginTransmission(CLOCK_ADDRESS);
	if (which) {
		Wire.write(0x0f);
	} else {
		Wire.write(0x0e);
	}
	Wire.write(control);
	Wire.endTransmission();
}


text:
namespace :poi do

  desc "crawl elementary schools' name and address from 'xuexiao.eol.cn'"
  task :update_elementary_schools do
    update_school(::Db::BaseElementarySchool, 'Elementary')
  end

  desc "crawl middle schools' name and address from 'xuexiao.eol.cn'"
  task :update_middle_schools do
    update_school(::Db::BaseMiddleSchool, 'Middle')
  end

  desc "crawl high schools' name and address from 'xuexiao.eol.cn'"
  task :update_high_schools do
    update_school(::Db::BaseHighSchool, 'High')
  end

  def call( type )
    eval "::POI::School::#{type}"
  end

  def update_school( base, type )
    # todo: dynamic call
    num_of_page = call(type).max_page_num

    thread_num = 5
    queue = Queue.new

    workers = (0..thread_num).map do
      # start a new thread
      Thread.new do
        begin
          while true
            page_i = num_of_page
            num_of_page -= 1
            raise "Works finished" if num_of_page < 0

            # sleep for 0.1 second
            sleep( 0.1 )
            begin # parse schools in one page and store into database
              call(type).schools_in_page( page_i ).each do | school |
                queue.push( school )
              end
            rescue => e
              puts "error encountered when processing page: " + page_i.to_s
              p e
            end
          end
        rescue ThreadError
        end
      end
    end # works.map

    # writer thread
    writer = Thread.new do
      # wait for workers
      sleep( thread_num*0.2 + 5 )
      while num_of_page > 0
        begin
          school = queue.pop
          school.delete(:page)
          base.using(:master).new( school ).save
        rescue => e
          p e
        end
        # adaptive wrting rate
        sleep( 3.0/(queue.length+1) )
      end
    end

    # hold main thread
    workers.map(&:join);
    writer.join
  end

end

redpajama_set_name: RedPajamaGithub


text:
package br.senac.tads4.piiv.config.init;

import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import br.senac.tads4.piiv.config.JpaConfig;
import br.senac.tads4.piiv.config.MailConfig;
import br.senac.tads4.piiv.config.SecurityConfig;
import br.senac.tads4.piiv.config.ServiceConfig;
import br.senac.tads4.piiv.config.WebConfig;

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class<?>[] { JpaConfig.class, SecurityConfig.class };
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class<?>[] { WebConfig.class, ServiceConfig.class, MailConfig.class };
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}

	@Override
	protected Filter[] getServletFilters() {
		return new Filter[] { };
	}

	@Override
	protected void customizeRegistration(Dynamic registration) {
		registration.setMultipartConfig(new MultipartConfigElement(""));
	}
}


text:
\section{To-do List Installation's guide}

\subsection{System Requirements}
The bubble To-do list can be used in any Browser that supports HTML5. To be able to install the To-do list Bubble, npm v4.2.0 and nodejs v7.10.0 are required. The informations on how to install them can be found by following these links:
\begin{itemize}
	\item Node.js: \url{https://nodejs.org/en/download/package-manager/};
	\item Npm: \url{https://docs.npmjs.com/cli/install}.
\end{itemize}

\subsection{Installation}
\begin{itemize}
\item Clone this repository: git clone https://github.com/or-bit/monolith-todos.git
\item Enter in it: cd monolith-todos
\item Install dependencies: npm i
\item Running example: npm start Browse http://localhost:5000 to see the result.
\end{itemize}

\section{\DemoName{} Installation's guide}

\subsection{ \DemoName{} System Requirements}
\DemoName{} can be used in any Browser that supports HTML5.
To be able to install \DemoName{} Bubble, npm v4.2.0 and nodejs v7.10.0 are required. The informations on how to install them can be found by following these links:
\begin{itemize}
	\item Node.js: \url{https://nodejs.org/en/download/package-manager/};
	\item Npm: \url{https://docs.npmjs.com/cli/install}.
\end{itemize}

\subsection{Installation}

\begin{itemize}
	\item ``npm install'';
	\item ``npm install express'';
	\item ``npm install socket.io'';
	\item ``npm install redux'';
	\item ``node packages/order-gateway/index.js'';
\end{itemize}


text:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

/*
This test is for VSW 523654.

Before the fix we were doing the following:

take loader lock
look for a MethodDesc in the hash table
didn't find it so create a new MethodDesc (MD)
add it to the hash table
release the lock

But the problem with this is that during creation of MethodDesc we were looking at the type handle and
calling managed code while holding the lock and that could potentially lead to a deadlock.

After the fix we do the following:

take loader lock
look for a MethodDesc in the hash table
didn't find it, release the lock

create a new MethodDesc

take the lock again
check that no one added the MethodDesc while we were creating it
If not there, add it to the hash table
Otherwise the MethodDesc is already in there
release the lock

There was a concern about race conditions for the scenario where we don't find the MD the first time
but find it the 2nd time.
This test calls the same method from 10 threads so that we would hit this scenario. But this code path
is only hit non-deterministically some of the time.
*/

using System;
using System.Threading;

public class A
{
	public static int i = 0;

	public void meth<T>()
	{
		Console.WriteLine(Thread.CurrentThread.Name + ": Inside meth<int>");
		++i;
	}
}

public class Test
{
	public static void RunTestThread1()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static void RunTestThread2()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static void RunTestThread3()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static void RunTestThread4()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static void RunTestThread5()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static void RunTestThread6()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static void RunTestThread7()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static void RunTestThread8()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static void RunTestThread9()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static void RunTestThread10()
	{
		A obj = new A();
		obj.meth<int>();
	}

	public static int Main()
	{

		Thread t1 = new Thread(RunTestThread1);
		t1.Name = "T1";

		Thread t2 = new Thread(RunTestThread2);
   		t2.Name = "T2";

		Thread t3 = new Thread(RunTestThread3);
		t3.Name = "T3";

		Thread t4 = new Thread(RunTestThread4);
	        t4.Name = "T4";

		Thread t5 = new Thread(RunTestThread5);
		t5.Name = "T5";

		Thread t6 = new Thread(RunTestThread6);
		t6.Name = "T6";

		Thread t7 = new Thread(RunTestThread7);
   		t7.Name = "T7";

		Thread t8 = new Thread(RunTestThread8);
		t8.Name = "T8";

		Thread t9 = new Thread(RunTestThread9);
	        t9.Name = "T9";

		Thread t10 = new Thread(RunTestThread10);
		t10.Name = "T10";

		t1.Start();
		t2.Start();
		t3.Start();
		t4.Start();
		t5.Start();

		t6.Start();
		t7.Start();
		t8.Start();
		t9.Start();
		t10.Start();

		t1.Join();
		t2.Join();
		t3.Join();
		t4.Join();
		t5.Join();

		t6.Join();
		t7.Join();
		t8.Join();
		t9.Join();
		t10.Join();

		Console.WriteLine("i should be 10");
		Console.WriteLine("i = " + A.i);

		if (A.i != 10)
		{
			Console.WriteLine("FAIL");
			return 	101;
		}
		else
		{
			Console.WriteLine("PASS");
			return 	100;
		}
	}

}


text:
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// 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.

/**
 * @fileoverview Shared tests for goog.editor.Field and ContentEditableField.
 * Since ContentEditableField switches many of the internal code paths in Field
 * (such as via usesIframe()) it's important to re-run a lot of the same tests.
 *
 * @author nicksantos@google.com (Nick Santos)
 * @author gboyer@google.com (Garrett Boyer)
 */

/** @suppress {extraProvide} */
goog.provide('goog.editor.field_test');

goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.dom.Range');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classes');
goog.require('goog.editor.BrowserFeature');
goog.require('goog.editor.Field');
goog.require('goog.editor.Plugin');
goog.require('goog.editor.range');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventType');
goog.require('goog.events.KeyCodes');
goog.require('goog.functions');
goog.require('goog.testing.LooseMock');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.dom');
goog.require('goog.testing.events');
goog.require('goog.testing.events.Event');
goog.require('goog.testing.recordFunction');
goog.require('goog.userAgent');
goog.setTestOnly('Tests for goog.editor.*Field');

/** Constructor to use for creating the field. Set by the test HTML file. */
var FieldConstructor;

/** Hard-coded HTML for the tests. */
var HTML = '
I am text.
';

function setUp() {
  goog.dom.getElement('parent').innerHTML = HTML;
  assertTrue('FieldConstructor should be set by the test HTML file',
      goog.isFunction(FieldConstructor));
}

function tearDown() {
  // NOTE(nicksantos): I think IE is blowing up on this call because
  // it is lame. It manifests its lameness by throwing an exception.
  // Kudos to XT for helping me to figure this out.
  try {
  } catch (e) {}
}

// Tests for the plugin interface.

/**
 * Dummy plugin for test usage.
 * @constructor
 * @extends {goog.editor.Plugin}
 * @final
 */
function TestPlugin() {
  this.getTrogClassId = function() {
    return 'TestPlugin';
  };

  this.handleKeyDown = goog.nullFunction;
  this.handleKeyPress = goog.nullFunction;
  this.handleKeyUp = goog.nullFunction;
  this.handleKeyboardShortcut = goog.nullFunction;
  this.isSupportedCommand = goog.nullFunction;
  this.execCommandInternal = goog.nullFunction;
  this.queryCommandValue = goog.nullFunction;
  this.activeOnUneditableFields = goog.nullFunction;
  this.handleSelectionChange = goog.nullFunction;
}
goog.inherits(TestPlugin, goog.editor.Plugin);

/**
 * Tests that calling registerPlugin will add the plugin to the
 * plugin map.
 */
function testRegisterPlugin() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();

  editableField.registerPlugin(plugin);

  assertEquals('Registered plugin must be in protected plugin map.',
      plugin, editableField.plugins_[plugin.getTrogClassId()]);
  assertEquals('Plugin has a keydown handler, should be in keydown map',
      plugin,
      editableField.indexedPlugins_[goog.editor.Plugin.Op.KEYDOWN][0]);
  assertEquals('Plugin has a keypress handler, should be in keypress map',
      plugin,
      editableField.indexedPlugins_[goog.editor.Plugin.Op.KEYPRESS][0]);
  assertEquals('Plugin has a keyup handler, should be in keuup map',
      plugin,
      editableField.indexedPlugins_[goog.editor.Plugin.Op.KEYUP][0]);
  assertEquals(
      'Plugin has a selectionchange handler, should be in selectionchange map',
      plugin,
      editableField.indexedPlugins_[goog.editor.Plugin.Op.SELECTION][0]);
  assertEquals('Plugin has a shortcut handler, should be in shortcut map',
      plugin,
      editableField.indexedPlugins_[goog.editor.Plugin.Op.SHORTCUT][0]);
  assertEquals('Plugin has a execCommand, should be in execCommand map',
      plugin,
      editableField.indexedPlugins_[goog.editor.Plugin.Op.EXEC_COMMAND][0]);
  assertEquals('Plugin has a queryCommand, should be in queryCommand map',
      plugin,
      editableField.indexedPlugins_[goog.editor.Plugin.Op.QUERY_COMMAND][0]);
  assertEquals('Plugin does not have a prepareContentsHtml,' +
      'should not be in prepareContentsHtml map',
      undefined,
      editableField.indexedPlugins_[
          goog.editor.Plugin.Op.PREPARE_CONTENTS_HTML][0]);
  assertEquals('Plugin does not have a cleanContentsDom,' +
      'should not be in cleanContentsDom map',
      undefined,
      editableField.indexedPlugins_[
          goog.editor.Plugin.Op.CLEAN_CONTENTS_DOM][0]);
  assertEquals('Plugin does not have a cleanContentsHtml,' +
      'should not be in cleanContentsHtml map',
      undefined,
      editableField.indexedPlugins_[
          goog.editor.Plugin.Op.CLEAN_CONTENTS_HTML][0]);

  editableField.dispose();
}

/**
 * Tests that calling unregisterPlugin will remove the plugin from
 * the map.
 */
function testUnregisterPlugin() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();

  editableField.registerPlugin(plugin);
  editableField.unregisterPlugin(plugin);

  assertUndefined('Unregistered plugin must not be in protected plugin map.',
      editableField.plugins_[plugin.getTrogClassId()]);

  editableField.dispose();
}

/**
 * Tests that registered plugins can be fetched by their id.
 */
function testGetPluginByClassId() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();

  assertUndefined('Must not be able to get unregistered plugins by class id.',
      editableField.getPluginByClassId(plugin.getTrogClassId()));

  editableField.registerPlugin(plugin);
  assertEquals('Must be able to get registered plugins by class id.',
      plugin, editableField.getPluginByClassId(plugin.getTrogClassId()));
  editableField.dispose();
}

/**
 * Tests that plugins get auto disposed by default when the field is disposed.
 * Tests that plugins with setAutoDispose(false) do not get disposed when the
 * field is disposed.
 */
function testDisposed_PluginAutoDispose() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();

  var noDisposePlugin = new goog.editor.Plugin();
  noDisposePlugin.getTrogClassId = function() {
    return 'noDisposeId';
  };
  noDisposePlugin.setAutoDispose(false);

  editableField.registerPlugin(plugin);
  editableField.registerPlugin(noDisposePlugin);
  editableField.dispose();
  assert(editableField.isDisposed());
  assertTrue(plugin.isDisposed());
  assertFalse(noDisposePlugin.isDisposed());
}

var STRING_KEY = String.fromCharCode(goog.events.KeyCodes.A).toLowerCase();

/**
 * @return {goog.events.Event} Returns an event for a keyboard shortcut
 * for the letter 'a'.
 */
function getBrowserEvent() {
  var e = new goog.events.BrowserEvent();
  e.ctrlKey = true;
  e.metaKey = true;
  e.charCode = goog.events.KeyCodes.A;
  return e;
}

/**
 * @param {boolean} followLinkInNewWindow Whether activating a hyperlink
 *     in the editable field will open a new window or not.
 * @return {!goog.editor.Field} Returns an editable field after its load phase.
 */
function createEditableFieldWithListeners(followLinkInNewWindow) {
  var editableField = new FieldConstructor('testField');
  editableField.setFollowLinkInNewWindow(followLinkInNewWindow);

  var originalElement = editableField.getOriginalElement();
  editableField.setupFieldObject(originalElement);
  editableField.handleFieldLoad();

  return editableField;
}

function getListenerTarget(editableField) {
  var elt = editableField.getElement();
  var listenerTarget =
      goog.editor.BrowserFeature.USE_DOCUMENT_FOR_KEY_EVENTS &&
          editableField.usesIframe() ? elt.ownerDocument : elt;
  return listenerTarget;
}

function assertClickDefaultActionIsCanceled(editableField) {
  var cancelClickDefaultActionListener = goog.events.getListener(
      getListenerTarget(editableField), goog.events.EventType.CLICK,
      goog.editor.Field.cancelLinkClick_, undefined, editableField);

  assertNotNull(cancelClickDefaultActionListener);
}

function assertClickDefaultActionIsNotCanceled(editableField) {
  var cancelClickDefaultActionListener = goog.events.getListener(
      getListenerTarget(editableField), goog.events.EventType.CLICK,
      goog.editor.Field.cancelLinkClick_, undefined, editableField);

  assertNull(cancelClickDefaultActionListener);
}

/**
 * Tests that plugins are disabled when the field is made uneditable.
 */

function testMakeUneditableDisablesPlugins() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();

  var calls = 0;
  plugin.disable = function(field) {
    assertEquals(editableField, field);
    assertTrue(field.isUneditable());
    calls++;
  };

  editableField.registerPlugin(plugin);
  editableField.makeEditable();

  assertEquals(0, calls);
  editableField.makeUneditable();

  assertEquals(1, calls);

  editableField.dispose();
}

/**
 * Test that if a browser open a new page when clicking a link in a content
 * editable element, a click listener is set to cancel this default action.
 */
function testClickDefaultActionIsCanceledWhenBrowserFollowsClick() {
  // Simulate a browser that will open a new page when activating a link in a
  // content editable element.
  var editableField =
      createEditableFieldWithListeners(true /* followLinkInNewWindow */);
  assertClickDefaultActionIsCanceled(editableField);

  editableField.dispose();
}

/**
 * Test that if a browser does not open a new page when clicking a link in a
 * content editable element, the click default action is not canceled.
 */
function testClickDefaultActionIsNotCanceledWhenBrowserDontFollowsClick() {
  // Simulate a browser that will NOT open a new page when activating a link in
  // a content editable element.
  var editableField =
      createEditableFieldWithListeners(false /* followLinkInNewWindow */);
  assertClickDefaultActionIsNotCanceled(editableField);

  editableField.dispose();
}

/**
 * Test that if a plugin registers keyup, it gets called.
 */
function testPluginKeyUp() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();

  var mockPlugin = new goog.testing.LooseMock(plugin);
  mockPlugin.getTrogClassId().$returns('mockPlugin');
  mockPlugin.registerFieldObject(editableField);
  mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin.handleKeyUp(e);
  mockPlugin.$replay();

  editableField.registerPlugin(mockPlugin);

  editableField.handleKeyUp_(e);

  mockPlugin.$verify();
}

/**
 * Test that if a plugin registers keydown, it gets called.
 */
function testPluginKeyDown() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();

  var mockPlugin = new goog.testing.LooseMock(plugin);
  mockPlugin.getTrogClassId().$returns('mockPlugin');
  mockPlugin.registerFieldObject(editableField);
  mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin.handleKeyDown(e).$returns(true);
  mockPlugin.$replay();

  editableField.registerPlugin(mockPlugin);

  editableField.handleKeyDown_(e);

  mockPlugin.$verify();
}

/**
 * Test that if a plugin registers keypress, it gets called.
 */
function testPluginKeyPress() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();

  var mockPlugin = new goog.testing.LooseMock(plugin);
  mockPlugin.getTrogClassId().$returns('mockPlugin');
  mockPlugin.registerFieldObject(editableField);
  mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin.handleKeyPress(e).$returns(true);
  mockPlugin.$replay();

  editableField.registerPlugin(mockPlugin);

  editableField.handleKeyPress_(e);

  mockPlugin.$verify();
}

/**
 * If one plugin handles a key event, the rest of the plugins do not get their
 * key handlers invoked.
 */
function testHandledKeyEvent() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();

  var mockPlugin1 = new goog.testing.LooseMock(plugin);
  mockPlugin1.getTrogClassId().$returns('mockPlugin1');
  mockPlugin1.registerFieldObject(editableField);
  mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    mockPlugin1.handleKeyDown(e).$returns(true);
  } else {
    mockPlugin1.handleKeyPress(e).$returns(true);
  }
  mockPlugin1.handleKeyUp(e).$returns(true);
  mockPlugin1.$replay();

  var mockPlugin2 = new goog.testing.LooseMock(plugin);
  mockPlugin2.getTrogClassId().$returns('mockPlugin2');
  mockPlugin2.registerFieldObject(editableField);
  mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin2.$replay();

  editableField.registerPlugin(mockPlugin1);
  editableField.registerPlugin(mockPlugin2);

  editableField.handleKeyUp_(e);
  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    editableField.handleKeyDown_(e);
  } else {
    editableField.handleKeyPress_(e);
  }

  mockPlugin1.$verify();
  mockPlugin2.$verify();
}

/**
 * Tests to make sure the cut and paste events are not dispatched immediately.
 */
function testHandleCutAndPasteEvents() {
  if (goog.editor.BrowserFeature.USE_MUTATION_EVENTS) {
    // Cut and paste events do not raise events at all in Mozilla.
    return;
  }
  var editableField = new FieldConstructor('testField');
  var clock = new goog.testing.MockClock(true);
  var delayedChanges = goog.testing.recordFunction();
  goog.events.listen(editableField, goog.editor.Field.EventType.DELAYEDCHANGE,
      delayedChanges);

  editableField.makeEditable();

  goog.testing.events.fireBrowserEvent(
      new goog.testing.events.Event('cut', editableField.getElement()));
  assertEquals('Cut event should be on a timer', 0,
      delayedChanges.getCallCount());
  clock.tick(1000);
  assertEquals('delayed change event should fire within 1s after cut', 1,
      delayedChanges.getCallCount());

  goog.testing.events.fireBrowserEvent(
      new goog.testing.events.Event('paste', editableField.getElement()));
  assertEquals('Paste event should be on a timer', 1,
      delayedChanges.getCallCount());
  clock.tick(1000);
  assertEquals('delayed change event should fire within 1s after paste', 2,
      delayedChanges.getCallCount());

  clock.dispose();
  editableField.dispose();
}

/**
 * If the first plugin does not handle the key event, the next plugin gets
 * a chance to handle it.
 */
function testNotHandledKeyEvent() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();

  var mockPlugin1 = new goog.testing.LooseMock(plugin);
  mockPlugin1.getTrogClassId().$returns('mockPlugin1');
  mockPlugin1.registerFieldObject(editableField);
  mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    mockPlugin1.handleKeyDown(e).$returns(false);
  } else {
    mockPlugin1.handleKeyPress(e).$returns(false);
  }
  mockPlugin1.handleKeyUp(e).$returns(false);
  mockPlugin1.$replay();

  var mockPlugin2 = new goog.testing.LooseMock(plugin);
  mockPlugin2.getTrogClassId().$returns('mockPlugin2');
  mockPlugin2.registerFieldObject(editableField);
  mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    mockPlugin2.handleKeyDown(e).$returns(true);
  } else {
    mockPlugin2.handleKeyPress(e).$returns(true);
  }
  mockPlugin2.handleKeyUp(e).$returns(true);
  mockPlugin2.$replay();

  editableField.registerPlugin(mockPlugin1);
  editableField.registerPlugin(mockPlugin2);

  editableField.handleKeyUp_(e);
  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    editableField.handleKeyDown_(e);
  } else {
    editableField.handleKeyPress_(e);
  }

  mockPlugin1.$verify();
  mockPlugin2.$verify();
}

/**
 * Make sure that handleKeyboardShortcut is called if other key handlers
 * return false.
 */
function testKeyboardShortcutCalled() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();

  var mockPlugin = new goog.testing.LooseMock(plugin);
  mockPlugin.getTrogClassId().$returns('mockPlugin');
  mockPlugin.registerFieldObject(editableField);
  mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    mockPlugin.handleKeyDown(e).$returns(false);
  } else {
    mockPlugin.handleKeyPress(e).$returns(false);
  }
  mockPlugin.handleKeyboardShortcut(e, STRING_KEY, true).$returns(false);
  mockPlugin.$replay();

  editableField.registerPlugin(mockPlugin);

  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    editableField.handleKeyDown_(e);
  } else {
    editableField.handleKeyPress_(e);
  }

  mockPlugin.$verify();
}

/**
 * Make sure that handleKeyboardShortcut is not called if other key handlers
 * return true.
 */
function testKeyboardShortcutNotCalled() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();

  var mockPlugin = new goog.testing.LooseMock(plugin);
  mockPlugin.getTrogClassId().$returns('mockPlugin');
  mockPlugin.registerFieldObject(editableField);
  mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    mockPlugin.handleKeyDown(e).$returns(true);
  } else {
    mockPlugin.handleKeyPress(e).$returns(true);
  }
  mockPlugin.$replay();

  editableField.registerPlugin(mockPlugin);

  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    editableField.handleKeyDown_(e);
  } else {
    editableField.handleKeyPress_(e);
  }

  mockPlugin.$verify();
}

/**
 * Make sure that handleKeyboardShortcut is not called if alt is pressed.
 * @bug 1363959
 */
function testKeyHandlingAlt() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();
  e.altKey = true;

  var mockPlugin = new goog.testing.LooseMock(plugin);
  mockPlugin.getTrogClassId().$returns('mockPlugin');
  mockPlugin.registerFieldObject(editableField);
  mockPlugin.isEnabled(editableField).$anyTimes().$returns(true);
  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    mockPlugin.handleKeyDown(e).$returns(false);
  } else {
    mockPlugin.handleKeyPress(e).$returns(false);
  }
  mockPlugin.$replay();

  editableField.registerPlugin(mockPlugin);

  if (goog.editor.BrowserFeature.USES_KEYDOWN) {
    editableField.handleKeyDown_(e);
  } else {
    editableField.handleKeyPress_(e);
  }

  mockPlugin.$verify();
}

/**
 * Test that if a plugin has an execCommand function, it gets called
 * but only for supported commands.
 */
function testPluginExecCommand() {
  var plugin = new TestPlugin();
  var passedCommand, passedArg;
  plugin.execCommand = function(command, arg) {
    passedCommand = command;
    passedArg = arg;
  };

  var editableField = new FieldConstructor('testField');
  editableField.registerPlugin(plugin);
  plugin.enable(editableField);
  plugin.isSupportedCommand = goog.functions.constant(true);

  editableField.execCommand('+indent', true);
  // Verify that the plugin's execCommand was called with the correct
  // args.
  assertEquals('+indent', passedCommand);
  assertTrue(passedArg);

  passedCommand = null;
  passedArg = null;
  plugin.isSupportedCommand = goog.functions.constant(false);

  editableField.execCommand('+outdent', false);
  // Verify that a plugin's execCommand is not called if it isn't a supported
  // command.
  assertNull(passedCommand);
  assertNull(passedArg);

  editableField.dispose();
  plugin.dispose();
}

/**
 * Test that if one plugin supports execCommand, no other plugins
 * get a chance to handle the execComand.
 */
function testSupportedExecCommand() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();

  var mockPlugin1 = new goog.testing.LooseMock(plugin);
  mockPlugin1.getTrogClassId().$returns('mockPlugin1');
  mockPlugin1.registerFieldObject(editableField);
  mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin1.isSupportedCommand('+indent').$returns(true);
  mockPlugin1.execCommandInternal('+indent').$returns(true);
  mockPlugin1.execCommand('+indent').$does(
      function() { mockPlugin1.execCommandInternal('+indent'); });
  mockPlugin1.$replay();

  var mockPlugin2 = new goog.testing.LooseMock(plugin);
  mockPlugin2.getTrogClassId().$returns('mockPlugin2');
  mockPlugin2.registerFieldObject(editableField);
  mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin2.$replay();

  editableField.registerPlugin(mockPlugin1);
  editableField.registerPlugin(mockPlugin2);

  editableField.execCommand('+indent');

  mockPlugin1.$verify();
  mockPlugin2.$verify();
}

/**
 * Test that if the first plugin does not support execCommand, the other
 * plugins get a chance to handle the execCommand.
 */
function testNotSupportedExecCommand() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();

  var mockPlugin1 = new goog.testing.LooseMock(plugin);
  mockPlugin1.getTrogClassId().$returns('mockPlugin1');
  mockPlugin1.registerFieldObject(editableField);
  mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin1.isSupportedCommand('+indent').$returns(false);
  mockPlugin1.$replay();

  var mockPlugin2 = new goog.testing.LooseMock(plugin);
  mockPlugin2.getTrogClassId().$returns('mockPlugin2');
  mockPlugin2.registerFieldObject(editableField);
  mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin2.isSupportedCommand('+indent').$returns(true);
  mockPlugin2.execCommandInternal('+indent').$returns(true);
  mockPlugin2.execCommand('+indent').$does(
      function() { mockPlugin2.execCommandInternal('+indent'); });
  mockPlugin2.$replay();

  editableField.registerPlugin(mockPlugin1);
  editableField.registerPlugin(mockPlugin2);

  editableField.execCommand('+indent');

  mockPlugin1.$verify();
  mockPlugin2.$verify();
}

/**
 * Tests that if a plugin supports a command that its queryCommandValue
 * gets called and no further plugins can handle the queryCommandValue.
 */
function testSupportedQueryCommand() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();

  var mockPlugin1 = new goog.testing.LooseMock(plugin);
  mockPlugin1.getTrogClassId().$returns('mockPlugin1');
  mockPlugin1.registerFieldObject(editableField);
  mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin1.isSupportedCommand('+indent').$returns(true);
  mockPlugin1.queryCommandValue('+indent').$returns(true);
  mockPlugin1.activeOnUneditableFields().$returns(true);
  mockPlugin1.$replay();

  var mockPlugin2 = new goog.testing.LooseMock(plugin);
  mockPlugin2.getTrogClassId().$returns('mockPlugin2');
  mockPlugin2.registerFieldObject(editableField);
  mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin2.$replay();

  editableField.registerPlugin(mockPlugin1);
  editableField.registerPlugin(mockPlugin2);

  editableField.queryCommandValue('+indent');

  mockPlugin1.$verify();
  mockPlugin2.$verify();
}

/**
 * Tests that if the first plugin does not support a command that its
 * queryCommandValue do not get called and the next plugin can handle the
 * queryCommandValue.
 */
function testNotSupportedQueryCommand() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();

  var mockPlugin1 = new goog.testing.LooseMock(plugin);
  mockPlugin1.getTrogClassId().$returns('mockPlugin1');
  mockPlugin1.registerFieldObject(editableField);
  mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin1.isSupportedCommand('+indent').$returns(false);
  mockPlugin1.$replay();

  var mockPlugin2 = new goog.testing.LooseMock(plugin);
  mockPlugin2.getTrogClassId().$returns('mockPlugin2');
  mockPlugin2.registerFieldObject(editableField);
  mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin2.isSupportedCommand('+indent').$returns(true);
  mockPlugin2.queryCommandValue('+indent').$returns(true);
  mockPlugin2.activeOnUneditableFields().$returns(true);
  mockPlugin2.$replay();

  editableField.registerPlugin(mockPlugin1);
  editableField.registerPlugin(mockPlugin2);

  editableField.queryCommandValue('+indent');

  mockPlugin1.$verify();
  mockPlugin2.$verify();
}

/**
 * Tests that if a plugin handles selectionChange that it gets called and
 * no further plugins can handle the selectionChange.
 */
function testHandledSelectionChange() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();

  var mockPlugin1 = new goog.testing.LooseMock(plugin);
  mockPlugin1.getTrogClassId().$returns('mockPlugin1');
  mockPlugin1.registerFieldObject(editableField);
  mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin1.handleSelectionChange(e, undefined).$returns(true);
  mockPlugin1.$replay();

  var mockPlugin2 = new goog.testing.LooseMock(plugin);
  mockPlugin2.getTrogClassId().$returns('mockPlugin2');
  mockPlugin2.registerFieldObject(editableField);
  mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin2.$replay();

  editableField.registerPlugin(mockPlugin1);
  editableField.registerPlugin(mockPlugin2);

  editableField.dispatchSelectionChangeEvent(e);

  mockPlugin1.$verify();
  mockPlugin2.$verify();
}

/**
 * Tests that if the first plugin does not handle selectionChange that
 * the next plugin gets a chance to handle it.
 */
function testNotHandledSelectionChange() {
  var editableField = new FieldConstructor('testField');
  var plugin = new TestPlugin();
  var e = getBrowserEvent();

  var mockPlugin1 = new goog.testing.LooseMock(plugin);
  mockPlugin1.getTrogClassId().$returns('mockPlugin1');
  mockPlugin1.registerFieldObject(editableField);
  mockPlugin1.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin1.handleSelectionChange(e, undefined).$returns(false);
  mockPlugin1.$replay();

  var mockPlugin2 = new goog.testing.LooseMock(plugin);
  mockPlugin2.getTrogClassId().$returns('mockPlugin2');
  mockPlugin2.registerFieldObject(editableField);
  mockPlugin2.isEnabled(editableField).$anyTimes().$returns(true);
  mockPlugin2.handleSelectionChange(e, undefined).$returns(true);
  mockPlugin2.$replay();

  editableField.registerPlugin(mockPlugin1);
  editableField.registerPlugin(mockPlugin2);

  editableField.dispatchSelectionChangeEvent(e);

  mockPlugin1.$verify();
  mockPlugin2.$verify();
}

// Tests for goog.editor.Field internals.

function testSelectionChange() {
  var editableField = new FieldConstructor('testField', document);
  var clock = new goog.testing.MockClock(true);
  var beforeSelectionChanges = goog.testing.recordFunction();
  goog.events.listen(editableField,
      goog.editor.Field.EventType.BEFORESELECTIONCHANGE,
      beforeSelectionChanges);
  var selectionChanges = goog.testing.recordFunction();
  goog.events.listen(editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
      selectionChanges);

  editableField.makeEditable();

  // Emulate pressing left arrow key, this should result in a
  // BEFORESELECTIONCHANGE event immediately, and a SELECTIONCHANGE event after
  // a short timeout.
  editableField.handleKeyUp_({keyCode: goog.events.KeyCodes.LEFT});
  assertEquals('Before selection change should fire immediately', 1,
      beforeSelectionChanges.getCallCount());
  assertEquals('Selection change should be on a timer', 0,
      selectionChanges.getCallCount());
  clock.tick(1000);
  assertEquals('Selection change should fire within 1s', 1,
      selectionChanges.getCallCount());

  // Programically place cursor at start. SELECTIONCHANGE event should be fired.
  editableField.placeCursorAtStart();
  assertEquals('Selection change should fire', 2,
      selectionChanges.getCallCount());

  clock.dispose();
  editableField.dispose();
}

function testSelectionChangeOnMouseUp() {
  var fakeEvent =
      new goog.events.BrowserEvent({type: 'mouseup', target: 'fakeTarget'});
  var editableField = new FieldConstructor('testField', document);
  var clock = new goog.testing.MockClock(true);
  var beforeSelectionChanges = goog.testing.recordFunction();
  goog.events.listen(editableField,
      goog.editor.Field.EventType.BEFORESELECTIONCHANGE,
      beforeSelectionChanges);
  var selectionChanges = goog.testing.recordFunction();
  goog.events.listen(editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
      selectionChanges);

  var plugin = new TestPlugin();
  plugin.handleSelectionChange = goog.testing.recordFunction();
  editableField.registerPlugin(plugin);

  editableField.makeEditable();

  // Emulate a mouseup event, this should result in immediate
  // BEFORESELECTIONCHANGE and SELECTIONCHANGE, plus a second SELECTIONCHANGE in
  // IE after a short timeout.
  editableField.handleMouseUp_(fakeEvent);
  assertEquals('Before selection change should fire immediately', 1,
      beforeSelectionChanges.getCallCount());
  assertEquals('Selection change should fire immediately', 1,
      selectionChanges.getCallCount());
  assertEquals('Plugin should have handled selection change immediately', 1,
      plugin.handleSelectionChange.getCallCount());
  assertEquals('Plugin should have received original browser event to handle',
      fakeEvent, plugin.handleSelectionChange.getLastCall().getArguments()[0]);

  // Pretend another plugin fired a SELECTIONCHANGE in the meantime.
  editableField.dispatchSelectionChangeEvent();
  assertEquals('Second selection change should fire immediately', 2,
      selectionChanges.getCallCount());
  assertEquals('Plugin should have handled second selection change immediately',
      2, plugin.handleSelectionChange.getCallCount());
  var args = plugin.handleSelectionChange.getLastCall().getArguments();
  assertTrue('Plugin should not have received data from extra firing',
      args.length == 0 ||
      !args[0] && (args.length == 1 || !args[1]));

  // Now check for the extra call in IE.
  clock.tick(1000);
  if (goog.userAgent.IE) {
    assertEquals('Additional selection change should fire within 1s', 3,
        selectionChanges.getCallCount());
    assertEquals('Plugin should have handled selection change within 1s', 3,
        plugin.handleSelectionChange.getCallCount());
    assertEquals('Plugin should have received target of original browser event',
        fakeEvent.target,
        plugin.handleSelectionChange.getLastCall().getArguments().pop());
  } else {
    assertEquals('No additional selection change should fire', 2,
        selectionChanges.getCallCount());
    assertEquals('Plugin should not have handled selection change again', 2,
        plugin.handleSelectionChange.getCallCount());
  }

  clock.dispose();
  editableField.dispose();
}

function testSelectionChangeBeforeUneditable() {
  var editableField = new FieldConstructor('testField', document);
  var clock = new goog.testing.MockClock(true);
  var selectionChanges = goog.testing.recordFunction();
  goog.events.listen(editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
      selectionChanges);

  editableField.makeEditable();
  editableField.handleKeyUp_({keyCode: goog.events.KeyCodes.LEFT});
  assertEquals('Selection change should be on a timer', 0,
      selectionChanges.getCallCount());
  editableField.makeUneditable();
  assertEquals('Selection change should fire during make uneditable', 1,
      selectionChanges.getCallCount());
  clock.tick(1000);
  assertEquals('No additional selection change should fire', 1,
      selectionChanges.getCallCount());

  clock.dispose();
  editableField.dispose();
}

function testGetEditableDomHelper() {
  var editableField = new FieldConstructor('testField', document);
  assertNull('Before being made editable, we do not know the dom helper',
      editableField.getEditableDomHelper());
  editableField.makeEditable();
  assertNotNull('After being made editable, we know the dom helper',
      editableField.getEditableDomHelper());
  assertEquals('Document from domHelper should be the editable elements doc',
      goog.dom.getOwnerDocument(editableField.getElement()),
      editableField.getEditableDomHelper().getDocument());
  editableField.dispose();
}

function testQueryCommandValue() {
  var editableField = new FieldConstructor('testField', document);
  assertFalse(editableField.queryCommandValue('boo'));
  assertObjectEquals({'boo': false, 'aieee': false},
      editableField.queryCommandValue(['boo', 'aieee']));

  editableField.makeEditable();
  assertFalse(editableField.queryCommandValue('boo'));

  focusFieldSync(editableField);
  assertNull(editableField.queryCommandValue('boo'));
  assertObjectEquals({'boo': null, 'aieee': null},
      editableField.queryCommandValue(['boo', 'aieee']));
  editableField.dispose();
}

function focusFieldSync(field) {
  field.focus();

  // IE fires focus events async, so create a fake focus event
  // synchronously.
  goog.testing.events.fireFocusEvent(field.getElement());
}

function testSetHtml() {
  var editableField = new FieldConstructor('testField', document);
  var clock = new goog.testing.MockClock(true);

  try {
    var delayedChangeCalled = false;
    goog.events.listen(editableField, goog.editor.Field.EventType.DELAYEDCHANGE,
        function() {
          delayedChangeCalled = true;
        });

    editableField.makeEditable();
    clock.tick(1000);
    assertFalse('Make editable must not fire delayed change.',
        delayedChangeCalled);

    editableField.setHtml(false, 'bar', true /* Don't fire delayed change */);
    goog.testing.dom.assertHtmlContentsMatch('bar', editableField.getElement());
    clock.tick(1000);
    assertFalse('setHtml must not fire delayed change if so configured.',
        delayedChangeCalled);

    editableField.setHtml(false, 'foo', false /* Fire delayed change */);
    goog.testing.dom.assertHtmlContentsMatch('foo', editableField.getElement());
    clock.tick(1000);
    assertTrue('setHtml must fire delayed change by default',
        delayedChangeCalled);
  } finally {
    clock.dispose();
    editableField.dispose();
  }
}

/**
 * Helper to test that the cursor is placed at the beginning of the editable
 * field's contents.
 * @param {string=} opt_html Html to replace the test file default field
 *     contents with.
 * @param {string=} opt_parentId Id of the parent of the node where the cursor
 *     is expected to be placed. If omitted, will expect cursor to be placed in
 *     the first child of the field element (or, if the field has no content, in
 *     the field element itself).
 */
function doTestPlaceCursorAtStart(opt_html, opt_parentId) {
  var editableField = new FieldConstructor('testField', document);
  editableField.makeEditable();
  // Initially place selection not at the start of the editable field.
  var textNode = editableField.getElement().firstChild;
  goog.dom.Range.createFromNodes(textNode, 1, textNode, 2).select();
  if (opt_html != null) {
    editableField.getElement().innerHTML = opt_html;
  }

  editableField.placeCursorAtStart();
  var range = editableField.getRange();
  assertNotNull(
      'Placing the cursor should result in a range object being available',
      range);
  assertTrue('The range should be collapsed', range.isCollapsed());
  textNode = editableField.getElement().firstChild;

  // We check whether getAttribute exist because textNode may be an actual
  // TextNode, which does not have getAttribute.
  if (textNode && textNode.getAttribute &&
      textNode.getAttribute('_moz_editor_bogus_node')) {
    // At least in FF >= 6, assigning '' to innerHTML of a contentEditable
    // element will results in textNode being modified into:
    //
 instead of nulling
    // it. So we should null it ourself.
    textNode = null;
  }

  var startNode = opt_parentId ?
      editableField.getEditableDomHelper().getElement(opt_parentId).firstChild :
      textNode ? textNode : editableField.getElement();
  if (goog.userAgent.WEBKIT &&
      !goog.userAgent.isVersionOrHigher('528')) {
    // Safari 3 seems to normalize the selection to the shallowest endpoint (in
    // this case the editable element) in all cases tested below. This is OK
    // because when you start typing it magically inserts the text at the
    // deepest endpoint, and even behaves as desired in the case tested by
    // testPlaceCursorAtStartNonImportantTextNode.
    startNode = editableField.getElement();
  }
  assertEquals('The range should start at the specified expected node',
      startNode, range.getStartNode());
  assertEquals('The range should start at the beginning of the node',
      0, range.getStartOffset());
}

/**
 * Verify that restoreSavedRange() restores the range and sets the focus.
 */
function testRestoreSavedRange() {
  var editableField = new FieldConstructor('testField', document);
  editableField.makeEditable();

  // Create another node to take the focus later.
  var doc = goog.dom.getOwnerDocument(editableField.getElement());
  var otherElem = doc.createElement(goog.dom.TagName.DIV);
  otherElem.tabIndex = '1';  // Make it focusable.
  editableField.getElement().parentNode.appendChild(otherElem);

  // Initially place selection not at the start of the editable field.
  var textNode = editableField.getElement().firstChild;
  var range = goog.dom.Range.createFromNodes(textNode, 1, textNode, 2);
  range.select();
  var savedRange = goog.editor.range.saveUsingNormalizedCarets(range);

  // Change range to be a simple cursor at start, and move focus away.
  editableField.placeCursorAtStart();
  otherElem.focus();

  editableField.restoreSavedRange(savedRange);

  // Verify that we have focus and the range is restored.
  assertEquals('Field should be focused',
      editableField.getElement(),
      goog.dom.getActiveElement(doc));
  var newRange = editableField.getRange();
  assertEquals('Range startNode', textNode, newRange.getStartNode());
  assertEquals('Range startOffset', 1, newRange.getStartOffset());
  assertEquals('Range endNode', textNode, newRange.getEndNode());
  assertEquals('Range endOffset', 2, newRange.getEndOffset());

}

function testPlaceCursorAtStart() {
  doTestPlaceCursorAtStart();
}

function testPlaceCursorAtStartEmptyField() {
  doTestPlaceCursorAtStart('');
}

function testPlaceCursorAtStartNonImportantTextNode() {
  doTestPlaceCursorAtStart(
      '\nimportant text', 'important');
}

/**
 * Helper to test that the cursor is placed at the beginning of the editable
 * field's contents.
 * @param {string=} opt_html Html to replace the test file default field
 *     contents with.
 * @param {string=} opt_parentId Id of the parent of the node where the cursor
 *     is expected to be placed. If omitted, will expect cursor to be placed in
 *     the first child of the field element (or, if the field has no content, in
 *     the field element itself).
 * @param {number=} opt_offset The offset to expect for the end position.
 */
function doTestPlaceCursorAtEnd(opt_html, opt_parentId, opt_offset) {
  var editableField = new FieldConstructor('testField', document);
  editableField.makeEditable();

  // Initially place selection not at the end of the editable field.
  var textNode = editableField.getElement().firstChild;
  goog.dom.Range.createFromNodes(textNode, 0, textNode, 1).select();
  if (opt_html != null) {
    editableField.getElement().innerHTML = opt_html;
  }

  editableField.placeCursorAtEnd();
  var range = editableField.getRange();
  assertNotNull(
      'Placing the cursor should result in a range object being available',
      range);
  assertTrue('The range should be collapsed', range.isCollapsed());
  textNode = editableField.getElement().firstChild;

  // We check whether getAttribute exist because textNode may be an actual
  // TextNode, which does not have getAttribute.

  var hasBogusNode = textNode && textNode.getAttribute &&
                     textNode.getAttribute('_moz_editor_bogus_node');
  if (hasBogusNode) {
    // At least in FF >= 6, assigning '' to innerHTML of a contentEditable
    // element will results in textNode being modified into:
    //
 instead of nulling
    // it. So we should null it ourself.
    textNode = null;
  }

  var endNode = opt_parentId ?
      editableField.getEditableDomHelper().getElement(opt_parentId).lastChild :
      textNode ? textNode : editableField.getElement();
  assertEquals('The range should end at the specified expected node',
      endNode, range.getEndNode());
  var offset = goog.isDefAndNotNull(opt_offset) ?
      opt_offset :
      textNode ? endNode.nodeValue.length : endNode.childNodes.length - 1;
  if (hasBogusNode) {
    assertEquals('The range should end at the ending of the bogus node ' +
                 'added by FF', offset + 1, range.getEndOffset());
  } else {
    assertEquals('The range should end at the ending of the node',
        offset, range.getEndOffset());
  }
}

function testPlaceCursorAtEnd() {
  doTestPlaceCursorAtEnd();
}

function testPlaceCursorAtEndEmptyField() {
  doTestPlaceCursorAtEnd('', null, 0);
}

function testPlaceCursorAtEndNonImportantTextNode() {
  doTestPlaceCursorAtStart(
      '\nimportant text', 'important');
}

// Tests related to change/delayed change events.

function testClearDelayedChange() {
  var clock = new goog.testing.MockClock(true);
  var editableField = new FieldConstructor('testField', document);
  editableField.makeEditable();

  var delayedChangeCalled = false;
  goog.events.listen(editableField, goog.editor.Field.EventType.DELAYEDCHANGE,
      function() {
        delayedChangeCalled = true;
      });

  // Clears delayed change timer.
  editableField.delayedChangeTimer_.start();
  editableField.clearDelayedChange();
  assertTrue(delayedChangeCalled);
  if (editableField.changeTimerGecko_) {
    assertFalse(editableField.changeTimerGecko_.isActive());
  }
  assertFalse(editableField.delayedChangeTimer_.isActive());

  // Clears delayed changes caused by changeTimerGecko_
  if (editableField.changeTimerGecko_) {
    delayedChangeCalled = false;
    editableField.changeTimerGecko_.start();
    editableField.clearDelayedChange();
    assertTrue(delayedChangeCalled);
    if (editableField.changeTimerGecko_) {
      assertFalse(editableField.changeTimerGecko_.isActive());
    }
    assertFalse(editableField.delayedChangeTimer_.isActive());
  }
  clock.dispose();
}

function testHandleChange() {
  if (goog.editor.BrowserFeature.USE_MUTATION_EVENTS) {
    var editableField = new FieldConstructor('testField', document);
    editableField.makeEditable();

    editableField.changeTimerGecko_.start();
    editableField.handleChange();
    assertFalse(editableField.changeTimerGecko_.isActive());
  }
}

function testDispatchDelayedChange() {
  var editableField = new FieldConstructor('testField', document);
  editableField.makeEditable();

  editableField.delayedChangeTimer_.start();
  editableField.dispatchDelayedChange_();
  assertFalse(editableField.delayedChangeTimer_.isActive());
}

function testHandleWindowLevelMouseUp() {
  var editableField = new FieldConstructor('testField', document);
  if (editableField.usesIframe()) {
    // Only run this test if the editor does not use an iframe.
    return;
  }
  editableField.setUseWindowMouseUp(true);
  editableField.makeEditable();
  var selectionHasFired = false;
  goog.events.listenOnce(
      editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
      function(e) {
        selectionHasFired = true;
      });
  var editableElement = editableField.getElement();
  var otherElement = goog.dom.createDom(goog.dom.TagName.DIV);
  goog.dom.insertSiblingAfter(otherElement, document.body.lastChild);

  goog.testing.events.fireMouseDownEvent(editableElement);
  assertFalse(selectionHasFired);
  goog.testing.events.fireMouseUpEvent(otherElement);
  assertTrue(selectionHasFired);
}

function testNoHandleWindowLevelMouseUp() {
  var editableField = new FieldConstructor('testField', document);
  editableField.setUseWindowMouseUp(false);
  editableField.makeEditable();
  var selectionHasFired = false;
  goog.events.listenOnce(
      editableField, goog.editor.Field.EventType.SELECTIONCHANGE,
      function(e) {
        selectionHasFired = true;
      });
  var editableElement = editableField.getElement();
  var otherElement = goog.dom.createDom(goog.dom.TagName.DIV);
  goog.dom.insertSiblingAfter(otherElement, document.body.lastChild);

  goog.testing.events.fireMouseDownEvent(editableElement);
  assertFalse(selectionHasFired);
  goog.testing.events.fireMouseUpEvent(otherElement);
  assertFalse(selectionHasFired);
}

function testIsGeneratingKey() {
  var regularKeyEvent = new goog.events.BrowserEvent();
  regularKeyEvent.charCode = goog.events.KeyCodes.A;

  var ctrlKeyEvent = new goog.events.BrowserEvent();
  ctrlKeyEvent.ctrlKey = true;
  ctrlKeyEvent.metaKey = true;
  ctrlKeyEvent.charCode = goog.events.KeyCodes.A;

  var imeKeyEvent = new goog.events.BrowserEvent();
  imeKeyEvent.keyCode = 229; // indicates from an IME - see KEYS_CAUSING_CHANGES

  assertTrue(goog.editor.Field.isGeneratingKey_(regularKeyEvent, true));
  assertFalse(goog.editor.Field.isGeneratingKey_(ctrlKeyEvent, true));
  if (goog.userAgent.WINDOWS && !goog.userAgent.GECKO) {
    assertTrue(goog.editor.Field.isGeneratingKey_(imeKeyEvent, false));
  } else {
    assertFalse(goog.editor.Field.isGeneratingKey_(imeKeyEvent, false));
  }
}

function testSetEditableClassName() {
  var element = goog.dom.getElement('testField');
  var editableField = new FieldConstructor('testField');

  assertFalse(goog.dom.classes.has(element, 'editable'));
  editableField.makeEditable();
  assertTrue(goog.dom.classes.has(element, 'editable'));
  assertEquals(1, goog.array.count(
      goog.dom.classes.get(element), goog.functions.equalTo('editable')));

  // Skip restore won't reset the original element's CSS classes.
  editableField.makeUneditable(true /* opt_skipRestore */);

  editableField.makeEditable();
  assertTrue(goog.dom.classes.has(element, 'editable'));
  assertEquals(1, goog.array.count(
      goog.dom.classes.get(element), goog.functions.equalTo('editable')));
}


text:
/** api: (define)
 *  module = ParkingManager
 *  class = AssetEditorPopup
 *  extends = GeoExt.Popup
 */

/** api: constructor
 *  .. class:: AssetEditorPopup(config)
 *
 *      Create a new popup which displays forms for editing parking space
 *      attributes.
 */
Ext.namespace("ParkingManager");
ParkingManager.AssetEditorPopup = Ext.extend(GeoExt.Popup, {

    /** i18n **/
    closeMsgTitle: 'Save Changes?',
    closeMsg: 'This feature has unsaved changes. Would you like to save your changes?',
    deleteMsgTitle: 'Delete Feature?',
    deleteMsg: 'Are you sure you want to delete this feature?',
    editButtonText: 'Edit',
    editButtonTooltip: 'Make this feature editable',
    deleteButtonText: 'Delete',
    deleteButtonTooltip: 'Delete this feature',
    cancelButtonText: 'Cancel',
    cancelButtonTooltip: 'Stop editing, discard changes',
    saveButtonText: 'Save',
    saveButtonTooltip: 'Save changes',

    /** private config overrides **/
    layout: "fit",

    /** api: config[feature]
     *  ``GeoExt.data.FeatureRecord``|``OpenLayers.Feature.Vector``
     *  The feature to edit and display.
     */

    /** api: config[vertexRenderIntent]
     *  ``String`` renderIntent for feature vertices when modifying. Undefined
     *  by default.
     */

    /** api: config[streetViewTitle]
     *  ``String``
     *  Title for street view tab (i18n).
     */
    streetViewTitle: "Street View",

    /** api: config[attributeFormTitle]
     *  ``String``
     *  Title for attribute form tab (i18n).
     */
    attributeFormTitle: "Space Attributes",

    /** api: config[headingAttribute]
     *  ``String``
     *  Optional feature attribute name with heading information.  Values should
     *  be degrees clockwise relative to north.  If present, this value will be
     *  used to orient the camera in the street view.
     */
    headingAttribute: "ORIENTATION",

    /** api: property[feature]
     *  ``OpenLayers.Feature.Vector`` The feature being edited/displayed.
     */
    feature: null,

    /** api: config[schema]
     *  ``GeoExt.data.AttributeStore`` Optional. If provided, available
     *  feature attributes will be determined from the schema instead of using
     *  the attributes that the feature has currently set.
     */
    schema: null,

    /** api: config[spaceIdField]
     *  ``String`` The field name to use in the externalUrl substitution
     *  for {space_id}.
     */
    spaceIdField: null,

    /** api: config[refreshLayerName]
     *  ``String`` The WMS layer name to refresh after this popup is closed.
     */
    refreshLayerName: null,

    /** api: config[externalUrl]
     * ``String`` The url to the external form application to handle the editing
     * further. There can be placeholders in this url which will be substituted
     * by the application with the actual values corresponding to the feature
     * which is being edited, these are: {space_id}, {latitude} and {longitude}.
     */
    externalUrl: null,

    /** api: config[formReadOnly]
     *  ``Boolean`` Set to true to have the form be read-only. Default is false.
     */
    formReadOnly: false,

    /** api: config[readOnly]
     *  ``Boolean`` Set to true to disable editing. Default is false.
     */
    readOnly: false,

    /** api: config[allowDelete]
     *  ``Boolean`` Set to true to provide a Delete button for deleting the
     *  feature. Default is false.
     */
    allowDelete: false,

    /** api: config[editing]
     *  ``Boolean`` Set to true to open the popup in editing mode.
     *  Default is false.
     */

    /** private: property[editing]
     *  ``Boolean`` If we are in editing mode, this will be true.
     */
    editing: true,

    /** private: property[modifyControl]
     *  ``OpenLayers.Control.ModifyFeature`` If in editing mode, we will have
     *  this control for editing the geometry.
     */
    modifyControl: null,

    /** private: property[geometry]
     *  ``OpenLayers.Geometry`` The original geometry of the feature we are
     *  editing.
     */
    geometry: null,

    /** private: property[attributes]
     *  ``Object`` The original attributes of the feature we are editing.
     */
    attributes: null,

    /** private: property[cancelButton]
     *  ``Ext.Button``
     */
    cancelButton: null,

    /** private: property[saveButton]
     *  ``Ext.Button``
     */
    saveButton: null,

    /** private: property[editButton]
     *  ``Ext.Button``
     */
    editButton: null,

    /** private: property[deleteButton]
     *  ``Ext.Button``
     */
    deleteButton: null,

    /** private: method[initComponent]
     */
    initComponent: function() {
        this.addEvents(
            /** api: events[featuremodified]
             *  Fires when the feature associated with this popup has been
             *  modified (i.e. when the user clicks "Save" on the popup) or
             *  deleted (i.e. when the user clicks "Delete" on the popup).
             *
             *  Listener arguments:
             *  * panel - :class:`ParkingManager.AssetEditorPopup` This popup.
             *  * feature - ``OpenLayers.Feature`` The modified feature.
             */
            "featuremodified",

            /** api: events[canceledit]
             *  Fires when the user exits the editing mode by pressing the
             *  "Cancel" button or selecting "No" in the popup's close dialog.
             *
             *  Listener arguments:
             *  * panel - :class:`ParkingManager.AssetEditorPopup` This popup.
             *  * feature - ``OpenLayers.Feature`` The feature. Will be null
             *    if editing of a feature that was just inserted was cancelled.
             */
            "canceledit",

            /** api: events[cancelclose]
             *  Fires when the user answers "Cancel" to the dialog that
             *  appears when a popup with unsaved changes is closed.
             *
             *  Listener arguments:
             *  * panel - :class:`ParkingManager.AssetEditorPopup` This popup.
             */
            "cancelclose"
        );

        if (this.feature instanceof GeoExt.data.FeatureRecord) {
            this.feature = this.feature.getFeature();
        }

        var feature = this.feature;
        if (!this.location) {
            this.location = feature
        };

        if (this.schema) {
            var attributes = {};
            this.schema.each(function(r) {
                var name = r.get("name");
                var type = r.get("type");
                if (type.match(/^[^:]*:?((Multi)?(Point|Line|Polygon|Curve|Surface|Geometry))/)) {
                    // exclude gml geometries
                    return;
                }
                attributes[name] = feature.attributes[name];
            }, this);
            feature.attributes = attributes;
        }

        this.anchored = !this.editing;

        if (!this.title && feature.fid) {
            this.title = feature.fid;
        }

        this.editButton = new Ext.Button({
            text: this.editButtonText,
            tooltip: this.editButtonTooltip,
            iconCls: "edit",
            handler: this.startEditing,
            scope: this
        });

        this.deleteButton = new Ext.Button({
            text: this.deleteButtonText,
            tooltip: this.deleteButtonTooltip,
            iconCls: "delete",
            hidden: !this.allowDelete,
            handler: this.deleteFeature,
            scope: this
        });

        this.cancelButton = new Ext.Button({
            text: this.cancelButtonText,
            tooltip: this.cancelButtonTooltip,
            iconCls: "cancel",
            hidden: true,
            handler: function() {
                this.stopEditing(false);
            },
            scope: this
        });

        this.saveButton = new Ext.Button({
            text: this.saveButtonText,
            tooltip: this.saveButtonTooltip,
            iconCls: "save",
            hidden: true,
            handler: this.showExternalForm,
            scope: this
        });

        if (this.feature.state === OpenLayers.State.INSERT) {
            this.readOnly = true;
            this.formContainer = new Ext.Panel({layout: 'fit',
                html: this.getIframeHTML(), title: this.attributeFormTitle});
        } else {
            this.initAttributeForm();
            this.formContainer = new Ext.Panel({layout: 'fit',
                title: this.attributeFormTitle, items:[this.attributeForm]});
        }
        this.items = [{
            xtype: "tabpanel",
            border: false,
            activeTab: 0,
            items: [this.formContainer, {
                xtype: "gxp_googlestreetviewpanel",
                title: this.streetViewTitle,
                heading: this.getOrientationForFeature(feature),
                zoom: 1
            }]
        }];

        this.bbar = new Ext.Toolbar({
            hidden: this.readOnly,
            items: [
                this.editButton,
                this.deleteButton,
                this.saveButton,
                this.cancelButton
            ]
        });

        ParkingManager.AssetEditorPopup.superclass.initComponent.call(this);

        this.on({
            "show": function() {
                if (this.editing) {
                    this.editing = null;
                    this.startEditing();
                }
            },
            "close": function() {
                if (this.refreshLayerName != null) {
                    for (var i=0, ii=this.map.layers.length; i<ii; i++) {
                        var layer = this.map.layers[i];
                        if (layer instanceof OpenLayers.Layer.WMS &&
                            layer.params.LAYERS.indexOf(this.refreshLayerName) >= 0) {
                                layer.redraw(true);
                                break;
                        }
                    }
                }
            },
            "beforeclose": function() {
                if (!this.editing) {
                    return;
                }
                if (this.feature.state === this.getDirtyState()) {
                    Ext.Msg.show({
                        title: this.closeMsgTitle,
                        msg: this.closeMsg,
                        buttons: Ext.Msg.YESNOCANCEL,
                        fn: function(button) {
                            if (button && button !== "cancel") {
                                this.stopEditing(button === "yes");
                                this.close();
                            } else {
                                this.fireEvent("cancelclose", this);
                            }
                        },
                        scope: this,
                        icon: Ext.MessageBox.QUESTION,
                        animEl: this.getEl()
                    });
                    return false;
                } else {
                    this.stopEditing(false);
                }
            },
            scope: this
        });
    },

    /** private: method[getIframeHTML]
     *  Get the HTML for the iframe to show the external form application.
     */
    getIframeHTML: function() {
        var url = this.externalUrl;
        if (url && this.feature) {
            var space_id = this.feature.attributes[this.spaceIdField] || "";
            var template = new Ext.Template(url);
            var point = this.feature.geometry.clone().transform(
                this.feature.layer.map.getProjectionObject(),
                new OpenLayers.Projection("EPSG:4326")
            );
            var url = template.applyTemplate({
                space_id: space_id,
                latitude: point.y.toFixed(10),
                longitude: point.x.toFixed(10)
            });
            var html = '<iframe width="100%" height="100%" src="'+url+'" style="border: none;"></iframe>';
            return html;
        }
    },

    /** private: method[showExternalForm]
     *  Show the external form for editing.
     */
    showExternalForm: function() {
        this.saveButton.hide();
        this.cancelButton.hide();
        var html = this.getIframeHTML();
        if (html) {
            this.formContainer.removeAll();
            this.formContainer.body.update(html);
            //TODO remove the line below when
            // http://trac.openlayers.org/ticket/2210 is fixed.
            this.modifyControl.deactivate();
            this.modifyControl.destroy();
            this.editing = false;
       }
    },

    /** private: method[initAttributeForm]
     *  Generate form for editing parking space attributes.
     */
    initAttributeForm: function() {
        var attributes = this.feature.attributes;
        this.attributeForm = new Ext.FormPanel({
            bodyStyle: "padding: 5px 5px 0",
            labelWidth: 100,
            defaults: {anchor: "98%", readOnly: this.formReadOnly},
            autoScroll: true,
            items: [{
                xtype: "textfield",
                name: "POST_ID",
                fieldLabel: "Post ID",
                regex: /^\d{3}-\d{5}$/,
                regexText: "ID must be of the form 123-45678",
                value: attributes.POST_ID
            }, {
                xtype: "combo",
                name: "CAP_COLOR",
                fieldLabel: "Cap Color",
                store: ["Gray", "Orange", "Red"],
                displayField: "field1",
                valueField: "field1",
                editable: false,
                value: attributes.CAP_COLOR
            }, {
                xtype: "textfield", // use "datefield" if you want to allow editing
                name: "CREATED_DT",
                fieldLabel: "Created Date",
                readOnly: true,
                value: attributes.CREATED_DT || (new Date()).format("Y-m-d")
            }, {
                xtype: "textfield", // use "datefield" if you want to allow editing
                name: "LAST_UPD_DT",
                fieldLabel: "Modified Date",
                readOnly: true,
                value: (new Date()).format("Y-m-d")
            }, {
                xtype: "combo",
                name: "SENSOR_FLAG",
                fieldLabel: "Sensor Flag",
                store: ["Y", "N"],
                displayField: "field1",
                valueField: "field1",
                editable: false,
                value: attributes.SENSOR_FLAG
            }, {
                xtype: "numberfield",
                name: "MS_SPACE_NUM",
                fieldLabel: "Space Number",
                value: attributes.MS_SPACE_NUM
            }]
        });
    },

    /** private: method[getOrientationForFeature]
     *  :arg feature:
     *
     *  Return the orientation of a feature based on the case insensitive
     *  `headingAttribute` property.
     */
    getOrientationForFeature: function(feature) {
        var orientation = 0;
        if (this.headingAttribute) {
            for (var attr in feature.attributes) {
                if (attr.toUpperCase() === this.headingAttribute.toUpperCase()) {
                    orientation = Number(feature.attributes[attr]);
                    break;
                }
            }
        }
        return orientation;
    },

    /** private: method[getDirtyState]
     *  Get the appropriate OpenLayers.State value to indicate a dirty feature.
     *  We don't cache this value because the popup may remain open through
     *  several state changes.
     */
    getDirtyState: function() {
        return this.feature.state === OpenLayers.State.INSERT ?
            this.feature.state : OpenLayers.State.UPDATE;
    },

    /** private: method[startEditing]
     */
    startEditing: function() {
        if (!this.editing) {
            this.editing = true;
            this.anc && this.unanchorPopup();

            this.editButton.hide();
            this.deleteButton.hide();
            this.saveButton.show();
            this.cancelButton.show();

            this.geometry = this.feature.geometry.clone();
            this.attributes = Ext.apply({}, this.feature.attributes);

            this.modifyControl = new OpenLayers.Control.ModifyFeature(
                this.feature.layer,
                {standalone: true, vertexRenderIntent: this.vertexRenderIntent}
            );
            this.feature.layer.map.addControl(this.modifyControl);
            this.modifyControl.activate();
            this.modifyControl.selectFeature(this.feature);
        }
    },

    /** private: method[stopEditing]
     *  :arg save: ``Boolean`` If set to true, changes will be saved and the
     *      ``featuremodified`` event will be fired.
     */
    stopEditing: function(save) {
        if (this.editing) {
            //TODO remove the line below when
            // http://trac.openlayers.org/ticket/2210 is fixed.
            this.modifyControl.deactivate();
            this.modifyControl.destroy();

            var feature = this.feature;

            var form = this.attributeForm.getForm();

            if (feature.state === this.getDirtyState()) {
                if (save === true) {
                    // apply modified attributes to feature
                    form.items.each(function(field) {
                        var value = field.getValue(); // this may be an empty string
                        feature.attributes[field.getName()] = value || field.value;
                    });
                    this.fireEvent("featuremodified", this, feature);
                } else if (feature.state === OpenLayers.State.INSERT) {
                    this.editing = false;
                    feature.layer.destroyFeatures([feature]);
                    this.fireEvent("canceledit", this, null);
                    this.close();
                } else {
                    form.reset();
                    var layer = feature.layer;
                    layer.drawFeature(feature, {display: "none"});
                    feature.geometry = this.geometry;
                    feature.attributes = this.attributes;
                    this.setFeatureState(null);
                    layer.drawFeature(feature);
                    this.fireEvent("canceledit", this, feature);
                }
            }

            if (!this.isDestroyed) {
                this.cancelButton.hide();
                this.saveButton.hide();
                this.editButton.show();
                this.allowDelete && this.deleteButton.show();
            }

            this.editing = false;
        }
    },

    deleteFeature: function() {
        Ext.Msg.show({
            title: this.deleteMsgTitle,
            msg: this.deleteMsg,
            buttons: Ext.Msg.YESNO,
            fn: function(button) {
                if (button === "yes") {
                    this.setFeatureState(OpenLayers.State.DELETE);
                    this.fireEvent("featuremodified", this, this.feature);
                    this.close();
                }
            },
            scope: this,
            icon: Ext.MessageBox.QUESTION,
            animEl: this.getEl()
        });
    },

    /** private: method[setFeatureState]
     *  Set the state of this popup's feature and trigger a featuremodified
     *  event on the feature's layer.
     */
    setFeatureState: function(state) {
        this.feature.state = state;
        var layer = this.feature.layer;
        layer && layer.events.triggerEvent("featuremodified", {
            feature: this.feature
        });
    }
});

/** api: xtype = app_asseteditorpopup */
Ext.reg('app_asseteditorpopup', ParkingManager.AssetEditorPopup);


text:
#include "dev/x86/i8237.hh"
#include "mem/packet.hh"
#include "mem/packet_access.hh"

Tick
X86ISA::I8237::read(PacketPtr pkt)
{
    assert(pkt->getSize() == 1);
    Addr offset = pkt->getAddr() - pioAddr;
    switch (offset) {
      case 0x0:
        panic("Read from i8237 channel 0 current address unimplemented.\n");
      case 0x1:
        panic("Read from i8237 channel 0 remaining "
                "word count unimplemented.\n");
      case 0x2:
        panic("Read from i8237 channel 1 current address unimplemented.\n");
      case 0x3:
        panic("Read from i8237 channel 1 remaining "
                "word count unimplemented.\n");
      case 0x4:
        panic("Read from i8237 channel 2 current address unimplemented.\n");
      case 0x5:
        panic("Read from i8237 channel 2 remaining "
                "word count unimplemented.\n");
      case 0x6:
        panic("Read from i8237 channel 3 current address unimplemented.\n");
      case 0x7:
        panic("Read from i8237 channel 3 remaining "
                "word count unimplemented.\n");
      case 0x8:
        panic("Read from i8237 status register unimplemented.\n");
      default:
        panic("Read from undefined i8237 register %d.\n", offset);
    }
    pkt->makeAtomicResponse();
    return latency;
}

Tick
X86ISA::I8237::write(PacketPtr pkt)
{
    assert(pkt->getSize() == 1);
    Addr offset = pkt->getAddr() - pioAddr;
    switch (offset) {
      case 0x0:
        panic("Write to i8237 channel 0 starting address unimplemented.\n");
      case 0x1:
        panic("Write to i8237 channel 0 starting "
                "word count unimplemented.\n");
      case 0x2:
        panic("Write to i8237 channel 1 starting address unimplemented.\n");
      case 0x3:
        panic("Write to i8237 channel 1 starting "
                "word count unimplemented.\n");
      case 0x4:
        panic("Write to i8237 channel 2 starting address unimplemented.\n");
      case 0x5:
        panic("Write to i8237 channel 2 starting "
                "word count unimplemented.\n");
      case 0x6:
        panic("Write to i8237 channel 3 starting address unimplemented.\n");
      case 0x7:
        panic("Write to i8237 channel 3 starting "
                "word count unimplemented.\n");
      case 0x8:
        panic("Write to i8237 command register unimplemented.\n");
      case 0x9:
        panic("Write to i8237 request register unimplemented.\n");
      case 0xa:
        {
            uint8_t command = pkt->get<uint8_t>();
            uint8_t select = bits(command, 1, 0);
            uint8_t bitVal = bits(command, 2);
            if (!bitVal)
                panic("Turning on i8237 channels unimplemented.\n");
            replaceBits(maskReg, select, bitVal);
        }
        break;
      case 0xb:
        panic("Write to i8237 mode register unimplemented.\n");
      case 0xc:
        panic("Write to i8237 clear LSB/MSB flip-flop "
                "register unimplemented.\n");
      case 0xd:
        panic("Write to i8237 master clear/reset register unimplemented.\n");
      case 0xe:
        panic("Write to i8237 clear mask register unimplemented.\n");
      case 0xf:
        panic("Write to i8237 write all mask register bits unimplemented.\n");
      default:
        panic("Write to undefined i8254 register.\n");
    }
    pkt->makeAtomicResponse();
    return latency;
}

X86ISA::I8237 *
I8237Params::create()
{
    return new X86ISA::I8237(this);
}


text:
<?php
Class CampaignController extends CI_Controller{
	public function campaigns($PropertySlug){
		$this->load->model('Getinfo');
		$result=$this->Getinfo->getpropertycampaigndata($PropertySlug);
		$cp['r']=$result;
		//foreach($result as $value)
          //    {
            //      $data['campdata'] = $value;
              //}
		$PropertyCampaignId=$result[0]->PropertyCampaignId;

		$imageresult=$this->Getinfo->getpropertycampaignimage($PropertyCampaignId);
		$cp['i']=$imageresult;
		$imgcount=count($imageresult);
		$cp['c']=$imgcount;
		$Appreciationres=$this->Getinfo->getAppreciation($PropertyCampaignId);
		$cp['a']=$Appreciationres;
		$SPVTenancyInformationres=$this->Getinfo->getSPVTenancyInformation($PropertyCampaignId);
		$cp['s']=$SPVTenancyInformationres;
		$this->load->view('header');
		$this->load->view('campaignpage',$cp);
		$this->load->view('footer');

	}
}
?>