text: 
  // Single Date Formatting
  // -------------------------------------------------------------------------------------------------
  // call this if you want Moment's original format method to be used
  function oldMomentFormat(mom, formatStr, isJalaali) {
  	return oldMomentProto.format.call(mom, toJalaaliUnit(formatStr, isJalaali)).replace(/j/g, ""); // oldMomentProto defined in moment-ext.js				return oldMomentProto.format.call(mom, toJalaaliUnit(formatStr, isJalaali)).replace(/j/g,""); // oldMomentProto defined in moment-ext.js
  }
  
  
  // Formats `date` with a Moment formatting string, but allow our non-zero areas and
  // additional token.
  function formatDate(date, formatStr, isJalaali) {
  	return formatDateWithChunks(date, getFormatStringChunks(formatStr, isJalaali), isJalaali);
  }
  
  
  function formatDateWithChunks(date, chunks, isJalaali) {
  	var s = '';
  	var i;
  
  	for (i=0; i<chunks.length; i++) {
  		s += formatDateWithChunk(date, chunks[i], isJalaali);
  	}
  
  	return s;
  }
  
  
  // addition formatting tokens we want recognized
  var tokenOverrides = {
  	t: function(date, isJalaali) { // "a" or "p"
  		return oldMomentFormat(date, 'a', isJalaali).charAt(0);
  	},
  	T: function(date, isJalaali) { // "A" or "P"
  		return oldMomentFormat(date, 'A', isJalaali).charAt(0);
  	}
  };
  
  
  function formatDateWithChunk(date, chunk, isJalaali) {
  	var token;
  	var maybeStr;
  
  	if (typeof chunk === 'string') { // a literal string
  		return chunk;
  	}
  	else if ((token = chunk.token)) { // a token, like "YYYY"
  		if (tokenOverrides[token]) {
  			return tokenOverrides[token](date, isJalaali); // use our custom token
  		}
  		return oldMomentFormat(date, token, isJalaali);
  	}
  	else if (chunk.maybe) { // a grouping of other chunks that must be non-zero
  		maybeStr = formatDateWithChunks(date, chunk.maybe, isJalaali);
  		if (maybeStr.match(/[1-9]/)) {
  			return maybeStr;
  		}
  	}
  
  	return '';
  }
  
  
  // Date Range Formatting
  // -------------------------------------------------------------------------------------------------
  // TODO: make it work with timezone offset
  
  // Using a formatting string meant for a single date, generate a range string, like
  // "Sep 2 - 9 2013", that intelligently inserts a separator where the dates differ.
  // If the dates are the same as far as the format string is concerned, just return a single
  // rendering of one date, without any separator.
  function formatRange(date1, date2, formatStr, separator, isRTL, isJalaali) {
  	var localeData;
  
  	date1 = FC.moment.parseZone(date1);
  	date2 = FC.moment.parseZone(date2);
  
  	localeData = date1.localeData();
  
  	// Expand localized format strings, like "LL" -> "MMMM D YYYY"
  	formatStr = localeData.longDateFormat(formatStr) || formatStr;
  	// BTW, this is not important for `formatDate` because it is impossible to put custom tokens
  	// or non-zero areas in Moment's localized format strings.
  
  	separator = separator || ' - ';
  
  	return formatRangeWithChunks(
  		date1,
  		date2,
  		getFormatStringChunks(formatStr, isJalaali),
  		separator,
  		isRTL,
  		isJalaali
  	);
  }
  FC.formatRange = formatRange; // expose
  
  
  function formatRangeWithChunks(date1, date2, chunks, separator, isRTL, isJalaali) {
  	var unzonedDate1 = date1.clone().stripZone(); // for formatSimilarChunk
  	var unzonedDate2 = date2.clone().stripZone(); // "
  	var chunkStr; // the rendering of the chunk
  	var leftI;
  	var leftStr = '';
  	var rightI;
  	var rightStr = '';
  	var middleI;
  	var middleStr1 = '';
  	var middleStr2 = '';
  	var middleStr = '';
  
  	// Start at the leftmost side of the formatting string and continue until you hit a token
  	// that is not the same between dates.
  	for (leftI=0; leftI<chunks.length; leftI++) {
  		chunkStr = formatSimilarChunk(date1, date2, unzonedDate1, unzonedDate2, chunks[leftI], isJalaali);
  		if (chunkStr === false) {
  			break;
  		}
  		leftStr += chunkStr;
  	}
  
  	// Similarly, start at the rightmost side of the formatting string and move left
  	for (rightI=chunks.length-1; rightI>leftI; rightI--) {
  		chunkStr = formatSimilarChunk(date1, date2, unzonedDate1, unzonedDate2,  chunks[rightI], isJalaali);
  		if (chunkStr === false) {
  			break;
  		}
  		rightStr = chunkStr + rightStr;
  	}
  
  	// The area in the middle is different for both of the dates.
  	// Collect them distinctly so we can jam them together later.
  	for (middleI=leftI; middleI<=rightI; middleI++) {
  		middleStr1 += formatDateWithChunk(date1, chunks[middleI], isJalaali);
  		middleStr2 += formatDateWithChunk(date2, chunks[middleI], isJalaali);
  	}
  
  	if (middleStr1 || middleStr2) {
  		if (isRTL) {
  			middleStr = middleStr2 + separator + middleStr1;
  		}
  		else {
  			middleStr = middleStr1 + separator + middleStr2;
  		}
  	}
  
  	return leftStr + middleStr + rightStr;
  }
  
  
  var similarUnitMap = {
  	Y: 'year',
  	M: 'month',
  	D: 'day', // day of month
  	d: 'day', // day of week
  	// prevents a separator between anything time-related...
  	A: 'second', // AM/PM
  	a: 'second', // am/pm
  	T: 'second', // A/P
  	t: 'second', // a/p
  	H: 'second', // hour (24)
  	h: 'second', // hour (12)
  	m: 'second', // minute
  	s: 'second' // second
  };
  // TODO: week maybe?
  
  
  // Given a formatting chunk, and given that both dates are similar in the regard the
  // formatting chunk is concerned, format date1 against `chunk`. Otherwise, return `false`.
  function formatSimilarChunk(date1, date2, unzonedDate1, unzonedDate2, chunk, isJalaali) {
  	var token;
  	var unit;
  
  	if (typeof chunk === 'string') { // a literal string
  		return chunk;
  	}
  	else if ((token = chunk.token)) {
  		unit = similarUnitMap[token.charAt(0)];
  
  		// are the dates the same for this unit of measurement?
  		// use the unzoned dates for this calculation because unreliable when near DST (bug #2396)
  		if (unit && unzonedDate1.isSame(unzonedDate2, toJalaaliUnit(unit, isJalaali))) {
  			return oldMomentFormat(date1, token); // would be the same if we used `date2`
  			// BTW, don't support custom tokens
  		}
  	}
  
  	return false; // the chunk is NOT the same for the two dates
  	// BTW, don't support splitting on non-zero areas
  }
  
  
  // Chunking Utils
  // -------------------------------------------------------------------------------------------------
  
  
  var formatStringChunkCache = {};
  
  
  function getFormatStringChunks (formatStr, isJalaali) {
  	formatStr = noJalaaliUnit (formatStr, isJalaali);
  	if (formatStr in formatStringChunkCache) {
  		return formatStringChunkCache[formatStr];
  	}
  	return (formatStringChunkCache[formatStr] = chunkFormatString(formatStr));
  }
  
  
  // Break the formatting string into an array of chunks
  function chunkFormatString(formatStr) {
  	var chunks = [];
  	var chunker = /\[([^\]]*)\]|\(([^\)]*)\)|(LTS|LT|(\w)\4*o?)|([^\w\[\(]+)/g; // TODO: more descrimination
  	var match;
  
  	while ((match = chunker.exec(formatStr))) {
  		if (match[1]) { // a literal string inside [ ... ]
  			chunks.push(match[1]);
  		}
  		else if (match[2]) { // non-zero formatting inside ( ... )
  			chunks.push({ maybe: chunkFormatString(match[2]) });
  		}
  		else if (match[3]) { // a formatting token
  			chunks.push({ token: match[3] });
  		}
  		else if (match[5]) { // an unenclosed literal string
  			chunks.push(match[5]);
  		}
  	}
  
  	return chunks;
  }
  
  
  // Misc Utils
  // -------------------------------------------------------------------------------------------------
  
  
  // granularity only goes up until day
  // TODO: unify with similarUnitMap
  var tokenGranularities = {
  	Y: { value: 1, unit: 'year' },
  	M: { value: 2, unit: 'month' },
  	W: { value: 3, unit: 'week' },
  	w: { value: 3, unit: 'week' },
  	D: { value: 4, unit: 'day' }, // day of month
  	d: { value: 4, unit: 'day' } // day of week
  };
  
  // returns a unit string, either 'year', 'month', 'day', or null
  // for the most granular formatting token in the string.
  FC.queryMostGranularFormatUnit = function(formatStr) {
  	var chunks = getFormatStringChunks(formatStr);
  	var i, chunk;
  	var candidate;
  	var best;
  
  	for (i = 0; i < chunks.length; i++) {
  		chunk = chunks[i];
  		if (chunk.token) {
  			candidate = tokenGranularities[chunk.token.charAt(0)];
  			if (candidate) {
  				if (!best || candidate.value > best.value) {
  					best = candidate;
  				}
  			}
  		}
  	}
  
  	if (best) {
  		return best.unit;
  	}
  
  	return null;
  };
  