<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected {color:[[ColorPalette::PrimaryDark]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}

.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}
.readOnly {background:[[ColorPalette::TertiaryPale]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:alpha(opacity=60);}
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0 1em 1em; left:0; top:0;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0 0; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0;}
.wizardFooter .status {padding:0 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0 0 0.5em;}
.tab {margin:0 0 0 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0 0.25em; padding:0 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0 3px 0 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0; font-size:.9em;}
.editorFooter .button {padding-top:0; padding-bottom:0;}

.fieldsetFix {border:0; padding:0; margin:1px 0px;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin-left:3em; padding:1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none !important;}
#displayArea {margin: 1em 1em 0em;}
noscript {display:none;} /* Fixes a feature in Firefox where print preview displays the noscript content */
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These [[InterfaceOptions]] for customising [[TiddlyWiki]] are saved in your browser

Your username for signing your edits. Write it as a [[WikiWord]] (eg [[JoeBloggs]])

<<option txtUserName>>
<<option chkSaveBackups>> [[SaveBackups]]
<<option chkAutoSave>> [[AutoSave]]
<<option chkRegExpSearch>> [[RegExpSearch]]
<<option chkCaseSensitiveSearch>> [[CaseSensitiveSearch]]
<<option chkAnimate>> [[EnableAnimations]]

Also see [[AdvancedOptions]]
Zhu Min

One clear lesson for oil-exporting countries in recent years, and especially in 2016, is that they should be adjusting their public policies to promote innovation and diversify their economies. Their agreement in late November to cut production— the first such accord in eight years—doesn’t change this, regardless of the short-term increase in prices.

To be sure, oil revenues appear to have magically boosted oil-exporting countries’ gross domestic products (GDPs) over the last quarter-century, especially in the Gulf region. And bustling, cosmopolitan cities—featuring dazzling skylines, world-class infrastructure, and higher-than-average living standards —have emerged in many of these countries.

But the world in 2017 and beyond will be very different. Downward pressure on oil prices reflects not just lower global energy demand, owing to slower economic growth; it also stems from technological changes in hydrocarbon production, the recent rise of renewable-energy sources, and global commitments to fight climate change, not least the December 2015 Paris climate agreement.

As a result, many oil-producing countries’ sole growth engine—hydrocarbon revenues—is running in low gear, and could continue to do so for a long time, if not permanently. Yet, as their recently agreed production cap suggests, oil-exporting economies remain overly dependent on it.

When oil prices stayed low during the 1980s and 1990s, oil-exporting countries’ living standards and employment rates fell, while their public debts skyrocketed. The same thing has happened since 2014, with countries burning through financial reserves and some forced to cut spending. This time, the oil-exporting countries have amassed ample financial reserves to weather an oil-price decline. Yet they remain under oil’s spell.

''More from The Year of Surprise''

A recent book published by the International Monetary Fund, Breaking the Oil Spell: The Gulf Falcons’ Path to Diversification (which I co-edited), sheds important light on how governments can reorient their countries’ economies. The book distills insights from countries such as Brazil, South Korea, Malaysia, and Singapore, where economic diversification has been successful.

These countries are not major oil exporters, but they provide powerful lessons nonetheless. In each country, economic diversification efforts have focused on high value-added industries that compete in international markets. These industries then generate productivity gains, producing a positive impact on other economic sectors. For example, in Malaysia, primary-commodity exports, as a share of total exports, fell from about 80% to about 20% between 1980 and 2012, while electronics exports increased from less than 10 % to more than 30%.

According to Breaking the Oil Spell, governments that have diversified their economies have done so with policies to improve “access to financing and business support services through venture capital funds, development banks, and export promotion agencies, and the creation of special economic zones, industry clusters, research-and-development centres, and start-up incubators”.

For example, Singapore has established manufacturing, science, and high-tech parks to promote research and development and the emergence of industry clusters; and Brazil has made substantial progress, with the support of the Brazilian Development Bank, in building its pharmaceutical, sugarcane, and software industries. Malaysia, for its part, has supported the industries that harvest, produce, and export its natural resources, including palm oil and rubber, while also venturing into the electronics market.

In all of the countries that have successfully diversified their economies, the state played a leading role, by promoting innovation and integrating the public and private sectors in order to support export-driven firms and human-capital development.

Oil-exporting countries’ governments should take the lead too and create incentives for individuals to develop skills needed in the private sector, particularly in high-value-added export industries. They should improve governance, transparency, competition, and, especially, education by implementing social development programmes and by keeping public-sector wages and employment in check, to avoid crowding out private firms from the labour market. And, of course, they should always take these steps with an eye toward macroeconomic and financial stability.

The prospect of persistently low oil prices should be a wake-up call for oil-exporting countries. ''Their governments must put economic diversification at the top of their policy agendas.'' Some already have: Saudi Arabia recently released its Vision 2030 plan, which establishes a blueprint for transforming the economy, by reducing its dependence on oil, increasing the role of the private sector, and creating more jobs for Saudi nationals.

Vision 2030 is a good first step, but translating these goals into reality will require carefully prioritized and sequenced policies and government interventions in the coming months and years. This is true not only for Saudi Arabia, but for all oil-exporting countries – and a new year is as good a time as any to break the spell that oil has long held over their economies.

Zhu Min, a former deputy managing director of the International Monetary Fund, is deputy governor of the People’s Bank of China.

#Gross Domestic Product (GDP) is defined as the market value of all final goods and services newly produced within a nation during a fixed period of time, typically a year. The important principles are: avoid double-counting; do not count asset appreciation; and do not count goods and services produced outside of the country. 
#What is the difference between Real and Nominal GDP? To compute nominal GDP, we use current prices. To compute real GDP, we use constant prices, namely, the prices at base year. Therefore, every time we mention real GDP, we need to be explicit about the base year. Please refer to lecture “Recent Economic Development in China” for a numerical example. 
#GDP deflator = 100*Nominal GDP / Real GDP. We can compute the inflation rate in year t  as the growth rate of the GDP deflator from year t-1 to t. For example, suppose the GDP deflators from 2001 to 2005 are given by 100, 99, 101, 105, 108. Inflation rate in 2002 is given by  (99 – 100)/100 = - 0.01 = -1%. 
#There are quite a few measures of inflation rate. As long as a price index is available, you can compute the growth rate of that index and obtain one measure of inflation rate. The inflation rate calculated based on the GDP deflator is one popular measure of inflation. The other popular inflation is called CPI inflation, based on the consumer price index (CPI). CPI measures the cost of a fixed consumption basket, which may contain food, clothing, energy, articles for daily use, and services. 
#Gross National Product (GNP) = GDP + Net Factor Payments from abroad (NFP), where NFP is defined as income paid to domestic factors of production (labor and capital) by the rest of the world minus income paid to foreign factors of production by the domestic economy. In some countries, GNP is also called GNI (Gross National Income). 
#Gross National Disposable Income (GNDI) = GNP + Net Transfers. Transfers include the official assistance as well as remittances from families residing abroad.
#National Saving (S) = Gross National Disposable Income – Consumption (Private and Government Consumption) 
#Current Account Balance include the following items (in US Dollars)
##Balance of trade in goods (export of goods – import of goods)
##Services net (services income – services payments). These services are the ones involving foreign individuals, foreign companies, and foreign governments. They include freight on exports and imports, passenger services, port services, travel expenditures, insurance services
##Investment income net (investment income – investment payments) 
##Compensation of employees net (compensation received from residents working abroad – compensation paid to foreign residents working in the country) 
##Current transfers net (explained in Item 6).
#If Current Account Balance is positive, the current account is said to be in surplus. If negative, it is said to be in deficit.
#Capital and Financial Account include the following items (in US Dollars)
##Direct Investment net. It is equal to Foreign Direct Investment in China (FDI) minus China’s Direct Investment abroad. 
##Portfolio Investment net. It equals to the increase in foreign holdings of assets in China such as stocks and bonds minus the increase in China’s holding of assets abroad. If a foreign entity holds 25% or more of the stock of company XYZ in China, this investment should be recorded as FDI rather than Portfolio Investment.
##Other Investment. It includes trade credit,  loans, etc.
#Balance of Payment Table includes Current Account Balance, Capital and Financial Account Balance, Errors and Omissions, and International Reserve Assets (a negative sign means an increase in International Reserves). 
##If Current Account Balance + Capital and Financial Account Balance = a positive number, the Balance of Payment (BOP) is said to be in surplus. If negative, then BOP in deficit.
#Government budget balance = Total Fiscal Revenue (tax and non-tax) – Total Fiscal Expenditure
##If the budget balance is negative, the government is said to be running a fiscal deficit.
##If for example, total fiscal revenue = $100 billion, total fiscal expenditure = $110 billion, the government budget balance =100 – 110 =  - $10 billion. In this case, the government budget deficit is said to be $10 billion.
#Primary Government budget balance = Total Revenue (tax and non-tax) – (Total Expenditure – debt service payment). In the above example b, if debt service payment is $2 billion, then the primary government budget balance = 100 – (110 – 2) = - $8 billion. Thus the primary fiscal deficit is only $8 billion. Namely, in computing primary fiscal deficit, the expenditure on debt service is excluded. 
#Sometimes, we focus more on primary budget deficit because the debt service payment is the legacy from budget executions in the past. The current government, if newly elected, should not be blamed. 
#What is expansionary fiscal policy?
##An increase in government consumption relative to GDP; an increase in government investment relative to GDP; a tax cut. According to Keynesians, all of these tend to increase aggregate demand and are therefore called expansionary fiscal policy.
#When to use expansionary fiscal policy?
##Answer: when the economy is in recession or is slipping into recession. The expansionary fiscal policy stimulates economic activity.
#Nominal Interest Rates measure the returns in nominal terms (in a currency) to interest-bearing assets. For example, if US 10 year government bond carries a nominal interest rate of 4.43 percent per year and Hong Kong 5 year government bond pays 3.2 percent per year, then the 4.43 percent and 3.2 percent are all nominal interest rates. Different instruments, for example, instruments with different issuers and different maturities, pay different rates of interest. Therefore when we talk about a nominal interest rate, we need to be explicit about which instrument we are referring to.
#Real interest rates measure the returns in purchasing power to interest-bearing assets. Real interest rate = Nominal interest rate – inflation rate. 
##Using the Hong Kong example above, if inflation rate is 2 percent, the real interest rate on 5 year government bond would be 3.2 – 2 = 1.2 percent. 
#Money is defined as those assets that are widely used and accepted in payments. Monetary aggregates measure the amounts of money in an economy. Three most widely used monetary aggregates are called M0, M1 and M2.
#M0 is also called the monetary base or high powered money. It includes currency in circulation outside of banks, vault cash in banks, the deposits at the central bank by the depository institutions. 
#M1 includes:
##Travelers’ checks
##Demand Deposits (non-interest-bearing checking accounts)
##Other checkable deposits
#All the components in M1 are actively used and widely accepted for making payments.
#M2 includes:
##All components of M1
##Savings deposits
##Small-denomination (less than $100,000 in the case of USA) time deposits
##Money Market Mutual Funds (non-institutional) and MMDA (Money Market Deposit Accounts)
#What is an expansionary monetary policy?
##If the central bank or monetary authority increases money supply (say M2) at a rate higher than in the recent past, the aggregate demand will be increased. The rapid increase in M2 is thus called an expansionary monetary policy.
#How can a central bank increase M2?
##By a reduction in reserve requirement. This will allow the commercial banks to have more resources for their lending programs. Hence M2 will be increased.
##By cutting the discount window rate. This reduces the cost of borrowing by the commercial banks at the central banks’ discount window. Hence, this type of borrowing will increase and more money will be circulating in the banking system.
##By an open market purchase (purchase of government bond or central bank bills from the financial sector). This will inject new money into the circulation.
#Doing the opposite of a, b, c above reduces M2 growth rate and therefore is meant for a monetary tightening.
#RMB appreciation would reduce the cost of imports and therefore enhance China’s purchasing power. So why would China resist the pressure for further appreciation? Answer: RMB appreciation shrinks the profit margin of the exporting firms and hurts China’s export industry. 
MAY 5, 2014 
China is Still Number Two

@@Jeffrey Frankel, a professor at Harvard University's Kennedy School of Government, previously served as a member of President Bill Clinton’s Council of Economic Advisers. @@

CAMBRIDGE – Headlines around the world this week trumpeted a watershed moment for the global economy. As the Financial Times put it, “China poised to pass US as world’s leading economic power this year.” This is a startling development – or it would be if the claim were not essentially wrong. In fact, the United States remains the world’s largest national economy by a substantial margin.

The story was based on the April 29 release of a report from the World Bank’s International Comparison Program. The ICP’s work is extremely valuable. I eagerly await and use their new estimates every six years or so, including to look at China.

The ICP data compare countries’ GDP using purchasing-power-parity (PPP) exchange rates, rather than market rates. This is the right thing to do when looking at real (inflation-adjusted) income per capita in order to measure people’s living standards. But it is the wrong thing to do when looking at national income in order to measure the country’s weight in the global economy.

The bottom line is that, by either criterion – per capita income (at PPP exchange rates) or aggregate GDP (at market rates) – the day when China surpasses the US remains in the future. This in no way detracts from the country’s impressive growth record, which, at about 10% per year for three decades, constitutes a historical miracle.

At market exchange rates, the American economy is still almost double the size of China’s (83% larger, to be precise). If the Chinese economy’s annual growth rate remains five percentage points higher than that of the US, with no significant change in the exchange rate, it will take another 12 years to catch up in total size. If the differential is eight percentage points – for example, because the renminbi appreciates at 3% a year in real terms – China will surpass the US within eight years.

The PPP-versus-market-exchange-rate issue is familiar to international economists. This annoying but unavoidable technical problem arises because China’s output is measured in renminbi, while US income is measured in dollars. How, then, should one translate the numbers so that they are comparable?

The obvious solution is to use the contemporaneous exchange rate – that is, multiply China’s renminbi-measured GDP by the dollar-per-renminbi exchange rate, so that the comparison is expressed in dollars. But then someone points out that if you want to measure Chinese citizens’ standard of living, you have to take into account that many goods and services are cheaper there. A renminbi spent in China goes further than a renminbi spent abroad.

For this reason, if you want to compare per capita income across countries, you need to measure local purchasing power, as the ICP does. The PPP measure is useful for many purposes, such as knowing which governments have succeeded in raising their citizens’ standard of living.

Looking at per capita income, even by the PPP measure, China is still a relatively poor country. Though it has come very far in a short time, its per capita income is now about the same as Albania’s – that is, in the middle of the distribution of 199 countries.

But Albania’s economy, unlike China’s, is not often in the headlines. That is not only because China has such a dynamic economy, but also because it has the world’s largest population. Multiplying a middling per capita income by more than 1.3 billion “capita” yields a big number. The combination of a large population and a medium income gives it economic power, and also political power.

Similarly, we consider the US the number-one incumbent power not just because it is rich. If per capita income were the criterion by which to judge, Monaco, Qatar, Luxembourg, Brunei, Liechtenstein, Kuwait, Norway, and Singapore would all rank ahead of the US. (For the purposes of this comparison, it does not matter much whether one uses market exchange rates or PPP rates.) If you are shopping for citizenship, you might want to consider one of those countries.

But we do not consider Monaco, Brunei, and Liechtenstein to be among the world’s “leading economic powers,” because they are so small. What makes the US the world’s leading economic power is the combination of its large population and high per capita income.

It is this combination that explains the widespread fascination with how China’s economic size or power compares to America’s, and especially with the question of whether the challenger has now displaced the long-reigning champion. But PPP exchange rates are not the best tool to use to answer that question.

The reason is that when we talk about an economy’s size or power, we are talking about a broad range of questions – and a broad range of interlocutors. From the viewpoint of multinational corporations, how big is the Chinese market? From the viewpoint of global financial markets, will the RMB challenge the dollar as an international currency? From the viewpoint of the International Monetary Fund and other multilateral agencies, how much money can China contribute, and how much voting power should it get in return? From the viewpoint of countries with rival claims in the South China Sea, how many ships can its military buy?

For these questions, and most others involving total economic heft, the indicator to use is GDP at market exchange rates, because what we want to know is how much the renminbi can buy on world markets, not how many haircuts or other local goods it can buy back home. And the answer to that question is that China can buy more than any other country in the world – except the US.

Read more from "China's Challenges"


Read more at http://www.project-syndicate.org/commentary/jeffrey-frankel-pours-cold-water-on-the-claim-that-the-us-economy-has-been-surpassed#UFIMWHzwgmigjsja.99
Simon Johnson and Jonathan Ruane, December 29, 2017 

WASHINGTON, DC – China has achieved much since 1978, when Deng Xiaoping initiated the transition to a market economy. In terms of headline economic progress, the pace of China’s transformation over the past 40 years is unprecedented. The country’s GDP grew by nearly 10% per year on average, while reshaping global trade patterns and becoming the second-largest economy in the world. This success lifted 800 million people out of poverty, and the mortality rate of children under five years old was halved between 2006 and 2015.

The question now is whether China, well positioned to become the world’s innovation leader, will realize that opportunity in 2018 or soon after.

China’s transformation has been underpinned by an unprecedented manufacturing boom. In 2016, China shipped more than $2 trillion worth of goods around the world, 13% of total global exports. It has also pursued economic modernization through massive infrastructure investment, including bridges, airports, roads, energy, and telecoms. In less than a decade, China built the world’s largest bullet train system, surpassing 22,000 kilometers (13,670 miles) in July 2017. Annual consumption is expected to rise by nearly $2 trillion by 2021, equivalent to adding another consumer market the size of Germany to the global economy.

Earlier this month, Apple CEO Tim Cook declared that, “China stopped being a low-labor-cost country many years ago, and that is not the reason to come to China.” The country’s manufacturing strengths now lie in its advanced production know-how and strong supply-chain networks. Understandably, China’s leadership wants to increase productivity and continue to move further up the value chain.

Building on its 13th Five Year Plan (in May 2016), the authorities established objectives for China to become an “innovative nation” by 2020, an “international innovation leader” by 2030, and a “world powerhouse of scientific and technological innovation” by 2050. It also committed to increasing its expenditure on research and development to 2.5% of GDP and almost doubling the number of patents filed per 10,000 people by 2020.

To enable this innovation, municipal governments are building technology hubs, hoping to attract talent. The city of Guangzhou is encouraging researchers, entrepreneurs, and corporations to base themselves there. General Electric recently committed to build its first Asian biopharmaceutical project in an $800 million bio-campus. The southern city of Shenzhen is already known as the “Silicon Valley of Hardware,” and the greater Shenzhen-Hong Kong area is ranked second in terms of global inventive clusters (measured by patents).

Business in China often operates at a speed and nimbleness unlike anywhere else in the world. China is fully embracing digital models, not just digitizing old models. Its lack of legacy systems has already enabled it to leapfrog the West in areas such as digital payments, the sharing economy (dockless bicycles are sweeping the world), and e-commerce.

Total spending on R&D in China (as a percentage of GDP) more than doubled from 0.9% in 2000 to 2.1% in 2016. To date, the increase has mostly been focused on applied research and commercial development, with only 5% dedicated to basic science. Nevertheless, China ranked 22nd in the 2017 Global Innovation Index (a survey of 127 countries and economies based on 81 indicators) ahead of Spain, Italy, and Australia. China’s share of high-impact academic publications (the top 0.1% of papers in Scopus, which rates by citations) has grown, from less than 1% in 1997 to about 20% in 2016.

The sheer volume of university graduates (6.2 million in 2012, six times the 2001 total) combined with an internationally trained, highly skilled diaspora whose members return home in large numbers – there are 800,000 Chinese students in tertiary education abroad – is likely to produce enough talent to achieve the desired effect.

American workers are still considerably more productive than their Chinese counterparts. On average, each Chinese worker generates only about 19% of the amount of GDP that an American worker does. But this lead is being eroded.

Other factors in America’s favor include 30 of the top 100 universities in the world, a risk-taking, entrepreneurial culture, and its companies’ heavy exposure to market forces. Traditionally, this has driven US firms to compete aggressively, often relying on innovation.

But American industry is not as dynamic as it once was. Between 1997 and 2012, two-thirds of America’s industries experienced an increase in market concentration, and a record 74% of employees are working at these aging (16 years or older) incumbents.

US President Donald Trump’s administration seems to have completely misunderstood what is needed. Trump favors a more protectionist future, which would take the pressure off US companies to be globally competitive or truly innovative. American universities are being undermined by changes in the tax code and impending spending cuts – part of what appears to be a broader war on science. And immigration – an essential source of talent and ideas – looks likely to be restricted.

Given its own policies, and those of the US, China is on track to become the world’s innovation leader. By the end of 2018, it will be more apparent just how quickly and easily this latest chapter in the Chinese success story will be written.

[[Danyang Xie|http://danyang.xie.tiddlyspot.com/]]
#[[National Bureau of Statistics of China|http://www.stats.gov.cn/english/statisticaldata/annualdata/]]
#[[Weekly Calendar|https://wallstreetcn.com/calendar]]
#[[US Economic Statistics Monthly|https://home.treasury.gov/system/files/226/monthly_ECONOMIC_DATA_TABLES.PDF]]
#[[Federal Reserve Beige Book (8 Publications/Year)|https://www.federalreserve.gov/monetarypolicy/beige-book-default.htm]]
#[[USA: Fiscal Expenditures|https://www.usaspending.gov/#/explorer/budget_function]] 
#[[USA: Major Foreign Holders of Treasury Securities|http://ticdata.treasury.gov/Publish/mfh.txt]] 
#[[USA New Home Sales|https://www.census.gov/econ/currentdata/dbsearch?program=RESSALES&startYear=1963&endYear=2018&categories=SOLD&dataType=TOTAL&geoLevel=US&notAdjusted=1&submit=GET+DATA&releaseScheduleId=#line]]
#[[Hong Kong Property Market Data|https://data.gov.hk/en-data/dataset/hk-rvd-tsinfo_rvd-property-market-statistics]]
#[[Historical Federal Funds Rate Target|http://www.fedprimerate.com/fedfundsrate/federal_funds_rate_history.htm#current]]
#[[Libor USA and others|http://www.economagic.com/libor.htm#US]]
#[[USA: Treasury Yield Curve|https://www.treasury.gov/resource-center/data-chart-center/interest-rates/Pages/TextView.aspx?data=yield]]
#[[St. Louis Fed Real Interest Rate|https://fred.stlouisfed.org/series/DFII5]]
!Capital Flow
#[[USA Treasury International Capital Monthly Data|https://www.treasury.gov/resource-center/data-chart-center/tic/Pages/ticpress.aspx#1]]
!Leading Indicators
#[[Economic Cycle Research Institute Weekly Leading Index|https://www.businesscycle.com/ecri-reports-indexes/all-indexes#]]
#[[Philadelphia Fed Business Outlook Survey|https://www.phil.frb.org/research-and-data/regional-economy/business-outlook-survey/]]
#[[Brent Crude Oil|https://tradingeconomics.com/commodity/brent-crude-oil]]
#[[Copper Cash Market|https://www.marketscreener.com/LME-COPPER-CASH-16161/charts-historical/]]
[[Weekly Group Blogs]]
#6 groups will be formed before the second week. 8 students/Group is the standard. A couple of groups may have 9 students (please seek my permission first). 
#Each group should create a [[wordpress blog account|http://wordpress.com/]], use an ID that is easy to remember.
#Send me the link to your blog.  
#@@On your blog page, upload a group photo and list the FULL name of the members of the group, from the left to the right.@@
#Group blogs start AFTER the first lecture. The blogs have to be updated by noon-Monday. So the @@first blog@@ is due @@Noon, Monday, Feb 11, 2019.@@
#Select your topic that is related to current event, write about the impact of the event, on the World/China. One blog per week per group. 
#Topics blogged will be discussed in the classroom, 5-10 minutes/blog. 
#Blogs stop after the 6th lecture. Namely, each group needs to write a total of 5 blogs for the course. The @@last (5th) blog@@ is due @@Noon, Monday, March 11, 2019.@@
DEC 8, 2016
Italy on the Brink

@@Philippe Legrain, a former economic adviser to the president of the European Commission, is a visiting senior fellow at the London School of Economics’ European Institute and the author of European Spring: Why Our Economies and Politics are in a Mess – and How to Put Them Right.@@

LONDON – Political instability in Italy is nothing new. But Italian voters’ rejection of constitutional reforms in a referendum has not only led Prime Minister Matteo Renzi to resign; it has dealt another blow to a crisis-ridden European Union. In the near term, Italy’s ongoing banking crisis could flare up again and threaten European stability; in the longer term, Italy may have to leave the eurozone, which would put the single currency itself at risk.

The “No” side was widely expected to win. But the scale of its victory – a whopping 59% of the vote – was shocking, and largely a triumph for anti-establishment forces, particularly the Five Star Movement. The movement, led by the comedian Beppe Grillo, is leading in opinion polls, supports holding a referendum on eurozone membership, and is now demanding an immediate general election.

Most Italian commentators have downplayed the referendum’s significance for the rest of Europe. They argue that a new caretaker government, probably led by the technocratic finance minister, Pier Carlo Padoan, will reform electoral laws to keep the Five Star Movement from power. And even if the Five Star Movement captures a majority in the Italian Parliament’s lower house, it will not have a majority in the Senate, so it cannot form a government, unless it breaks its pledge not to join a coalition. In any case, the argument goes, a eurozone referendum would be hard to deliver, because it would require a constitutional amendment.

All of that may be true, but it misses the big picture. Renzi was the pro-EU establishment’s best – and perhaps last – hope for delivering the growth-enhancing reforms needed to secure Italy’s long-term future in the eurozone. Muddling through with a weak technocratic-led government amounts to waiting for an accident to happen. And, with the far-right Northern League and former Prime Minister Silvio Berlusconi’s Forza Italia also aligned against the euro, an anti-euro government is likely to come to power at some point – perhaps after the next general election, which is due by 2018 (but could be held as early as next spring). Then all bets will be off.

The immediate problem is Italy’s zombie banks, which are inadequately capitalized, insufficiently profitable, and saddled with bad loans. These banks need to raise fresh capital, which was already proving difficult before the referendum, and now may be impossible amid the heightened political uncertainty.

Capital is fleeing Italy, as Carmen Reinhart observes. Government-bond yields, which rose sharply in the run-up to the referendum, have so far remained steady; if they were to spike, however, Italian banks’ fragile balance sheets would deteriorate further. And, because the European Central Bank has already bought many Italian bonds through its quantitative easing (QE) program, it could not readily intervene further.

The most endangered bank is Monte dei Paschi di Siena (MPS), which is trying to raise €5 billion ($5.3 billion) in new capital. If it fails to do so, the government will likely furnish it with public money to stave off a collapse. That, in turn, would require MPS’s junior bondholders to take losses, unless the government breaches the EU’s bank “bail-in” rules, which would undermine the eurozone’s new banking union. While small investors who were mis-sold bonds could be compensated, large, politically powerful ones would not be.

MPS’s travails could have wider economic repercussions. Italy’s largest bank, UniCredit, which is better positioned than MPS, could struggle to raise the more than €10 billion that it is seeking. Because many eurozone banks remain weak, the crisis could then spread.

In the longer term, Italy’s eurozone membership could be at risk. Unless Italy enacts radical reforms to address its sclerotic growth, it is hard to see how it could ever have a viable future in a dysfunctional monetary union dominated by a mercantilist, deflationary Germany.

Italy’s economy is no larger today than it was in 2000. Its share of global exports has plummeted. And despite the boost from the ECB’s QE program, a weak euro, and the looser fiscal policies of recent years, output is growing at an annual rate of less than 1%. Moreover, Italy could barely stabilize its public debt – which now amounts to 133% of GDP – even when bond yields were reaching record lows. An economic downturn or rising interest rates would thus send public debt soaring again – and Italy cannot count on the ECB to continue buying its bonds indefinitely.

Italy’s political situation – marked by a sense of never-ending misery and growing resentment against the EU and Germany – is equally unsustainable. Youth unemployment is at 37%. Eurozone fiscal rules continue to chafe. And EU governments have done little to help Italy cope with the refugee crisis.

Italians used to be enthusiastically pro-European, and saw EU governance as preferable to corrupt domestic mismanagement. But support for the euro, the EU, and the country’s pro-EU establishment has plunged.

The mere possibility of Italy leaving the eurozone – which would entail the redenomination of €2.2 trillion of Italian government bonds in devalued lira – could spark financial panic. What’s more, Italy is too big to bail out, and an anti-euro government may be unwilling or unable to agree to the strictures of an EU loan, which would be necessary for the ECB to quell the panic. It is also implausible that Germany would offer to enter into a eurozone fiscal union that entails pooling its debts with Italy’s.

In addition to stronger demand in the eurozone, Italy desperately needs bold leadership – to restructure its banks, write down unpayable corporate and household debts, reform its economy, boost investment, and clean up its politics. So it is scarcely reassuring that both the eurozone and Italy are likely to try to muddle on for now.

|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Date:''|mar 17, 2007|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
version.extensions.LoadRemoteFileThroughProxy = {
 major: 1, minor: 1, revision: 0, 
 date: new Date("mar 17, 2007"), 
 source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};

if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};

bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
 if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){ 
 url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
 return bidix.core.loadRemoteFile(url,callback,params);
[[Danyang Xie]]
[[Group Blogs]]
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Date:''|Apr 19, 2007|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
version.extensions.PasswordOptionPlugin = {
	major: 1, minor: 0, revision: 2, 
	date: new Date("Apr 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.2.0 (Beta 5)'

config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");

merge(config.macros.option.types, {
	'pas': {
		elementType: "input",
		valueField: "value",
		eventName: "onkeyup",
		className: "pasOptionInput",
		typeValue: config.macros.option.passwordInputType,
		create: function(place,type,opt,className,desc) {
			// password field
			// checkbox linked with this password "save this password on this computer"
			// text savePasswordCheckboxLabel
		onChange: config.macros.option.genericOnChange

merge(config.optionHandlers['chk'], {
	get: function(name) {
		// is there an option linked with this chk ?
		var opt = name.substr(3);
		if (config.options[opt]) 
		return config.options[name] ? "true" : "false";

merge(config.optionHandlers, {
	'pas': {
 		get: function(name) {
			if (config.options["chk"+name]) {
				return encodeCookie(config.options[name].toString());
			} else {
				return "";
		set: function(name,value) {config.options[name] = decodeCookie(value);}

// need to reload options to load passwordOptions

if (!config.options['pasPassword'])
	config.options['pasPassword'] = '';

		pasPassword: "Test password"
Danyang Xie
China in the Global Economy
*[[China: 40 Years of Reforms and Opening Up|http://ihome.ust.hk/~dxie/Econ5120/Color Lecture 1.pdf]]
*[[Monetary Policy Analysis|http://ihome.ust.hk/~dxie/Econ5120/Color Lecture 2.pdf]]
*[[Fiscal Policy Analysis|http://ihome.ust.hk/~dxie/Econ5120/Color Lecture 3.pdf]]
*[[Macroeconomic Adjustment|http://ihome.ust.hk/~dxie/Econ5120/Color Lecture 4.pdf]]
*[[Determinants of Economic Growth|http://ihome.ust.hk/~dxie/Econ5120/Color Lecture 5.pdf]]
*[[Country Analysis and Business Strategies in Riding the Waves|http://ihome.ust.hk/~dxie/Econ5120/Color Lecture 6.pdf]]
**[[Country Profiles|http://ihome.ust.hk/~dxie/Econ5120/Country Profiles Complete New.pdf]]
!Course Website: http://econ5120.tiddlyspot.com
This course provides an understanding of China’s macroeconomic accounts, develops your ability to anticipate China’s macroeconomic adjustment. Special attention is devoted to the fact that China is increasingly more integrated with the rest of the world. You will have a good grasp of macroeconomic topics including the determinants of growth, business cycles, fiscal and monetary policy, exchange rate issues, financial crises, current account sustainability, and capital flows. Frequent cross-country comparisons will enable you to acquire an international perspective. 
Given the rapid changes occurring in China, any book on China, even if published most recently, quickly becomes obsolete. Students are strongly recommended to study reports on China from the central bank, international organizations, and the investment banks. For those students who have no background on Macroeconomics, please consult the [[Brief Note on Basic Macro Concepts]].
Class Discussion: 20%. [[Group Blogs]]: 40%. Final Exam (March 26, 2019, closed-book except one A4-size page double-sided notes; no need to put down the concepts since the Brief Note on Basic Macro Concepts will be attached to the Final Exam Paper): 40%.
@@Warning: Electronic device is prohibited during class time.@@

!An Overview
An overview of this course
*[[Learning from China by Erik Berglöf, LSE, December 2018|https://www.project-syndicate.org/commentary/impediments-to-exporting-china-development-model-by-erik-berglof-2018-12]]
*[[China's Malign Secrecy by Ricardo Hausmann, Harvard Kenndy School, January 2019|https://www.project-syndicate.org/commentary/china-development-finance-secrecy-by-ricardo-hausmann-2019-01]]
*[[Why American Firms and Households need China by Shang-Jin Wei, Columbia University, January 2019|https://www.project-syndicate.org/commentary/apple-revenues-china-trade-war-employment-by-shang-jin-wei-2019-01]] 
*[[Overview: A Resurgent East Asia, World Bank, 2018|https://openknowledge.worldbank.org/bitstream/handle/10986/30858/211333ov.pdf?sequence=2&isAllowed=y]]
!China: 40 Years of Reform and Opening Up
Key concepts: Production Possibility Frontier, Income Inequality, Trade Dependency, Deleveraging, Global Imbalance
!Monetary Policy in Action
Key concepts: Direct and Indirect monetary policy instruments, Central Bank independence
*[[Monetary Policy Report of ~PBoC. Q3, 2018. English Version|http://ihome.ust.hk/~dxie/Econ5120/Monetary Policy Report Q3 2018.docx]]
!Fiscal Account Analysis and Fiscal Policy
Key concepts: Ponzi game, fiscal sustainability, automatic stabilizer.
*[[China’s Fiscal Position and Policy: Current Status of Local Government Debt Problems and Challenges|https://www.mof.go.jp/english/pri/publication/pp_review/ppr027/ppr027c.pdf]], Ministry of Finance, Japan, 2015.
*[[China Fiscal Revenue 2017|http://www.stats.gov.cn/tjsj/ndsj/2018/html/EN0702.jpg]]
*[[China Fiscal Expenditure 2017|http://www.stats.gov.cn/tjsj/ndsj/2018/html/EN0703.jpg]]
!Macroeconomic Adjustment
Key concepts: Impossible Trinity, Capital flows and Sterilization, Yield Curve.
*[[The Impossible Trinity|http://www.voxeu.org/index.php?q=node/7340]], Stephen Grenville, November 2011. Based on an ADB Working Paper.
*[[China: Two Distinct Diagnoses of the Current Macroeconomic Situation|http://ihome.ust.hk/~dxie/Econ5120/Two%20Views.pdf]], Bank of America, September 2006.
!Determinants of Economic Growth
Key concepts: Human capital, Growth Accounting, Total Factor Productivity.
*[[Does openness generate growth? Reconciling the experiences of Mexico and China|http://voxeu.org/index.php?q=node/7301]], Timothy Kehoe and Kim Ruhl, November 2011
*[[Xavier ~Sala-i-Martin, “I just Ran 4 Million Regressions”|http://ihome.ust.hk/~dxie/Econ5120/Sala-i-Martin%204%20million%20regressions.pdf]], 1997.
!International Trade and Capital Flows.
Key concepts: Comparative Advantage, Harberger Triangle, FDI and portfolio investment, Tobin Tax, WTO accession.
*[[The Capital Flow Conundrum|http://voxeu.org/index.php?q=node/6718]], Uri Dadush and Bennett Stancil, July 2011
!Country Analysis and Business Strategies in Riding the Waves
*[[Country Profiles|http://pan.baidu.com/s/1kT2JlPH]]
*[[Data Sources|http://www.tradingeconomics.com/germany/current-account-to-gdp]]
!Structural Reforms and the Global Positioning of the Chinese Economy
*[[China is still number two]] May 5, 2014
!Final Exam'': Bring your calculator.

DEC 2, 2015 
The Great Policy Divergence

@@Mohamed A. ~El-Erian, Chief Economic Adviser at Allianz, the corporate parent of PIMCO where he served as CEO and co-Chief Investment Officer, is Chairman of US President Barack Obama’s Global Development Council. @@

WASHINGTON, DC – Over the next few weeks, the US Federal Reserve and the European Central Bank are likely to put in place notably different policies. The Fed is set to raise interest rates for the first time in almost ten years. Meanwhile, the ECB is expected to introduce additional unconventional measures to drive rates in the opposite direction, even if that means putting further downward pressure on some government bonds that are already trading at negative nominal yields. 

In implementing these policies, both central banks are pursuing domestic objectives mandated by their governing legislation. The problem is that there may be few, if any, orderly mechanisms to manage the international repercussions of this growing divergence. 

The Fed is responding to continued indications of robust job creation in the United States and other signs that the country’s economy is recovering, albeit moderately so. Also conscious of the risk to financial stability if interest rates remain at artificially low levels, the Fed is expected to increase them when its policy-setting Federal Open Market Committee meets on December 15-16. The move marks a turning point in the Fed’s approach to the economy. In deciding to raise interest rates, it will be doing more than simply lifting its foot from the financial-stimulus accelerator; it will also be taking a notable step toward the multiyear normalization of its overall policy stance. 

In the meantime, the ECB is facing a very different set of economic conditions, including generally sluggish growth, the risk of deflation, and worries about the impact of the terrorist attacks in Paris on business and consumer confidence. As a result, the bank’s decision-makers are giving serious consideration to pushing the discount rate further into negative territory and extending its large-scale asset-purchase program (otherwise known as quantitative easing). In other words, the ECB is likely to expand and extend experimental measures that will press even harder on the financial-stimulus accelerator. 

In a perfect world, policymakers would have assessed the potential for international spillovers from these divergent policies (including possible spillbacks on both sides of the Atlantic) and put in place a range of instruments to ensure a better alignment of domestic and global objectives. Unfortunately, political polarization and general policy dysfunction in both the US and the European Union continue to inhibit such an effort. As a result, lacking a more comprehensive policy response, the harmonization of their central banks’ divergent policies will be left to the markets – in particular, those for fixed-income assets and currencies. 

Already, the interest-rate differential between “risk-free” bonds on both sides of the Atlantic – say, US Treasuries and German Bunds – has widened notably. And, at the same time, the dollar has strengthened not only against the euro, but also against most other currencies. Left unchecked, these trends are likely to persist. 

If history is any guide, there are three major issues that warrant careful monitoring in the coming months. First, the US is unlikely to stand by for long if its currency appreciates significantly and its international competitiveness deteriorates substantially. Companies are already reporting earning pressures due to the rising dollar, and some are even asking their governments to play a more forceful role in countering a stealth “currency war.” 

Second, because the dollar is used as a reserve currency, a rapid rise in its value could put pressure on those who have used it imprudently. At particular risk are emerging-country companies that, having borrowed overwhelmingly in dollars but generating only limited dollar earnings, might have large currency mismatches in their assets and liabilities or their incomes and expenditures. 

And, finally, sharp movements in interest rates and exchange rates can cause volatility in other markets, most notably for equities. Because regulatory controls and market constraints have made brokers less able to play a countercyclical role by accumulating inventory on their balance sheets, the resulting price instability is likely to be large. There is a risk that some portfolios will be forced into disordered unwinding. Furthermore, the central banks’ policy of curtailing so-called “volatile volatility” is likely to be challenged. 

Of course, none of these outcomes is preordained. Politicians on both sides of the Atlantic have the ability to lower the risk of instability by implementing structural reforms, ensuring more balanced aggregate demand, removing pockets of excessive indebtedness, and smoothing out the mechanisms of multilateral and regional governance. 

The three possible outcomes of all this include a relatively stable multi-speed world, notable disruptions that undermine the US’s economic recovery, and a European revival that benefits from US growth. The good news is that the impact of the divergence will depend on how policymakers manage its pressures. The bad news is that they have yet to find the political will to act decisively to minimize the risks. 

As the Fed normalizes its monetary policy and the ECB doubles down on extraordinary measures, we certainly should hope for the best. But we should also be planning for a substantial rise in financial and economic uncertainty. 


Read more at https://www.project-syndicate.org/commentary/federal-reserve-ecb-policy-divergence-by-mohamed-a--el-erian-2015-12#9Fz4wq5QG3xmmBT5.99
DEC 26, 2016
Trump’s Gathering Trade War

@@Stephen S. Roach, former Chairman of Morgan Stanley Asia and the firm's chief economist, is a senior fellow at Yale University's Jackson Institute of Global Affairs and a senior lecturer at Yale's School of Management. He is the author of Unbalanced: The Codependency of America and China.@@

NEW HAVEN – During his campaign, US President-elect Donald Trump used foreign trade as a lightning rod in his supposed defense of the beleaguered American middle class. This is not an uncommon tactic for candidates at either end of the political spectrum. What is unusual is that Trump has not moderated his anti-trade tone since winning. Instead, he has upped the ante and fired a series of early warning shots in what could turn into a full-blown global trade war, with disastrous consequences for the United States and the rest of the world.

Consider Trump’s key personnel decisions. Industrialist Wilbur Ross, the Commerce Secretary-designate, has been vocal in his desire to abrogate America’s “dumb” trade deals. Peter Navarro, an economics professor at the University of California at Irvine, will be the director of the National Trade Council – a new White House policy shop to be set up on a par with the National Security Council and the National Economic Council. Navarro is one of America’s most extreme China hawks. The titles of his two most recent books – Death by China (2011) and Crouching Tiger: What China’s Militarism Means for the World (2015) – speak volumes about his tabloid-level biases.

Ross and Navarro were also co-authors of an economic-policy position paper published on the Trump campaign website that stretched any semblance of credibility. Now they will get the opportunity to put their ideas into practice. And, in fact, the process has already begun.

Trump has made it clear that he will immediately withdraw the US from the Trans-Pacific Partnership (TPP) – in keeping with Ross’s criticism of America’s trade deals. And his brazen willingness to challenge the 40-year-old “One China” policy by speaking directly with Taiwanese President Tsai Ing-wen – to say nothing of his subsequent anti-China tweets – leaves little doubt that his administration will follow Navarro’s prescription and take dead aim at America’s largest and most powerful trading partner.

Of course, Trump, a self-proclaimed master dealmaker, may simply be talking tough, in order to put China and the world on notice that the US is now prepared to operate from a position of strength in the foreign-trade arena. A bold opening gambit, the argument goes, softens the adversary for a more palatable endgame.

But, while such tough talk undoubtedly played well with voters, it fails a key reality check: America’s large trade deficit – a visible manifestation of its low saving – calls into question the very notion of economic strength. A significant domestic saving deficit, such as that which afflicts the US, accounts for America’s insatiable appetite for surplus saving from abroad, which in turn spawns its chronic current-account deficit and a massive trade deficit.

Dealmakers who try to address this macroeconomic problem one country at a time cannot possibly succeed: the US ran trade deficits with 101 countries in 2015. There can be no bilateral fix for a multilateral problem. It’s like the proverbial Dutch boy sticking his finger in a leaky dike. Unless the source of the problem – a saving shortfall that is likely to worsen in the face of Trump’s inevitable widening of federal budget deficits –is addressed, America’s current-account and trade deficits will only widen. Squeezing China would merely shift the trade imbalance to other countries – most likely to higher-cost producers, which would effectively raise the prices of foreign goods sold to hard-pressed American families.

But the story doesn’t end there. The Trump administration is playing with live ammunition, implying profound, global repercussions. Nowhere is this more evident than in the likely Chinese response to America’s new muscle-flexing. The Trump team is dismissive of China’s reaction to its threats – believing that the US has nothing to lose and everything to gain.

Alas, that may not be the case. Like it or not, America and China are locked in a codependent economic relationship. Yes, China depends on US demand for its exports, but the US also depends on China: the Chinese own over $1.5 trillion in US Treasury securities and other US dollar-based assets. Moreover, China is America’s third-largest export market (after Canada and Mexico) and the one that is expanding most rapidly – hardly inconsequential for a growth-starved US economy. It is foolish to think that America holds all the cards in this bilateral economic relationship.

Codependency is a very reactive connection. If one partner changes the terms of engagement, the other is likely to respond in kind. If the US goes after China – as Trump, Navarro, and Ross have long advocated and now seem to be doing – it must also face the consequences. On the economic front, that spells the possibility of reciprocal tariffs on US exports to China, as well as potential ramifications for Chinese purchases of Treasuries. And other countries – tightly linked to China through global supply chains – may well impose countervailing tariffs of their own.

Global trade wars are rare. But, like military conflicts, they often start with accidental skirmishes or misunderstandings. More than 85 years ago, US Senator Reed Smoot and Representative Willis Hawley fired the first shot in sponsoring the Tariff Act of 1930. That led to a catastrophic global trade war, which many believe turned a serious recession into the Great Depression.

It is the height of folly to ignore the lessons of history. For today’s saving-short, deficit-prone US economy, it will take far more than China-bashing to make America great again. Turning trade into a weapon of mass economic destruction could be a policy blunder of epic proportions.

Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from http://tiddlywiki.bidix.info for a complete working Tiddlyspot site.

// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'econ5120';

// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too

// disable autosave in d3
if (window.location.protocol != "file:")
	config.options.chkGTDLazyAutoSave = false;

// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
	SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
	SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
	OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
	DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
	MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");

// create some shadow tiddler content

 "| tiddlyspot password:|<<option pasUploadPassword>>|",
 "| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<br>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
 "| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[blog|http://tiddlyspot.blogspot.com/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"

 "tiddlyspot password:",
 "<<option pasUploadPassword>>",

 "<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"

 "This document is a ~TiddlyWiki from tiddlyspot.com.  A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
 "@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below.  Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
 "<<tiddler TspotControls>>",
 "See also GettingStarted.",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick.  You can make changes and save them locally without being connected to the Internet.  When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]].  Also visit [[TiddlyWiki.org|http://tiddlywiki.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help.  If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your tiddlyspot.com site.  Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."

[[Online Article by Danyang Xie, March 2006|http://www.china-embassy.org/eng/zmgx/1/t242849.htm]]

Despite the noises continuously made by the US congressmen, the Chinese RMB will not be up there on international investors' worry list of global risks. The main risks lie in the United States, the risks that the former Fed chairman, Alan Greenspan, repeatedly warned the public about: the property market bubble and the unsustainable current account deficit.

    The property market bubble in the United States began at about the same time as the "New Economy" and grew with the Internet bubble. Housing prices continue to climb upward in the aftermath of the stock market crash in 2001, thanks to the Fed's aggressive interest rate cuts in an effort to keep American consumers spending and to boost the US economy. The subsequent hikes of the Federal Funds rate since June 2004, to Mr Greenspan's dismay, have failed to push the long-term interest rates up. If anything, the 30-year bond rate has been getting lower in this round of monetary tightening, to the effect that the yield curve turned inverted towards the end of 2005. With the average 30-year mortgage rate staying below 6.5 per cent, the property market simply marches on.

    According to the US Census Bureau press release on February 14, housing construction reached a seasonally adjusted annual pace of 2.28 million homes in January, the highest rate in 33 years. Despite the signs of slowdown in sales volume of existing homes, the price could resume its upward movement when mortgage interest rate stays low.

    As to the unsustainability of the US current account deficit, we should remember that in June 1977, Treasury Secretary W. Michael Blumenthal made comments on the issue when it was only 1 per cent of GDP, thereby establishing his reputation for "talking down" the dollar. Nowadays, the US current account deficit is more than 6 per cent of GDP and the US net investment position is negative with a magnitude over 20 per cent of GDP. Could international investors turn a blind eye to the obvious imbalance, or should they seriously worry about the ultimate crash of the US dollar and the resulting global meltdown as the investors unload their dollar assets?

    A solution to resolving these two issues is needed. And no finger should point at the RMB exchange rate policy. After all, 10 years after the 1985 Plaza Accord and with 100 per cent appreciation of the yen from 200 yen/Dollar to 100 yen/Dollar as well as 72 per cent appreciation of the Deutsche Mark from 2.46 DM/Dollar to 1.43 DM/Dollar, the US current account deficit to GDP barely improved by 1 percentage point. How can anyone think that RMB appreciation would magically shrink the US current account deficit?

    In fact, if substantial RMB appreciation forces bankruptcy in low-profit-margin Chinese firms that have been absorbing the layoffs by State-owned enterprises over the years, the resulting social unrest could destabilize China, which will lead to lower demand for US exports and greater US current account deficit. By improving the flexibility of RMB exchange rate system in a gradual manner and thereby maintaining China's social stability, China contributes the most to global economic stability. This is exactly how an important country such as China should behave as a responsible member of the global economic community.

    The solution is with the United States and not, as pointed out by Governor Ben Bernanke in March 2005, with global savers. The problem is not the global savings glut, it is the lack of savings in the United States on the part of the government no doubt, but also on the part of the individual households. Believe it or not, the extremely low household savings in the United States can be partly attributed to the rising housing price. Think about it: Who needs to save if his house is gaining value astronomically? By the time of retirement, all he needs to do is to cash in on the house or get an annuity through reverse mortgage to pay for all the bills for the rest of his life (increase in rental cost consistently falls short of the increases in housing price). Saving little is indeed rational if the housing price continues to rise at its current pace indefinitely, if down payment and mortgage cost remains low, and if the dollar does not collapse in the foreign exchange market. Unfortunately, these are big "ifs."

    International investors would like to see more responsible behaviour and better targeted policies from the United States. They do not mind that the US property market cools off, as long as it does not melt down. They can cope with the US dollar easing off, but not crashing.

    Yes, the Fed is pushing up the short-term interest rate in order to control the speculative favour in the property market. Yes, Greenspan repeatedly issued public warnings of the property market bubble and the unsustainability of current account deficit. Yes, the US Treasury has abandoned the gibberish that a strong dollar is in line with the US national interest. But when conventional medicines do not work, one needs to look for alternatives.

    "Something unusual is clearly at play here," as Greenspan said. The long rate had stayed stubbornly low and failed to respond to the hikes of the Federal Funds rate. The reason, alas, is that the long rate stayed low precisely because the Fed has been doing such a terrific job so that the long-term inflation expectation is low. The hawks in the Fed have had the upper hand for too long. It is time to welcome the Fed doves. If Bernanke, the new Fed chairman, has his heart where his mind was when he was an academic, we may have some hope. We will see his true colour in the upcoming FOMC meetings. Remember, unusual circumstances require unusual thinking.

    What the Fed should do is to follow "inflation targeting," a framework that Bernanke favoured before he took the helm at the Fed. Furthermore, the medium- to long-term core CPI inflation target could be in the range between 2.25 and 3.25 per cent, namely with an upper bound higher than the average core CPI inflation rate during the past 10 years but nevertheless at a level that will not cause much concern for business planning.

    Bernanke could announce his intention by putting on hold the rise of the Federal Funds rate. This will surprise the Wall Street, but no matter. The expectation of long-term inflation will be revised up and the yield curve will be tilted upward. Given the slightly higher long-term inflation, the dollar will gradually weaken, which in turn feeds back to the international investors demand for higher long-term yield in US government bonds.

    We need the Fed doves to help remove the lid on the long rates and break the wishful thinking that cheap financing is always around for property purchase, for fiscal budget deficit, and for current account deficit. The property market will cool off. With well-targeted CPI inflation rate eating into the value of the house and with dollar depreciating against all major currencies, the property market bubble will shrink over time. The US households will finally begin to realize that they need to save for their retirement nest egg, or else, be prepared to face the reality that the equity they build into and the capital gains on their house won't be enough to keep them happy for the rest of their retirement life.

    The risk of allowing for inflation to resurface is that it may go out of control. But we trust that the Fed has had more experience in putting the inflation rate within the desired range. Will the moderate increase in long-term inflation rate have a negative impact on long-term health of the economy? There is no evidence suggesting this will be the case. A core CPI inflation rate between 2.25 and 3.25 per cent will not put a dent in long-term productivity growth.

    The author is a professor of economics and Senior Wei Lun Fellow at Hong Kong University of Science and Technology.
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 01/03/2019 09:48:04 | Danyang Xie | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
| 04/03/2019 09:33:11 | Danyang Xie | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
| 04/03/2019 09:55:40 | Danyang Xie | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
| 07/03/2019 15:43:21 | YourName | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
| 11/03/2019 10:06:06 | YourName | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
| 11/03/2019 11:55:29 | YourName | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
| 11/03/2019 12:33:49 | YourName | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
| 18/03/2019 13:03:48 | YourName | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
| 19/03/2019 17:44:23 | YourName | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
| 25/03/2019 08:55:23 | YourName | [[/|http://econ5120.tiddlyspot.com/]] | [[store.cgi|http://econ5120.tiddlyspot.com/store.cgi]] | . | [[index.html | http://econ5120.tiddlyspot.com/index.html]] | . |
|''Description:''|Save to web a TiddlyWiki|
|''Date:''|Feb 24, 2008|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 3,
	date: new Date("Feb 24, 2008"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0'

// Environment

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;	// true to activate both in Plugin and UploadService
// Upload Macro

config.macros.upload = {
// default values
	defaultBackupDir: '',	//no backup
	defaultStoreScript: "store.php",
	defaultToFilename: "index.html",
	defaultUploadDir: ".",
	authenticateUser: true	// UploadService Authenticate User
config.macros.upload.label = {
	promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web", 
	saveToDisk: "save to disk",
	uploadLabel: "upload"	

config.macros.upload.messages = {
	noStoreUrl: "No store URL in parmeters or options",
	usernameOrPasswordMissing: "Username or password missing"

config.macros.upload.handler = function(place,macroName,params) {
	if (readOnly)
	var label;
	if (document.location.toString().substr(0,4) == "http") 
		label = this.label.saveLabel;
		label = this.label.uploadLabel;
	var prompt;
	if (params[0]) {
		prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0], 
			(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
	} else {
		prompt = this.label.promptOption;
	createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);

config.macros.upload.action = function(params)
		// for missing macro parameter set value from options
		if (!params) params = {};
		var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
		var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
		var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
		var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
		var username = params[4] ? params[4] : config.options.txtUploadUserName;
		var password = config.options.pasUploadPassword; // for security reason no password as macro parameter	
		// for still missing parameter set default value
		if ((!storeUrl) && (document.location.toString().substr(0,4) == "http")) 
			storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
		if (storeUrl.substr(0,4) != "http")
			storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
		if (!toFilename)
			toFilename = bidix.basename(window.location.toString());
		if (!toFilename)
			toFilename = config.macros.upload.defaultToFilename;
		if (!uploadDir)
			uploadDir = config.macros.upload.defaultUploadDir;
		if (!backupDir)
			backupDir = config.macros.upload.defaultBackupDir;
		// report error if still missing
		if (!storeUrl) {
			return false;
		if (config.macros.upload.authenticateUser && (!username || !password)) {
			return false;
		bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password); 
		return false; 

config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir) 
	if (!storeUrl)
		return null;
		var dest = bidix.dirname(storeUrl);
		if (uploadDir && uploadDir != '.')
			dest = dest + '/' + uploadDir;
		dest = dest + '/' + toFilename;
	return dest;

// uploadOptions Macro

config.macros.uploadOptions = {
	handler: function(place,macroName,params) {
		var wizard = new Wizard();
		var markList = wizard.getElement("markList");
		var listWrapper = document.createElement("div");
		var uploadCaption;
		if (document.location.toString().substr(0,4) == "http") 
			uploadCaption = config.macros.upload.label.saveLabel;
			uploadCaption = config.macros.upload.label.uploadLabel;
				{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption, 
					onClick: config.macros.upload.action},
				{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
	options: [
	refreshOptions: function(listWrapper) {
		var opts = [];
		for(i=0; i<this.options.length; i++) {
			var opt = {};
			opt.option = "";
			n = this.options[i];
			opt.name = n;
			opt.lowlight = !config.optionsDesc[n];
			opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
		var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
		for(n=0; n<opts.length; n++) {
			var type = opts[n].name.substr(0,3);
			var h = config.macros.option.types[type];
			if (h && h.create) {
	onCancel: function(e)
		return false;
	wizardTitle: "Upload with options",
	step1Title: "These options are saved in cookies in your browser",
	step1Html: "<input type='hidden' name='markList'></input><br>",
	cancelButton: "Cancel",
	cancelButtonPrompt: "Cancel prompt",
	listViewTemplate: {
		columns: [
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'} 

// upload functions

if (!bidix.upload) bidix.upload = {};

if (!bidix.upload.messages) bidix.upload.messages = {
	//from saving
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to upload backup file",
	rssSaved: "RSS feed uploaded",
	rssFailed: "Failed to upload RSS feed file",
	emptySaved: "Empty template uploaded",
	emptyFailed: "Failed to upload empty template file",
	mainSaved: "Main TiddlyWiki file uploaded",
	mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
	//specific upload
	loadOriginalHttpPostError: "Can't get original file",
	aboutToSaveOnHttpPost: 'About to upload on %0 ...',
	storePhpNotFound: "The store script '%0' was not found."

bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
	var callback = function(status,uploadParams,original,url,xhr) {
		if (!status) {
		if (bidix.debugMode) 
		// Locate the storeArea div's 
		var posDiv = locateStoreArea(original);
		if((posDiv[0] == -1) || (posDiv[1] == -1)) {
	if(onlyIfDirty && !store.isDirty())
	// save on localdisk ?
	if (document.location.toString().substr(0,4) == "file") {
		var path = document.location.toString();
		var localPath = getLocalPath(path);
	// get original
	var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
	var originalPath = document.location.toString();
	// If url is a directory : add index.html
	if (originalPath.charAt(originalPath.length-1) == "/")
		originalPath = originalPath + "index.html";
	var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
	var log = new bidix.UploadLog();
	log.startUpload(storeUrl, dest, uploadDir,  backupDir);
	if (bidix.debugMode) 
		alert("about to execute Http - GET on "+originalPath);
	var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
	if (typeof r == "string")
	return r;

bidix.upload.uploadRss = function(uploadParams,original,posDiv) 
	var callback = function(status,params,responseText,url,xhr) {
		if(status) {
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
		} else {
	// do uploadRss
	if(config.options.chkGenerateAnRssFeed) {
		var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
		var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		var rssString = generateRss();
		// no UnicodeToUTF8 conversion needed when location is "file" !!!
		if (document.location.toString().substr(0,4) != "file")
			rssString = convertUnicodeToUTF8(rssString);	
	} else {

bidix.upload.uploadMain = function(uploadParams,original,posDiv) 
	var callback = function(status,params,responseText,url,xhr) {
		var log = new bidix.UploadLog();
		if(status) {
			// if backupDir specified
			if ((params[3]) && (responseText.indexOf("backupfile:") > -1))  {
				var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
		} else {
	// do uploadMain
	var revised = bidix.upload.updateOriginal(original,posDiv);

bidix.upload.httpUpload = function(uploadParams,data,callback,params)
	var localCallback = function(status,params,responseText,url,xhr) {
		url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
		if (xhr.status == 404)
		if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
			if (responseText.indexOf("Debug mode") >= 0 )
				responseText = responseText.substring(responseText.indexOf("\n\n")+2);
		} else if (responseText.charAt(0) != '0') 
		if (responseText.charAt(0) != '0')
			status = null;
	// do httpUpload
	var boundary = "---------------------------"+"AaB03x";	
	var uploadFormName = "UploadPlugin";
	// compose headers data
	var sheader = "";
	sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
	sheader += uploadFormName +"\"\r\n\r\n";
	sheader += "backupDir="+uploadParams[3] +
				";user=" + uploadParams[4] +
				";password=" + uploadParams[5] +
				";uploaddir=" + uploadParams[2];
	if (bidix.debugMode)
		sheader += ";debug=1";
	sheader += ";;\r\n"; 
	sheader += "\r\n" + "--" + boundary + "\r\n";
	sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
	sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
	sheader += "Content-Length: " + data.length + "\r\n\r\n";
	// compose trailer data
	var strailer = new String();
	strailer = "\r\n--" + boundary + "--\r\n";
	data = sheader + data + strailer;
	if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
	var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
	if (typeof r == "string")
	return r;

// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
	if (!posDiv)
		posDiv = locateStoreArea(original);
	if((posDiv[0] == -1) || (posDiv[1] == -1)) {
	var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
				store.allTiddlersAsHtml() + "\n" +
	var newSiteTitle = getPageTitle().htmlEncode();
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
	revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
	revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
	revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
	return revised;

// UploadLog
// config.options.chkUploadLog :
//		false : no logging
//		true : logging
// config.options.txtUploadLogMaxLine :
//		-1 : no limit
//      0 :  no Log lines but UploadLog is still in place
//		n :  the last n lines are only kept
//		NaN : no limit (-1)

bidix.UploadLog = function() {
	if (!config.options.chkUploadLog) 
		return; // this.tiddler = null
	this.tiddler = store.getTiddler("UploadLog");
	if (!this.tiddler) {
		this.tiddler = new Tiddler();
		this.tiddler.title = "UploadLog";
		this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
		this.tiddler.created = new Date();
		this.tiddler.modifier = config.options.txtUserName;
		this.tiddler.modified = new Date();
	return this;

bidix.UploadLog.prototype.addText = function(text) {
	if (!this.tiddler)
	// retrieve maxLine when we need it
	var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
	if (isNaN(maxLine))
		maxLine = -1;
	// add text
	if (maxLine != 0) 
		this.tiddler.text = this.tiddler.text + text;
	// Trunck to maxLine
	if (maxLine >= 0) {
		var textArray = this.tiddler.text.split('\n');
		if (textArray.length > maxLine + 1)
			this.tiddler.text = textArray.join('\n');		
	// update tiddler fields
	this.tiddler.modifier = config.options.txtUserName;
	this.tiddler.modified = new Date();
	// refresh and notifiy for immediate update
	store.notify(this.tiddler.title, true);

bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {
	if (!this.tiddler)
	var now = new Date();
	var text = "\n| ";
	var filename = bidix.basename(document.location.toString());
	if (!filename) filename = '/';
	text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
	text += config.options.txtUserName + " | ";
	text += "[["+filename+"|"+location + "]] |";
	text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
	text += uploadDir + " | ";
	text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
	text += backupDir + " |";

bidix.UploadLog.prototype.endUpload = function(status) {
	if (!this.tiddler)
	this.addText(" "+status+" |");

// Utilities

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  && 
			((ext.major > major) || 
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"

bidix.dirname = function(filePath) {
	if (!filePath) 
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(0, lastpos);
	} else {
		return filePath.substring(0, filePath.lastIndexOf("\\"));

bidix.basename = function(filePath) {
	if (!filePath) 
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("#")) != -1) 
		filePath = filePath.substring(0, lastpos);
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(lastpos + 1);
	} else
		return filePath.substring(filePath.lastIndexOf("\\")+1);

bidix.initOption = function(name,value) {
	if (!config.options[name])
		config.options[name] = value;

// Initializations

// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);

// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");

	txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
	txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
	txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
	txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
	txtUploadUserName: "Upload Username",
	pasUploadPassword: "Upload Password",
	chkUploadLog: "do Logging in UploadLog (default: true)",
	txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"

// Options Initializations

// Backstage
	uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}


bidix.UploadLog.prototype.endUpload = function(status) { 
        if (this.tiddler) this.addText(" "+status+" |"); 
        window.location.reload( false ); 
!Final Exam Date and Venue: Mar 26, Tuesday, 2pm to 5pm. Room 4621, Lift 32
![[Sample Exam Questions|http://ihome.ust.hk/~dxie/Econ5120/Sample.pdf]]
**[[Market-Oriented Train-Ticket Pricing during Spring Festival?|https://8fishhkust.art.blog/2019/02/08/the-difficulty-of-buying-train-tickets-under-chunyun-and-its-solution/]]
**[[Beautiful Great Bay Area|https://8fishhkust.art.blog/2019/02/17/beautiful-greater-bay-area/]]
**[[Urbanization in China|https://8fishhkust.art.blog/2019/02/25/urbanization-in-china/]]
**[[Bike-sharing platforms|https://8fishhkust.art.blog/2019/03/03/different-situation-of-bicycle-sharing-in-china-and-the-united-stated-the-dilemma-and-opportunity/]]
**[[Overseas Success of TikTok|https://8fishhkust.art.blog/2019/03/11/oversea-success-of-tiktok/]]
*[[The London School|https://thelondonschool2019.wordpress.com/]]
**[[A System Clash|https://thelondonschool2019.wordpress.com/2019/02/11/more-than-just-a-trade-war-a-system-clash/]]
**[[China's Internet Censorship|https://thelondonschool2019.wordpress.com/2019/02/17/implications-of-chinas-internet-censorship/]]
**[[Huawei from 5G to trade war|https://thelondonschool2019.wordpress.com/2019/02/25/the-huawei-case-from-5g-to-trade-war/]]
**[[Trump-Kim Summit: Impact on China|https://thelondonschool2019.wordpress.com/2019/03/01/trump-kim-summit-the-impact-on-china/]]
**[[China's $20 Billion Venezuela Problem|https://thelondonschool2019.wordpress.com/2019/03/11/chinas-20-billion-venezuela-problem/]]
*[[Econ Cafe|https://econcafecom.wordpress.com/]]
**[[QE in China?|https://econcafecom.wordpress.com/2019/02/10/china-one-step-closer-to-full-on-quantitative-easing-qe-trying-to-boost-its-banks-lending/]]
**[[Economic Downturn and Monetary Policy|https://econcafecom.wordpress.com/2019/02/18/chinese-economy-downturn-and-monetary-policy/]]
**[[New Board for Technology Start-ups|https://econcafecom.wordpress.com/2019/02/24/china-to-speed-up-creation-of-new-board-for-technology-start-ups-at-shanghai-stock-exchange/]]
**[[Electric Vehicles|https://econcafecom.wordpress.com/2019/03/03/environmental-issue-and-electric-car-industry/]]
**[[Environmental Demands and Economic Growth|https://econcafecom.wordpress.com/2019/03/11/environmental-demands-and-economic-growth-paradox-in-china/]]
*[[New Silk Road|https://hkustnewsilkroad.wordpress.com/]]
**[[The Effect of BRI on a Frontier Market|https://hkustnewsilkroad.wordpress.com/2019/02/10/the-effect-of-the-belt-road-initiative-on-a-frontier-market/]]
**[[China 2019: Growth and Opening|https://hkustnewsilkroad.wordpress.com/2019/02/17/china-2019-growth-and-opening/]]
**[[Secrets and Lies|https://hkustnewsilkroad.wordpress.com/2019/02/24/secrets-lies-a-country-at-risk/]]
**[[China and Latin America|https://hkustnewsilkroad.wordpress.com/2019/03/03/china-and-latam-a-win-win-relationship/]]
**[[Cross Strait Relations|https://hkustnewsilkroad.wordpress.com/2019/03/10/to-be-or-not-to-be-independent-cross-strait-relations-and-its-effect-on-china-in-the-global-economy/]]
**[[PDD and the Youth in Town|https://group123.home.blog/2019/02/11/the-expansion-of-pingduoduo-and-youth-in-town/]]
**[[Mobile Payments|https://group123.home.blog/2019/02/17/the-proliferation-of-mobile-payments-and-its-impacts/]]
**[[China Bond Default Analysis|https://group123.home.blog/2019/02/25/china-bond-default-analysis-from-property-sector/]]
**[[House Price|https://group123.home.blog/2019/03/03/house-price-slowdown-in-china/]]
**[[2019 Tax Cut in China|https://group123.home.blog/2019/03/10/2019-tax-cuts-in-china/]]
*[[The 2x2 Matrix|https://thetwobytwomatrix.home.blog/]]
**[[The Huawei Saga|https://thetwobytwomatrix.home.blog/2019/02/11/continuing-saga-of-huawei-dispute-sentencing-of-robert-schellenberg/]]
**[[Respect of IP Laws|https://thetwobytwomatrix.home.blog/2019/02/17/respect-of-ip-laws-a-contentious-subject-between-the-us-and-china/]]
**[[Apple's China Trouble|https://thetwobytwomatrix.home.blog/2019/02/24/why-apples-china-troubles-arent-going-away-anytime-soon/]]
**[[China's Water Crisis|https://thetwobytwomatrix.home.blog/2019/03/03/chinas-water-crisis/]]
**[[The State Advances, the Private Sector Retreats|https://thetwobytwomatrix.home.blog/2019/03/09/guo-jin-min-tui-the-state-advances-the-private-sector-retreats/]]
*[[The China Reforms|https://chinareforms.wordpress.com/]]
**[[Births in China|https://chinareforms.wordpress.com/2019/02/10/births-in-china/]]
**[[Robots vs Trump|https://chinareforms.wordpress.com/2019/02/18/robots-vs-trump-the-effects-of-increased-introduction-of-robotics-and-the-china-us-trade-war-on-employment-in-china/]]
**[[Chinese Happiness?|https://chinareforms.wordpress.com/2019/02/24/chinese-happiness-what-accounts-for-it/]]
**[[China's Consumers|https://chinareforms.wordpress.com/2019/03/01/higher-consumption-necessary-for-chinese-economic-growth/]]
**[[Chinese Influx into the Philippines|https://chinareforms.wordpress.com/2019/03/11/the-effects-of-chinese-influx-into-the-philippines/]]
if (!version.extensions.YourSearchPlugin) {

version.extensions.YourSearchPlugin = {
	major: 2, minor: 1, revision: 5,
	source: "http://tiddlywiki.abego-software.de/#YourSearchPlugin",
	licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
	copyright: "Copyright (c) abego Software GmbH, 2005-2010 (www.abego-software.de)"

if (!window.abego) window.abego = {};

// define the Array forEach when not yet defined (e.g. by Mozilla)
if (!Array.forEach) {
    Array.forEach = function(obj, callback, thisObj) {
        for (var i = 0,len = obj.length; i < len; i++)
            callback.call(thisObj, obj[i], i, obj);
    Array.prototype.forEach = function(callback, thisObj) {
        for (var i = 0,len = this.length; i < len; i++)
            callback.call(thisObj,  this[i], i, this);

abego.toInt = function(s, defaultValue) {
	if (!s) return defaultValue;
	var n = parseInt(s);
	return (n == NaN) ? defaultValue : n;

abego.createEllipsis = function(place) {
	var e = createTiddlyElement(place,"span");
	e.innerHTML = "&hellip;";

//#concept Object
abego.shallowCopy = function(object) {
	if (!object)
		return object;
	var result = {};
	for (var n in object) 
		result[n] = object[n];
	return result;

// Returns a shallow copy of the options, or a new, empty object if options is null/undefined.
// @param options [may be null/undefined]
//#concept Object, Options
//#import abego.shallowCopy
abego.copyOptions = function(options) {
	return !options ? {} : abego.shallowCopy(options);

//#import abego.define-namespace
// returns the number of occurances of s in the text
abego.countStrings = function(text, s) {
	if (!s)
		return 0;
	var len = s.length;
	var n = 0;
	var lastIndex = 0;
	while (1) {
		var i = text.indexOf(s, lastIndex);
		if (i < 0)
			return n;
		lastIndex = i+len;
	return n;
};// Returns the content of the first "braced" text {...}
// Also takes care of nested braces
// Returns undefined when no braced text is found or it is not properly nested
// @param [optional] when defined and a braced text is found lastIndexRef.lastIndex will contain the index of the char following the (final) closing brace on return.
abego.getBracedText = function(text, offset,lastIndexRef) {
	if (!offset) offset = 0;
	var re = /\{([^\}]*)\}/gm;
	re.lastIndex = offset;
	var m = re.exec(text);
	if (m) {
		// The matching stopped at the first closing brace.
		// But if the matched text contains opening braces 
		// this is not the final closing brace.
		// Handle this case specially, find the "corresponding" closing brace
		var s = m[1];
		var nExtraOpenBrace = abego.countStrings(s,"{");
		if (!nExtraOpenBrace) {
			if (lastIndexRef)
				lastIndexRef.lastIndex = re.lastIndex;
			// simple case: no nested braces
			return s;

		// special case: "nested braces"
		var len = text.length;
		for (var i = re.lastIndex; i < len && nExtraOpenBrace; i++) {
			var c = text.charAt(i);
			if (c == "{") 
			else if (c == "}")
		if (!nExtraOpenBrace) {
			// found the corresponding "}".
			if (lastIndexRef)
				lastIndexRef.lastIndex = i-1;
			return text.substring(m.index+1, i-1);
	// no return means: return undefined;

// Returns an array with those items from the array that pass the given test
// @param test an one-arg boolean function that returns true when the item should be added.
// @param testObj [optional] the receiver for the test function (global if undefined or null)
// @param result [optional] an array. When define the selected items are added to this array, otherwise a new array is used.
//#import Array.prototype.forEach
abego.select = function(array,test,testObj,result) {
	if (!result) result = [];
	array.forEach(function(t) {
		if (test.call(testObj,t)) 
	return result;

// A portable way to "consume an event"
// (Uses "stopPropagation" and "preventDefault", but will also "cancelBubble",
// even though this is a "non-standard method" , just in case).
abego.consumeEvent = function(e) {
	if (e.stopPropagation) e.stopPropagation();
	if (e.preventDefault) e.preventDefault();
	e.cancelBubble = true;
	e.returnValue = true;

// Class abego.TiddlerFilterTerm =================================================================
// Used to check if a tiddler contains a given text.
// A list of fields (standard and/or extended) may be specified to restrict the search to certain fields. 
// When no explicit fields are given the fields defined by defaultFields are checked, plus all extended 
// fields (when options.withExtendedFields is true).
// @param options [may be null/undefined]
//		options.fields @seeParam abego.MultiFieldRegExpTester.fields
// 		options.withExtendedFields @seeParam abego.MultiFieldRegExpTester.withExtendedFields  
// 		options.caseSensitive [Default: false]
// 		options.fullWordMatch [Default: false]
// 		options.textIsRegExp [Default: false] when true the given text is already a regExp
//#import abego.MultiFieldRegExpTester
abego.TiddlerFilterTerm = function(text,options) {
	if (!options) options = {};

	var reText = text;
	if (!options.textIsRegExp) {
		reText = text.escapeRegExp();
		if (options.fullWordMatch) 
			reText = "\\b"+reText+"\\b";
	var regExp = new RegExp(reText, "m"+(options.caseSensitive ? "" : "i"));

	this.tester = new abego.MultiFieldRegExpTester(regExp, options.fields, options.withExtendedFields);

abego.TiddlerFilterTerm.prototype.test = function(tiddler) {
	return this.tester.test(tiddler);

// Recognize a string like
//     "Some Title. Some content text #Tag1 #Tag2 Tag3"
// with the tags and the text being optional.
// Also the period at the end of the title is optional when no content text is specified)
// Returns the result in an object with properties "title" and "params",
// with "params" following the parseParams format, containing the "tag" and "text" arguments.
abego.parseNewTiddlerCommandLine = function(s) {
	var m = /(.*?)\.(?:\s+|$)([^#]*)(#.*)?/.exec(s);
	if (!m) 
		m = /([^#]*)()(#.*)?/.exec(s);
	if (m) {
		var r;
		if (m[3]) {
			var s2 = m[3].replace(/#/g,"");
			r = s2.parseParams("tag");
		} else
			r = [[]];
		// add the text parameter
		var text = m[2]?m[2].trim():"";
		r.push({name: "text", value: text});
		r[0].text = [text];
		return {title: m[1].trim(), params: r}; 
	} else
		return {title: s.trim(),params: [[]]};
// 		options.defaultFields [@seeOptionDefault abego.TiddlerFilterTerm.fields] fields to check when no fields are explicitly specified in queryText.
// 		options.withExtendedFields [@seeOptionDefault abego.TiddlerFilterTerm.withExtendedFields] when true and no fields are explicitly specified in queryText also the extended fields are considered (in addition to the ones in defaultFields).
// @seeOptions abego.TiddlerFilterTerm (-fields -fullWordMatch -withExtendedFields)
//#import abego.getBracedText
//#import abego.copyOptions
//#import abego.TiddlerFilterTerm
abego.parseTiddlerFilterTerm = function(queryText,offset,options) {
	// group 1: {...} 		(JavaScript expression)
	// group 2: '=' 		(full word match (optional))
	// group 3: [!%#] 		(field selection short cuts)
	// group 4: fieldName ':'
	// group 5: String literal "..."
	// group 6: RegExp literal /.../
	// group 7: scheme '://' nonSpaceChars
	// group 8: word
	var re = /\s*(?:(?:\{([^\}]*)\})|(?:(=)|([#%!])|(?:(\w+)\s*\:(?!\/\/))|(?:(?:("(?:(?:\\")|[^"])+")|(?:\/((?:(?:\\\/)|[^\/])+)\/)|(\w+\:\/\/[^\s]+)|([^\s\)\-\"]+)))))/mg; // " <- The syntax highlighting of my editors gets confused without this quote
	var shortCuts = {'!':'title','%':'text','#':'tags'};
	var fieldNames = {};
	var fullWordMatch;
	re.lastIndex = offset;
	while (1) {
		var i = re.lastIndex;
		var m = re.exec(queryText);
		if (!m || m.index != i) 
			throw "Word or String literal expected";
		if (m[1]) {
			var lastIndexRef = {};
			var code = abego.getBracedText(queryText,0,lastIndexRef);
			if (!code)
				throw "Invalid {...} syntax";
			var f = Function("tiddler","return ("+code+");");
			return {func: f,
					markRE: null};
		if (m[2])
			fullWordMatch = true;
		else if (m[3]) 
			fieldNames[shortCuts[m[3]]] = 1;
		else if (m[4]) 
			fieldNames[m[4]] = 1;
		else {
			var textIsRegExp = m[6];
			var text = m[5] ? window.eval(m[5]) : m[6] ? m[6] :  m[7] ? m[7] : m[8];
			var options = abego.copyOptions(options);
			options.fullWordMatch = fullWordMatch;
			options.textIsRegExp = textIsRegExp;

			var fields = [];
			for (var n in fieldNames)
			if (fields.length == 0) {
				options.fields = options.defaultFields;
			} else {
				options.fields = fields;
				options.withExtendedFields	= false;
			var term = new abego.TiddlerFilterTerm(text,options);
			var markREText = textIsRegExp ? text : text.escapeRegExp();
			if (markREText && fullWordMatch)
				markREText = "\\b"+markREText+"\\b";
			return {func: function(tiddler) {return term.test(tiddler);},
					markRE: markREText ? "(?:"+markREText+")" : null};

// Class abego.BoolExp =================================================================
// Allows the execution/evaluation of a boolean expression, according to this syntax:
// boolExpression    : unaryExpression (("AND"|"OR"|"&&"|"||")? unaryExpression)*
//                   ;
// unaryExpression   : ("not"|"-")? primaryExpression
//                   ;
// primaryExpression : "(" boolExpression ")" 
//                   | Term
//                   ;
// For flexibility the Term syntax is defined by a separate parse function.
// Notice that there is no precedence between "AND" and "OR" operators, i.e. they are evaluated from left to right.
// To evaluate the expression in a given context use code like this:
//	var be = new abego.BoolExp(s, termParseFunc);
//  var result = be.exec(context);
// @param s the text defining the expression 
// @param parseTermFunc a Function(text,offset,options) that parses the text starting at offset for a "Term" and returns an object with properties {func: Function(context), lastIndex: ...}. func is the function to be used to evaluate the term in the given context.
// @param options [may be null/undefined] (is also passed to the parseTermFunc)
// 			options.defaultOperationIs_OR [Default: false] When true the concatenation of unaryExpressions (without an operator) is interpreted as an "OR", otherwise as an "AND".
// 			options.caseSensitive [default: false]
abego.BoolExp = function(s, parseTermFunc, options) {
	this.s = s;
	var defaultOperationIs_OR = options && options.defaultOperationIs_OR;
	var reStart = /\s*(?:(\-|not)|(\())/gi; 		// group 1: NOT, group2 "("
	var reCloseParenthesis = /\s*\)/g;  			// match )
	var reAndOr = /\s*(?:(and|\&\&)|(or|\|\|))/gi; 	// group 1: AND, group 2: OR
	var reNonWhiteSpace = /\s*[^\)\s]/g;
	var reNot_Parenthesis = /\s*(\-|not)?(\s*\()?/gi;
	var parseBoolExpression; //#Pre-declare function name to avoid problem with "shrinkSafe"
	var parseUnaryExpression = function(offset) {
		reNot_Parenthesis.lastIndex = offset;
		var m = reNot_Parenthesis.exec(s);
		var negate;
		var result;
		if (m && m.index == offset) {
			offset += m[0].length;
			negate = m[1];
			if (m[2]) {
				// case:  (...)
				var e = parseBoolExpression(offset);
				reCloseParenthesis.lastIndex = e.lastIndex;
				if (!reCloseParenthesis.exec(s))
					throw "Missing ')'";
				result = {func: e.func, lastIndex: reCloseParenthesis.lastIndex, markRE: e.markRE};
		if (!result)
			result = parseTermFunc(s,offset,options);

		if (negate) {
			result.func = (function(f){return function(context) {return !f(context);}})(result.func);
			// don't mark patterns that are negated
			// (This is essential since the marking may also be used to calculate "ranks". If we
			// would also count the negated matches (i.e. that should not exist) the rank may get too high)
			result.markRE = null;
		return result;

	parseBoolExpression = function(offset) {
		var result = parseUnaryExpression(offset);
		while (1) {
			var l = result.lastIndex;
			reAndOr.lastIndex = l;
			var m = reAndOr.exec(s);
			var isOrCase;
			var nextExp;
			if (m && m.index == l) {
				isOrCase = !m[1];
				nextExp = parseUnaryExpression(reAndOr.lastIndex);
			} else {
				// no "AND" or "OR" found. 
				// Maybe it is a concatenations of parseUnaryExpression without operators
				try {
					nextExp = parseUnaryExpression(l);
				} catch (e) {
					// no unary expression follows. We are done
					return result;
				isOrCase = defaultOperationIs_OR;
			result.func = (function(func1, func2, isOrCase) {
					return isOrCase
						? function(context) {return func1(context) || func2(context);}
						: function(context) {return func1(context) && func2(context);};
			result.lastIndex = nextExp.lastIndex;
			if (!result.markRE)
				result.markRE = nextExp.markRE;
			else if (nextExp.markRE) 
				result.markRE = result.markRE + "|" + nextExp.markRE;
	var expr = parseBoolExpression(0);
	this.evalFunc = expr.func;
	if (expr.markRE)
		this.markRegExp = new RegExp(expr.markRE, options.caseSensitive ? "mg" : "img");

abego.BoolExp.prototype.exec = function() {
	return this.evalFunc.apply(this,arguments);

abego.BoolExp.prototype.getMarkRegExp = function() {
	return this.markRegExp;

abego.BoolExp.prototype.toString = function() {
	return this.s;

// Class abego.MultiFieldRegExpTester ==================================================================
// @param fields [optional; Default: ["title","text","tags"]] array of names of fields to be considered
// @param withExtendedFields [optional; Default: false] when true also extended fields are considered (in addition to the ones given in 'fields')
abego.MultiFieldRegExpTester = function(re, fields, withExtendedFields) {
	this.re = re;
	this.fields = fields ? fields : ["title","text","tags"];
	this.withExtendedFields = withExtendedFields;

// Returns the name of the first field found that value succeeds the given test,
// or null when no such field is found
abego.MultiFieldRegExpTester.prototype.test = function(tiddler) {
	var re = this.re;
	// Check the fields explicitly specified
	for (var i = 0; i < this.fields.length; i++) {
		var s = store.getValue(tiddler, this.fields[i]);
		if (typeof s == "string" && re.test(s))
			return this.fields[i];		
	// Check the extended fields (if required)
	if (this.withExtendedFields) 
		return store.forEachField(
				function(tiddler, fieldName, value) {
					return typeof value == "string" && re.test(value)?fieldName:null;
				}, true);
	return null;

// Class abego.TiddlerQuery ==================================================================
//#import abego.select
//#import abego.MultiFieldRegExpTester
abego.TiddlerQuery = function(queryText,caseSensitive,useRegExp,defaultFields,withExtendedFields) {
	if (useRegExp) {
		this.regExp = new RegExp(queryText, caseSensitive ? "mg" : "img");
		this.tester = new abego.MultiFieldRegExpTester(this.regExp, defaultFields, withExtendedFields);
	} else {
		this.expr = new abego.BoolExp(
				abego.parseTiddlerFilterTerm, {
				defaultFields: defaultFields,
				caseSensitive: caseSensitive,
				withExtendedFields: withExtendedFields});
	this.getQueryText = function() {
		return queryText;
	this.getUseRegExp = function() {
		return useRegExp;
	this.getCaseSensitive = function() {
		return caseSensitive;
	this.getDefaultFields = function() {
		return defaultFields;
	this.getWithExtendedFields = function() {
		return withExtendedFields;

// Returns true iff the query includes the given tiddler
// @param tiddler [may be null/undefined]
abego.TiddlerQuery.prototype.test = function(tiddler) {
	if (!tiddler) return false;
	if (this.regExp) {
		return this.tester.test(tiddler);
	return this.expr.exec(tiddler);

// Returns an array with those tiddlers from the tiddlers array that match the query.
abego.TiddlerQuery.prototype.filter = function(tiddlers) {
	return abego.select(tiddlers,this.test,this);

abego.TiddlerQuery.prototype.getMarkRegExp = function() {
	if (this.regExp) {
		// Only use the regExp for marking when it does not match the empty string.
		return "".search(this.regExp) >= 0 ? null :  this.regExp;
	return this.expr.getMarkRegExp();

abego.TiddlerQuery.prototype.toString = function() {
	return (this.regExp ? this.regExp : this.expr).toString();

// Class abego.PageWiseRenderer ================================================
// Subclass or instance must implement getItemsPerPage function;
// They should also implement onPageChanged and refresh the container of the
// PageWiseRenderer on that event.
//#import abego.toInt
abego.PageWiseRenderer = function() {
	this.firstIndexOnPage = 0; // The index of the first item of the lastResults list displayed on the search result page

merge(abego.PageWiseRenderer.prototype, {
	setItems: function(items) {
		this.items = items;
	// Maximum number of pages listed in the navigation bar (before or after the current page)
	getMaxPagesInNavigation: function() {
		return 10;
	getItemsCount: function(items) {
		return this.items ? this.items.length : 0;
	getCurrentPageIndex: function() {
		return Math.floor(this.firstIndexOnPage / this.getItemsPerPage());
	getLastPageIndex: function() {
		return Math.floor((this.getItemsCount()-1) / this.getItemsPerPage())
	setFirstIndexOnPage: function(index) {
		this.firstIndexOnPage = Math.min(Math.max(0, index), this.getItemsCount()-1);
	getFirstIndexOnPage: function() {
		// Ensure that the firstIndexOnPage is really a page start. 
		// This may have become violated when getItemsPerPage has changed,
		// (e.g. when switching between previewText and simple mode.)
		this.firstIndexOnPage = Math.floor(this.firstIndexOnPage / this.getItemsPerPage()) * this.getItemsPerPage();
		return this.firstIndexOnPage;
	getLastIndexOnPage: function() {
		return Math.min(this.getFirstIndexOnPage()+this.getItemsPerPage()-1, this.getItemsCount()-1);
	onPageChanged: function(pageIndex,oldPageIndex) {
	renderPage: function(itemRenderer) {
		if (itemRenderer.beginRendering)
		try {
			// When there are items found add them to the result page (pagewise)
			if (this.getItemsCount()) {
				// Add the items of the current page
				var lastIndex = this.getLastIndexOnPage();
				var iInPage = -1;
				for (var i=this.getFirstIndexOnPage(); i <= lastIndex; i++) {
		} finally {
			if (itemRenderer.endRendering)
	addPageNavigation: function(place) {
		if (!this.getItemsCount()) return;
		var self = this;
		var onNaviButtonClick = function(e) {
			if (!e) var e = window.event;


			var pageIndex = abego.toInt(this.getAttribute("page"),0);
			var oldPageIndex = self.getCurrentPageIndex();
			if (pageIndex == oldPageIndex)
			var index = pageIndex * self.getItemsPerPage();
		var button;
		var currentPageIndex = this.getCurrentPageIndex();
		var lastPageIndex = this.getLastPageIndex();
		if (currentPageIndex > 0) {
			button = createTiddlyButton(place, "Previous", "Go to previous page (Shortcut: Alt-'<')", onNaviButtonClick, "prev");
		for (var i = -this.getMaxPagesInNavigation(); i < this.getMaxPagesInNavigation(); i++) {
			var pageIndex = currentPageIndex+i;
			if (pageIndex < 0) continue;
			if (pageIndex > lastPageIndex) break;
			var pageNo = (i+currentPageIndex+1).toString();
			var buttonClass = pageIndex == currentPageIndex ? "currentPage" : "otherPage";
			button = createTiddlyButton(place, pageNo, "Go to page %0".format([pageNo]), onNaviButtonClick, buttonClass);
		if (currentPageIndex < lastPageIndex) {
			button = createTiddlyButton(place, "Next", "Go to next page (Shortcut: Alt-'>')", onNaviButtonClick, "next");

// Class abego.LimitedTextRenderer ===========================================================
// Renders a given text, ensuring that a given limit of number of characters 
// is not exceeded.
// A "markRegExp" may be specified. Substring matching this regular expression 
// ("matched strings") are rendered with the class "marked". 
// if the given text is longer than the limit the matched strings are preferred 
// to be included in the rendered text (with some leading and trailing "context text"). 
// Example:
//     var renderer = new abego.LimitedTextRenderer();
//     var place = ... // a DOM element that should contain the rendered (limited) text
//     var s = "This is another 'Hello World' example, as saying 'Hello' is always nice. So let's say it again: >Hello!<";
//     var maxLen = 50;
//     var markRE = /hello/gi;
//     renderer.render(place,s,maxLen,markRE);
//#import abego.createEllipsis
abego.LimitedTextRenderer = function() {
	var minMatchWithContextSize = 40; 
	var maxMovementForWordCorrection = 4; // When a "match" context starts or end on a word the context borders may be changed to at most this amount to include or exclude the word.
	// Ranges
	// Objects with a "start" and "end" property (not a specific class). 
	// In a corresponding "Ranges array" these objects are sorted by their start 
	// and no Range object intersects/touches any other in the array.
	// Adds the Range [startIndex,endIndex[ to the ranges, ensuring that the Ranges
	// in the array are sorted by their start and no Range object 
	// intersects/touches any other in the array (i.e. possibly the new Range is 
	// "merged" with existing ranges)
	// @param ranges array of Range objects
	var addRange = function(ranges, startIndex, endIndex) {
		var n = ranges.length;
		// When there are no ranges in ranges, just add it.
		if (n == 0) {
			ranges.push({start: startIndex, end: endIndex});
		var i = 0;
		for (; i < n; i++) {
			var range = ranges[i];
			// find the first range that intersects or "touches" [startIndex, endIndex[
			if (range.start <= endIndex && startIndex <= range.end) {
				// Found.
				var r;
				// find the first range behind the new range that does not interfere
				var rIndex = i+1;
				for (; rIndex < n; rIndex++) {
					r = ranges[rIndex];
					if (r.start > endIndex || startIndex > range.end) {
				// Replace the ranges i to rIndex-1 with the union of the new range with these ranges.
				var unionStart = startIndex;
				var unionEnd = endIndex;
				for (var j = i; j < rIndex; j++) {
					r = ranges[j];
					unionStart = Math.min(unionStart, r.start);
					unionEnd = Math.max(unionEnd, r.end);
				ranges.splice(i, rIndex-i, {start: unionStart, end: unionEnd});
			// if we found a range R that is right of the new range there is no
			// intersection and we can insert the new range before R.
			if (range.start > endIndex) {
		// When we are here the new range does not interfere with any range in ranges and
		// i is the index of the first range right to it (or ranges.length, when the new range
		// becomes the right most range). 
		ranges.splice(i, 0, {start: startIndex, end: endIndex});
	// Returns the total size of all Ranges in ranges
	var getTotalRangesSize = function(ranges) {
		var totalRangeSize = 0;
		for (var i=0; i < ranges.length; i++) {
			var range = ranges[i];
			totalRangeSize += range.end-range.start;
		return totalRangeSize;
	var isWordChar = function(c) {
		return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c == "_";
	// Returns the bounds of the word in s around offset as a {start: , end:} object.
	// Returns null when the char at offset is not a word char.
	var getWordBounds = function(s, offset) {
		// Handle the "offset is not in word" case
		if (!isWordChar(s[offset])) return null;
		for (var i = offset-1; i >= 0 && isWordChar(s[i]); i--) 
		var startIndex = i+1;
		var n = s.length;
		for (i = offset+1; i < n && isWordChar(s[i]); i++) 
		return {start: startIndex, end: i};
	var moveToWordBorder = function(s, offset, isStartOffset) {
		var wordBounds;
		if (isStartOffset) {
			wordBounds = getWordBounds(s, offset);
		} else {
			if (offset <= 0) return offset;
			wordBounds = getWordBounds(s, offset-1);
		if (!wordBounds) return offset;
		if (isStartOffset) {
			if (wordBounds.start >= offset-maxMovementForWordCorrection) return wordBounds.start;
			if (wordBounds.end <= offset+maxMovementForWordCorrection) return wordBounds.end;
		} else {
			if (wordBounds.end <= offset+maxMovementForWordCorrection) return wordBounds.end;
			if (wordBounds.start >= offset-maxMovementForWordCorrection) return wordBounds.start;
		return offset;
	// Splits s into a sequence of "matched" and "unmatched" substrings, using the 
	// matchRegExp to do the matching.
	// Returns an array of objects with a "text" property containing the substring text. 
	// Substrings that are "matches" also contain a boolean "isMatch" property set to true.
	// @param matchRegExp [may be null] when null no matching is performed and the returned 
	// 			array just contains one item with s as its text
	var getTextAndMatchArray = function(s, matchRegExp) {
		var result = [];
		if (matchRegExp) {
			var startIndex = 0;
			var n = s.length;
			var currentLen = 0;
			do {
				matchRegExp.lastIndex = startIndex;
				var match = matchRegExp.exec(s);
				if (match) {
					if (startIndex < match.index) {
						var t = s.substring(startIndex, match.index);
					result.push({text:match[0], isMatch:true});
					startIndex = match.index + match[0].length;
				} else {
					result.push({text: s.substr(startIndex)});
			} while (true);
		} else {
			result.push({text: s});
		return result;
	var getMatchedTextCount = function(textAndMatches) {
		var result = 0;
		for (var i=0; i < textAndMatches.length; i++) {
			if (textAndMatches[i].isMatch) {
		return result;	
	var getContextRangeAround = function(s, startIndex, endIndex, matchCount, maxLen) {
		// Partition the available space into equal sized areas for each match and one 
		// for the text start.
		// But the size should not go below a certain limit
		var size = Math.max(Math.floor(maxLen/(matchCount+1)), minMatchWithContextSize);
		// Substract the size of the range to get the size of the context.
		var contextSize = Math.max(size-(endIndex-startIndex), 0);
		// Two thirds of the context should be before the match, one third after.
		var contextEnd = Math.min(Math.floor(endIndex+contextSize/3), s.length);
		var contextStart = Math.max(contextEnd - size, 0);
		// If the contextStart/End is inside a word and the end of the word is
		// close move the pointers accordingly to make the text more readable.
		contextStart = moveToWordBorder(s, contextStart, true);
		contextEnd = moveToWordBorder(s, contextEnd, false);
		return {start: contextStart, end: contextEnd};
	// Get all ranges around matched substrings with their contexts
	var getMatchedTextWithContextRanges = function(textAndMatches, s, maxLen) {
		var ranges = [];
		var matchCount = getMatchedTextCount(textAndMatches);
		var pos = 0;
		for (var i=0; i < textAndMatches.length; i++) {
			var t = textAndMatches[i];
			var text = t.text;
			if (t.isMatch) {
				var range = getContextRangeAround(s, pos, pos+text.length, matchCount, maxLen);
				addRange(ranges, range.start, range.end);
			pos += text.length;
		return ranges;
	var fillUpRanges = function(s, ranges, maxLen) {
		var remainingLen = maxLen - getTotalRangesSize(ranges);
		while (remainingLen > 0) {
			if (ranges.length == 0) {
				// No matches added yet. Make one large range.
				addRange(ranges, 0, moveToWordBorder(s, maxLen, false));
			} else {
				var range = ranges[0];
				var startIndex;
				var maxEndIndex;
				if (range.start == 0) {
					// The first range already starts at the beginning of the string.
					// When there is a second range fill to the next range start or to the maxLen.
					startIndex = range.end;
					if (ranges.length > 1) {
						maxEndIndex =  ranges[1].start;
					} else {
						// Only one range. Add a range after that with the complete remaining len 
						// (corrected to "beautify" the output)
						addRange(ranges, startIndex, moveToWordBorder(s, startIndex+remainingLen, false));
				} else {
					// There is unused space between the start of the text and the first range.
					startIndex = 0;
					maxEndIndex = range.start;
				var endIndex = Math.min(maxEndIndex, startIndex+remainingLen);
				addRange(ranges, startIndex, endIndex);
				remainingLen -= (endIndex-startIndex);
	// Write the given ranges of s, using textAndMatches for marking portions of the text.
	var writeRanges = function(place, s, textAndMatches, ranges, maxLen) {
		if (ranges.length == 0) return;
		// Processes the text between startIndex and endIndex of the textAndMatches
		// "writes" them (as DOM elements) at the given place, possibly as "marked" text.
		// When endIndex is not the end of the full text an ellisis is appended. 
		var writeTextAndMatchRange = function(place, s, textAndMatches, startIndex, endIndex) {
			var t;
			var text;
			// find the first text item to write
			var pos = 0;
			var i = 0;
			var offset = 0;
			for (;i < textAndMatches.length; i++) {
				t = textAndMatches[i];
				text = t.text;
				if (startIndex < pos+text.length) {
					offset = startIndex - pos;
				pos += text.length;
			var remainingLen = endIndex - startIndex;
			for (; i < textAndMatches.length && remainingLen > 0; i++) {
				t = textAndMatches[i];
				text = t.text.substr(offset);
				offset = 0;
				if (text.length > remainingLen) text = text.substr(0,remainingLen);
				if (t.isMatch) {
				} else {
					createTiddlyText(place, text);
				remainingLen -= text.length;
			if (endIndex < s.length) {
		// When the first range is not at the start of the text write an ellipsis("...")
		// (Ellipses between ranges are written in the writeTextAndMatchRange method)
		if (ranges[0].start > 0) abego.createEllipsis(place);
		var remainingLen = maxLen;
		for (var i = 0; i < ranges.length && remainingLen > 0; i++) {
			var range = ranges[i];
			var len = Math.min(range.end - range.start, remainingLen);
			writeTextAndMatchRange(place, s, textAndMatches, range.start, range.start+len);
			remainingLen -= len;
	this.render = function(place,s,maxLen,markRegExp) {
		if (s.length < maxLen) maxLen = s.length;
		var textAndMatches = getTextAndMatchArray(s, markRegExp);
		var ranges = getMatchedTextWithContextRanges(textAndMatches, s, maxLen);
		// When the maxLen is not yet reached add more ranges 
		// starting from the beginning until either maxLen or 
		// the end of the string is reached.
		fillUpRanges(s, ranges, maxLen);
		writeRanges(place, s, textAndMatches, ranges, maxLen);

(function() {

function alertAndThrow(msg) {
	throw msg;

if (version.major < 2 || (version.major == 2 && version.minor < 1)) 
	alertAndThrow("YourSearchPlugin requires TiddlyWiki 2.1 or newer.\n\nCheck the archive for YourSearch plugins\nsupporting older versions of TiddlyWiki.\n\nArchive: http://tiddlywiki.abego-software.de/archive");

abego.YourSearch = {};

// The Search Core

// Model Variables
var lastResults; // Array of tiddlers that matched the last search
var lastQuery; // The last Search query (TiddlerQuery)

var setLastResults = function(array) {
	lastResults = array;

var getLastResults = function() {
	return lastResults ? lastResults : [];

var getLastResultsCount = function() {
	return lastResults ? lastResults.length : 0;

// Standard Ranking Weights
var matchInTitleWeight = 4;
var precisionInTitleWeight = 10;
var matchInTagsWeight = 2;

var getMatchCount = function(s, re) {
	var m = s.match(re);
	return m ? m.length : 0;

var standardRankFunction = function(tiddler, query) {	
	// Count the matches in the title and the tags
	var markRE = query.getMarkRegExp();
	if (!markRE) return 1;
	var matchesInTitle = tiddler.title.match(markRE);
	var nMatchesInTitle =  matchesInTitle ? matchesInTitle.length : 0;
	var nMatchesInTags = getMatchCount(tiddler.getTags(), markRE);

	// Calculate the "precision" of the matches in the title as the ratio of
	// the length of the matches to the total length of the title.
	var lengthOfMatchesInTitle = matchesInTitle ? matchesInTitle.join("").length : 0;
	var precisionInTitle = tiddler.title.length > 0 ? lengthOfMatchesInTitle/tiddler.title.length : 0;
	// calculate a weighted score
	var rank= nMatchesInTitle * matchInTitleWeight 
			+ nMatchesInTags * matchInTagsWeight 
			+ precisionInTitle * precisionInTitleWeight 
			+ 1;

	return rank;

// @return Tiddler[]
var findMatches = function(store, searchText,caseSensitive,useRegExp,sortField,excludeTag) {
	lastQuery = null;
	var candidates = store.reverseLookup("tags",excludeTag,false);
	try {
		var defaultFields = [];
		if (config.options.chkSearchInTitle) defaultFields.push("title");
		if (config.options.chkSearchInText) defaultFields.push("text");
		if (config.options.chkSearchInTags) defaultFields.push("tags");
		lastQuery = new abego.TiddlerQuery(
				searchText,caseSensitive, useRegExp,defaultFields,config.options.chkSearchExtendedFields); 
	} catch (e) {
		// when an invalid query is given no tiddlers are matched
		return [];

	var results = lastQuery.filter(candidates);

	// Rank the results
	var rankFunction = abego.YourSearch.getRankFunction();
	for (var i = 0; i < results.length; i++) {
		var tiddler = results[i];
		var rank = rankFunction(tiddler, lastQuery);
		// Add the rank information to the tiddler.
		// This is used during the sorting, but it may also
		// be used in the result, e.g. to display some "relevance" 
		// information in the result	
		tiddler.searchRank = rank;	
	// sort the result, taking care of the rank and the sortField	
	if(!sortField) {
		sortField = "title";
	var sortFunction = function (a,b) {
		var searchRankDiff = a.searchRank - b.searchRank;
		if (searchRankDiff == 0) {
			if (a[sortField] == b[sortField]) {
			} else {
				return (a[sortField] < b[sortField]) ? -1 : +1; 
		} else {
			return (searchRankDiff > 0) ? -1 : +1; 
	return results;

// The Search UI (Result page)

// Visual appearance of the result page
var maxCharsInTitle = 80;
var maxCharsInTags = 50;
var maxCharsInText = 250;
var maxCharsInField = 50;

var itemsPerPageDefault = 25; // Default maximum number of items on one search result page
var itemsPerPageWithPreviewDefault = 10; // Default maximum number of items on one search result page when PreviewText is on

// DOM IDs
var yourSearchResultID = "yourSearchResult";
var yourSearchResultItemsID = "yourSearchResultItems";

var lastSearchText; // The last search text, as passed to findMatches

var resultElement; // The (popup) DOM element containing the search result [may be null]
var searchInputField; // The "search" input field
var searchButton; // The "search" button
var lastNewTiddlerButton;

var initStylesheet = function() {
	if (version.extensions.YourSearchPlugin.styleSheetInited) 
	version.extensions.YourSearchPlugin.styleSheetInited = true;

var isResultOpen = function() {
	return resultElement != null && resultElement.parentNode == document.body;

var closeResult = function() {
	if (isResultOpen()) {

// Closes the Search Result window and displays the tiddler 
// defined by the "tiddlyLink" attribute of this element
var closeResultAndDisplayTiddler = function(e)
	var title = this.getAttribute("tiddlyLink");
	if(title) {
		var withHilite = this.getAttribute("withHilite");
		var oldHighlightHack = highlightHack;
		if (withHilite && withHilite=="true" && lastQuery) {
			highlightHack = lastQuery.getMarkRegExp();
		highlightHack = oldHighlightHack;

// Adjusts the resultElement's size and position, relative to the search input field.
var adjustResultPositionAndSize = function() {
	if (!searchInputField) return;
	var root = searchInputField;
	// Position the result below the root and resize it if necessary.
	var rootLeft = findPosX(root);
	var rootTop = findPosY(root);
	var rootHeight = root.offsetHeight;
	var popupLeft = rootLeft;
	var popupTop = rootTop + rootHeight;

	// Make sure the result is not wider than the window
	var winWidth = findWindowWidth();
	if (winWidth < resultElement.offsetWidth) {
		resultElement.style.width = (winWidth - 100)+"px";
		winWidth = findWindowWidth();

	// Ensure that the left and right of the result are not
	// clipped by the window. Move it to the left or right, if necessary.	
	var popupWidth = resultElement.offsetWidth;
	if(popupLeft + popupWidth > winWidth)
		popupLeft = winWidth - popupWidth-30;
	if (popupLeft < 0) popupLeft = 0;
	// Do the actual moving
	resultElement.style.left = popupLeft + "px";
	resultElement.style.top = popupTop + "px";
	resultElement.style.display = "block";

var scrollVisible = function() {
	// Scroll the window to make the result page (and the search Input field) visible.
	if (resultElement) window.scrollTo(0,ensureVisible(resultElement));
	if (searchInputField) window.scrollTo(0,ensureVisible(searchInputField));

// Makes sure the result page has a good size and position and visible
// (may scroll the window)
var	ensureResultIsDisplayedNicely = function() {

var indexInPage; // The index (in the current page) of the tiddler currently rendered.
var currentTiddler; // While rendering the page the tiddler that is currently rendered.

var pager = new abego.PageWiseRenderer();

var MyItemRenderer = function(parent) {
	// Load the template how to display the items that represent a found tiddler
	this.itemHtml = store.getTiddlerText("YourSearchItemTemplate");
	if (!this.itemHtml) alertAndThrow("YourSearchItemTemplate not found");
	// Locate the node that shall contain the list of found tiddlers
	this.place = document.getElementById(yourSearchResultItemsID);
		this.place = createTiddlyElement(parent,"div",yourSearchResultItemsID);

	render: function(pager,object,index,indexOnPage) {
		// Define global variables, referenced by macros during applyHtmlMacros
		indexInPage = indexOnPage;
		currentTiddler = object;
		var item = createTiddlyElement(this.place,"div",null, "yourSearchItem");
		item.innerHTML = this.itemHtml;

	endRendering: function(pager) {
		// The currentTiddler must only be defined while rendering the found tiddlers
		currentTiddler = null;

// Refreshes the content of the result with the current search result
// of the selected page.
// Assumes that the result is already open. 
var refreshResult = function() {
	if (!resultElement || !searchInputField) return;

	// Load the template for the YourSearchResult
	var html = store.getTiddlerText("YourSearchResultTemplate");
	if (!html) html = "<b>Tiddler YourSearchResultTemplate not found</b>";
	resultElement.innerHTML = html;

	// Expand the template macros etc.
	var itemRenderer = new MyItemRenderer(resultElement);


pager.getItemsPerPage = function() {
	var n = (config.options.chkPreviewText) 
			? abego.toInt(config.options.txtItemsPerPageWithPreview, itemsPerPageWithPreviewDefault) 
			: abego.toInt(config.options.txtItemsPerPage, itemsPerPageDefault);
	return (n > 0) ? n : 1;

pager.onPageChanged = function() {

var	reopenResultIfApplicable = function() {
	if (searchInputField == null || !config.options.chkUseYourSearch) return;
	if ((searchInputField.value == lastSearchText) && lastSearchText && !isResultOpen()) {
		// For speedup we check re-use the previously created resultElement, if possible.
		if (resultElement && (resultElement.parentNode != document.body)) {
		} else {

var invalidateResult = function() {
	resultElement = null;
	lastSearchText = null;

// Close the search result page when the user clicks on the document
// (and not into the searchInputField, on the search button or in the result)
// or presses the ESC key

// Returns true if e is either self or a descendant (child, grandchild,...) of self.
// @param self DOM:Element
// @param e DOM:Element or null
var isDescendantOrSelf = function(self, e) {
	while (e != null) {
		if (self == e) return true;
		e = e.parentNode;
	return false;

var onDocumentClick = function(e) {
	if (e.target == searchInputField) return; 
	if (e.target == searchButton) return; 
	if (resultElement && isDescendantOrSelf(resultElement, e.target)) return; 

var onDocumentKeyup = function(e) {
	// Close the search result page when the user presses "ESC"
	if (e.keyCode == 27) closeResult();

// Our Search Macro Hijack Function ==========================================

// Helper
var myStorySearch = function(text,useCaseSensitive,useRegExp)
	lastSearchText = text;
	setLastResults(findMatches(store, text,useCaseSensitive,useRegExp,"title","excludeSearch"));


var myMacroSearchHandler = function(place,macroName,params,wikifier,paramString,tiddler)

	lastSearchText = "";
	var searchTimeout = null;
	var doSearch = function(txt)
		if (config.options.chkUseYourSearch)
		lastSearchText = txt.value;
	var clickHandler = function(e)
		return false;
	var keyHandler = function(e)
		if (!e) var e = window.event;
		searchInputField = this;
			case 13:
				if (e.ctrlKey && lastNewTiddlerButton && isResultOpen())
			case 27:
				// When the result is open, close it, 
				// otherwise clear the content of the input field
				if (isResultOpen()) {
				} else {
					this.value = "";
		if (String.fromCharCode(e.keyCode) == this.accessKey || e.altKey) 

		if(this.value.length<3 && searchTimeout) clearTimeout(searchTimeout);
		if(this.value.length > 2)
		 	if (this.value != lastSearchText)
				if (!config.options.chkUseYourSearch || config.options.chkSearchAsYouType)
					var txt = this;
					searchTimeout = setTimeout(function() {doSearch(txt);},500);
		if (this.value.length == 0) 

	var focusHandler = function(e)

	var args = paramString.parseParams("list",null,true);
	var buttonAtRight = getFlag(args, "buttonAtRight");
	var sizeTextbox = getParam(args, "sizeTextbox", this.sizeTextbox);
	var btn;
	if (!buttonAtRight)
		btn = createTiddlyButton(place,this.label,this.prompt,clickHandler);
	var txt = createTiddlyElement(null,"input",null,"txtOptionInput searchField",null);
		txt.value = params[0];
	txt.onkeyup = keyHandler;
	txt.onfocus = focusHandler;


	if (buttonAtRight)
		btn = createTiddlyButton(place,this.label,this.prompt,clickHandler);

	searchInputField = txt;
	searchButton = btn;

// Support for Macros

var openAllFoundTiddlers = function() {
	var results = getLastResults();
	var n = results.length;
	if (n) {
		var titles=[];
		for(var i = 0; i<n; i++)

var createOptionWithRefresh = function(place, optionParams, wikifier,tiddler) {
	// The option macro appended the component at the end of the place.
	var elem = place.lastChild;
	var oldOnClick = elem.onclick;
	elem.onclick = function(e) {
		var result = oldOnClick.apply(this, arguments);
		return result;
	return elem;

var removeTextDecoration = function(s) {
	var removeThis = ["''", "{{{", "}}}", "//", "<<<", "/***", "***/"];
	var reText = "";
	for (var i = 0; i < removeThis.length; i++) {
		if (i != 0) reText += "|";
		reText += "("+removeThis[i].escapeRegExp()+")";
	return s.replace(new RegExp(reText, "mg"), "").trim();

// Returns the "shortcut number" of the currentTiddler. 
// I.e. When the user presses Alt-n the given tiddler is opened/display.
// @return 0-9 or -1 when no number is defined
var getShortCutNumber = function() {
	var i = indexInPage;
	return (i >= 0 && i <= 9) 
		? (i < 9 ? (i+1) : 0)
		: -1;

var limitedTextRenderer = new abego.LimitedTextRenderer();
var renderLimitedText = function(place, s, maxLen) {

// When any tiddler are changed reset the result.
var oldTiddlyWikiSaveTiddler = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields) {
	oldTiddlyWikiSaveTiddler.apply(this, arguments);
var oldTiddlyWikiRemoveTiddler = TiddlyWiki.prototype.removeTiddler;
TiddlyWiki.prototype.removeTiddler = function(title) {
	oldTiddlyWikiRemoveTiddler.apply(this, arguments);

// Macros

// ====Macro yourSearch ================================================

config.macros.yourSearch = {
	// Standard Properties
	label: "yourSearch",
	prompt: "Gives access to the current/last YourSearch result",
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		if (params.length == 0) return;
		var name = params[0];
		var func = config.macros.yourSearch.funcs[name];
		if (func) func(place,macroName,params,wikifier,paramString,tiddler);
	tests: {
		"true" : function() {return true;},
		"false" : function() {return false;},
		"found" : function() {return getLastResultsCount() > 0;},
		"previewText" : function() {return config.options.chkPreviewText;}

	funcs: {
		itemRange: function(place) {
			if (getLastResultsCount()) {
				var lastIndex = pager.getLastIndexOnPage();
				var s = "%0 - %1".format([pager.getFirstIndexOnPage()+1,lastIndex+1]);
				createTiddlyText(place, s);
		count: function(place) {
			createTiddlyText(place, getLastResultsCount().toString());
		query: function(place) {
			if (lastQuery) {
				createTiddlyText(place, lastQuery.toString());
		version: function(place) {
			var t = "YourSearch %0.%1.%2".format(
			var e = createTiddlyElement(place, "a");
			e.setAttribute("href", "http://tiddlywiki.abego-software.de/#YourSearchPlugin");
			e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">'+t+'<font>';
		copyright: function(place) {
			var e = createTiddlyElement(place, "a");
			e.setAttribute("href", "http://www.abego-software.de");
			e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">&copy; 2005-2008 <b><font color="red">abego</font></b> Software<font>';
		newTiddlerButton: function(place) {
			if (lastQuery) {
				var r = abego.parseNewTiddlerCommandLine(lastQuery.getQueryText());
				var btn = config.macros.newTiddler.createNewTiddlerButton(place,r.title,r.params,"new tiddler","Create a new tiddler based on search text. (Shortcut: Ctrl-Enter; Separators: '.', '#')",null,"text");				
				// Close the result before the new tiddler is created.
				var oldOnClick = btn.onclick;
				btn.onclick = function() {
				lastNewTiddlerButton = btn;
		linkButton: function(place,macroName,params,wikifier,paramString,tiddler) {
			if (params < 2) return;
			var	tiddlyLink = params[1];
			var text = params < 3 ? tiddlyLink : params[2];
			var tooltip = params < 4 ? text : params[3];
			var accessKey = params < 5 ? null : params[4];
			var btn = createTiddlyButton(place,text,tooltip,closeResultAndDisplayTiddler,null,null, accessKey);
		closeButton: function(place,macroName,params,wikifier,paramString,tiddler) {
			var button = createTiddlyButton(place, "close", "Close the Search Results (Shortcut: ESC)", closeResult);
		openAllButton: function(place,macroName,params,wikifier,paramString,tiddler) {
			var n = getLastResultsCount();
			if (n == 0) return;
			var title = n == 1 ? "open tiddler" : "open all %0 tiddlers".format([n]);
			var button = createTiddlyButton(place, title, "Open all found tiddlers (Shortcut: Alt-O)", openAllFoundTiddlers);
		naviBar: function(place,macroName,params,wikifier,paramString,tiddler) {
		"if": function(place,macroName,params,wikifier,paramString,tiddler) {
			if (params.length < 2) return;
			var testName = params[1];
			var negate = (testName == "not");
			if (negate) {
				if (params.length < 3) return;
				testName = params[2];
			var test = config.macros.yourSearch.tests[testName];
			var showIt = false;
			try {
				if (test) {
					showIt = test(place,macroName,params,wikifier,paramString,tiddler) != negate;
				} else {
					// When no predefined test is specified try to evaluate it as a JavaScript expression.
					showIt = (!eval(testName)) == negate;
			} catch (ex) {
			if (!showIt) {
		chkPreviewText: function(place,macroName,params,wikifier,paramString,tiddler) {
			var optionParams = params.slice(1).join(" ");
			var elem = createOptionWithRefresh(place, "chkPreviewText", wikifier,tiddler);
			elem.setAttribute("accessKey", "P");
			elem.title = "Show text preview of found tiddlers (Shortcut: Alt-P)";	
			return elem;

// ====Macro foundTiddler ================================================

config.macros.foundTiddler = {
	// Standard Properties
	label: "foundTiddler",
	prompt: "Provides information on the tiddler currently processed on the YourSearch result page",
	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
		var name = params[0];
		var func = config.macros.foundTiddler.funcs[name];
		if (func) func(place,macroName,params,wikifier,paramString,tiddler);
	funcs: {
		title: function(place,macroName,params,wikifier,paramString,tiddler) {
			if (!currentTiddler) return;
			var shortcutNumber = getShortCutNumber();
			var tooltip = shortcutNumber >= 0 
					? "Open tiddler (Shortcut: Alt-%0)".format([shortcutNumber.toString()])
					: "Open tiddler";
			var btn = createTiddlyButton(place,null,tooltip,closeResultAndDisplayTiddler,null);
			renderLimitedText(btn, currentTiddler.title, maxCharsInTitle);
			if (shortcutNumber >= 0) {
		tags: function(place,macroName,params,wikifier,paramString,tiddler) {
			if (!currentTiddler) return;
			renderLimitedText(place, currentTiddler.getTags(), maxCharsInTags);
		text: function(place,macroName,params,wikifier,paramString,tiddler) {
			if (!currentTiddler) return;
			renderLimitedText(place, removeTextDecoration(currentTiddler.text), maxCharsInText);
		field:  function(place,macroName,params,wikifier,paramString,tiddler) {
			if (!currentTiddler) return;
			var	name = params[1];
			var len = params.length > 2 ? abego.toInt(params[2],maxCharsInField) : maxCharsInField;
			var v = store.getValue(currentTiddler,name);
			if (v)
				renderLimitedText(place, removeTextDecoration(v), len);
		// Renders the "shortcut number" of the current tiddler, to indicate to the user
		// what number to "Alt-press" to open the tiddler.
		number: function(place,macroName,params,wikifier,paramString,tiddler) {
			var numberToDisplay = getShortCutNumber();
			if (numberToDisplay >= 0) {
				var text = "%0)".format([numberToDisplay.toString()]);

// Configuration Stuff

var opts = {chkUseYourSearch:true,
for (var n in opts) 
	if (config.options[n] == undefined) config.options[n] = opts[n];

// Shadow Tiddlers

config.shadowTiddlers.AdvancedOptions += "\n<<option chkUseYourSearch>> Use 'Your Search' //([[more options|YourSearch Options]]) ([[help|YourSearch Help]])// ";

config.shadowTiddlers["YourSearch Help"] =
"!Field Search\nWith the Field Search you can restrict your search to certain fields of a tiddler, e.g"+
" only search the tags or only the titles. The general form is //fieldname//'':''//textToSearch// (e."+
"g. {{{title:intro}}}). In addition one-character shortcuts are also supported for the standard field"+
"s {{{title}}}, {{{text}}} and {{{tags}}}:\n|!What you want|!What you type|!Example|\n|Search ''titles "+
"only''|start word with ''!''|{{{!jonny}}} (shortcut for {{{title:jonny}}})|\n|Search ''contents/text "+
"only''|start word with ''%''|{{{%football}}} (shortcut for {{{text:football}}})|\n|Search ''tags only"+
"''|start word with ''#''|{{{#Plugin}}} (shortcut for {{{tags:Plugin}}})|\n\nUsing this feature you may"+
" also search the extended fields (\"Metadata\") introduced with TiddlyWiki 2.1, e.g. use {{{priority:1"+
"}}} to find all tiddlers with the priority field set to \"1\".\n\nYou may search a word in more than one"+
" field. E.g. {{{!#Plugin}}} (or {{{title:tags:Plugin}}} in the \"long form\") finds tiddlers containin"+
"g \"Plugin\" either in the title or in the tags (but does not look for \"Plugin\" in the text). \n\n!Boole"+
"an Search\nThe Boolean Search is useful when searching for multiple words.\n|!What you want|!What you "+
"type|!Example|\n|''All words'' must exist|List of words|{{{jonny jeremy}}} (or {{{jonny and jeremy}}}"+
")|\n|''At least one word'' must exist|Separate words by ''or''|{{{jonny or jeremy}}}|\n|A word ''must "+
"not exist''|Start word with ''-''|{{{-jonny}}} (or {{{not jonny}}})|\n\n''Note:'' When you specify two"+
" words, separated with a space, YourSearch finds all tiddlers that contain both words, but not neces"+
"sarily next to each other. If you want to find a sequence of word, e.g. '{{{John Brown}}}', you need"+
" to put the words into quotes. I.e. you type: {{{\"john brown\"}}}.\n\nUsing parenthesis you may change "+
"the default \"left to right\" evaluation of the boolean search. E.g. {{{not (jonny or jeremy)}}} finds"+
" all tiddlers that contain neither \"jonny\" nor \"jeremy. In contrast to this {{{not jonny or jeremy}}"+
"} (i.e. without parenthesis) finds all tiddlers that either don't contain \"jonny\" or that contain \"j"+
"eremy\".\n\n!'Exact Word' Search\nBy default a search result all matches that 'contain' the searched tex"+
"t. E.g. if you search for {{{Task}}} you will get all tiddlers containing 'Task', but also '~Complet"+
"edTask', '~TaskForce' etc.\n\nIf you only want to get the tiddlers that contain 'exactly the word' you"+
" need to prefix it with a '='. E.g. typing '=Task' will find the tiddlers that contain the word 'Tas"+
"k', ignoring words that just contain 'Task' as a substring.\n\n!~CaseSensitiveSearch and ~RegExpSearch"+
"\nThe standard search options ~CaseSensitiveSearch and ~RegExpSearch are fully supported by YourSearc"+
"h. However when ''~RegExpSearch'' is on Filtered and Boolean Search are disabled.\n\nIn addition you m"+
"ay do a \"regular expression\" search even with the ''~RegExpSearch'' set to false by directly enterin"+
"g the regular expression into the search field, framed with {{{/.../}}}. \n\nExample: {{{/m[ae][iy]er/"+
"}}} will find all tiddlers that contain either \"maier\", \"mayer\", \"meier\" or \"meyer\".\n\n!~JavaScript E"+
"xpression Filtering\nIf you are familiar with JavaScript programming and know some TiddlyWiki interna"+
"ls you may also use JavaScript expression for the search. Just enter a JavaScript boolean expression"+
" into the search field, framed with {{{ { ... } }}}. In the code refer to the variable tiddler and e"+
"valuate to {{{true}}} when the given tiddler should be included in the result. \n\nExample: {{{ { tidd"+
"ler.modified > new Date(\"Jul 4, 2005\")} }}} returns all tiddler modified after July 4th, 2005.\n\n!Com"+
"bined Search\nYou are free to combine the various search options. \n\n''Examples''\n|!What you type|!Res"+
"ult|\n|{{{!jonny !jeremy -%football}}}|all tiddlers with both {{{jonny}}} and {{{jeremy}}} in its tit"+
"les, but no {{{football}}} in content.|\n|{{{#=Task}}}|All tiddlers tagged with 'Task' (the exact wor"+
"d). Tags named '~CompletedTask', '~TaskForce' etc. are not considered.|\n\n!Access Keys\nYou are encour"+
"aged to use the access keys (also called \"shortcut\" keys) for the most frequently used operations. F"+
"or quick reference these shortcuts are also mentioned in the tooltip for the various buttons etc.\n\n|"+
"!Key|!Operation|\n|{{{Alt-F}}}|''The most important keystroke'': It moves the cursor to the search in"+
"put field so you can directly start typing your query. Pressing {{{Alt-F}}} will also display the pr"+
"evious search result. This way you can quickly display multiple tiddlers using \"Press {{{Alt-F}}}. S"+
"elect tiddler.\" sequences.|\n|{{{ESC}}}|Closes the [[YourSearch Result]]. When the [[YourSearch Resul"+
"t]] is already closed and the cursor is in the search input field the field's content is cleared so "+
"you start a new query.|\n|{{{Alt-1}}}, {{{Alt-2}}},... |Pressing these keys opens the first, second e"+
"tc. tiddler from the result list.|\n|{{{Alt-O}}}|Opens all found tiddlers.|\n|{{{Alt-P}}}|Toggles the "+
"'Preview Text' mode.|\n|{{{Alt-'<'}}}, {{{Alt-'>'}}}|Displays the previous or next page in the [[Your"+
"Search Result]].|\n|{{{Return}}}|When you have turned off the 'as you type' search mode pressing the "+
"{{{Return}}} key actually starts the search (as does pressing the 'search' button).|\n\n//If some of t"+
"hese shortcuts don't work for you check your browser if you have other extensions installed that alr"+
"eady \"use\" these shortcuts.//";

config.shadowTiddlers["YourSearch Options"] =
"|>|!YourSearch Options|\n|>|<<option chkUseYourSearch>> Use 'Your Search'|\n|!|<<option chkPreviewText"+
">> Show Text Preview|\n|!|<<option chkSearchAsYouType>> 'Search As You Type' Mode (No RETURN required"+
" to start search)|\n|!|Default Search Filter:<<option chkSearchInTitle>>Title ('!')     <<option chk"+
"SearchInText>>Text ('%')     <<option chkSearchInTags>>Tags ('#')    <<option chkSearchExtendedFiel"+
"ds>>Extended Fields<html><br><font size=\"-2\">The fields of a tiddlers that are searched when you don"+
"'t explicitly specify a filter in the search text <br>(Explictly specify fields using one or more '!"+
"', '%', '#' or 'fieldname:' prefix before the word/text to find).</font></html>|\n|!|Number of items "+
"on search result page: <<option txtItemsPerPage>>|\n|!|Number of items on search result page with pre"+
"view text: <<option txtItemsPerPageWithPreview>>|\n";
config.shadowTiddlers["YourSearchStyleSheet"] = 
"/***\n!~YourSearchResult Stylesheet\n***/\n/*{{{*/\n.yourSearchResult {\n\tposition: absolute;\n\twidth: 800"+
"px;\n\n\tpadding: 0.2em;\n\tlist-style: none;\n\tmargin: 0;\n\n\tbackground: #ffd;\n\tborder: 1px solid DarkGra"+
"y;\n}\n\n/*}}}*/\n/***\n!!Summary Section\n***/\n/*{{{*/\n.yourSearchResult .summary {\n\tborder-bottom-width:"+
" thin;\n\tborder-bottom-style: solid;\n\tborder-bottom-color: #999999;\n\tpadding-bottom: 4px;\n}\n\n.yourSea"+
"rchRange, .yourSearchCount, .yourSearchQuery   {\n\tfont-weight: bold;\n}\n\n.yourSearchResult .summary ."+
"button {\n\tfont-size: 10px;\n\n\tpadding-left: 0.3em;\n\tpadding-right: 0.3em;\n}\n\n.yourSearchResult .summa"+
"ry .chkBoxLabel {\n\tfont-size: 10px;\n\n\tpadding-right: 0.3em;\n}\n\n/*}}}*/\n/***\n!!Items Area\n***/\n/*{{{*"+
"/\n.yourSearchResult .marked {\n\tbackground: none;\n\tfont-weight: bold;\n}\n\n.yourSearchItem {\n\tmargin-to"+
"p: 2px;\n}\n\n.yourSearchNumber {\n\tcolor: #808080;\n}\n\n\n.yourSearchTags {\n\tcolor: #008000;\n}\n\n.yourSearc"+
"hText {\n\tcolor: #808080;\n\tmargin-bottom: 6px;\n}\n\n/*}}}*/\n/***\n!!Footer\n***/\n/*{{{*/\n.yourSearchFoote"+
"r {\n\tmargin-top: 8px;\n\tborder-top-width: thin;\n\tborder-top-style: solid;\n\tborder-top-color: #999999;"+
"\n}\n\n.yourSearchFooter a:hover{\n\tbackground: none;\n\tcolor: none;\n}\n/*}}}*/\n/***\n!!Navigation Bar\n***/"+
"\n/*{{{*/\n.yourSearchNaviBar a {\n\tfont-size: 16px;\n\tmargin-left: 4px;\n\tmargin-right: 4px;\n\tcolor: bla"+
"ck;\n\ttext-decoration: underline;\n}\n\n.yourSearchNaviBar a:hover {\n\tbackground-color: none;\n}\n\n.yourSe"+
"archNaviBar .prev {\n\tfont-weight: bold;\n\tcolor: blue;\n}\n\n.yourSearchNaviBar .currentPage {\n\tcolor: #"+
"FF0000;\n\tfont-weight: bold;\n\ttext-decoration: none;\n}\n\n.yourSearchNaviBar .next {\n\tfont-weight: bold"+
";\n\tcolor: blue;\n}\n/*}}}*/\n";

config.shadowTiddlers["YourSearchResultTemplate"] =
"<!--\n{{{\n-->\n<span macro=\"yourSearch if found\">\n<!-- The Summary Header ============================"+
"================ -->\n<table class=\"summary\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\">"+
"<tbody>\n  <tr>\n\t<td align=\"left\">\n\t\tYourSearch Result <span class=\"yourSearchRange\" macro=\"yourSearc"+
"h itemRange\"></span>\n\t\t&nbsp;of&nbsp;<span class=\"yourSearchCount\" macro=\"yourSearch count\"></span>\n"+
"\t\tfor&nbsp;<span class=\"yourSearchQuery\" macro=\"yourSearch query\"></span>\n\t</td>\n\t<td class=\"yourSea"+
"rchButtons\" align=\"right\">\n\t\t<span macro=\"yourSearch chkPreviewText\"></span><span class=\"chkBoxLabel"+
"\">preview text</span>\n\t\t<span macro=\"yourSearch newTiddlerButton\"></span>\n\t\t<span macro=\"yourSearch openAllButton\"></span>\n\t\t<span macro=\"yourSearch lin"+
"kButton 'YourSearch Options' options 'Configure YourSearch'\"></span>\n\t\t<span macro=\"yourSearch linkB"+
"utton 'YourSearch Help' help 'Get help how to use YourSearch'\"></span>\n\t\t<span macro=\"yourSearch clo"+
"seButton\"></span>\n\t</td>\n  </tr>\n</tbody></table>\n\n<!-- The List of Found Tiddlers ================="+
"=========================== -->\n<div id=\"yourSearchResultItems\" itemsPerPage=\"25\" itemsPerPageWithPr"+
"eview=\"10\"></div>\n\n<!-- The Footer (with the Navigation) ==========================================="+
"= -->\n<table class=\"yourSearchFooter\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody"+
">\n  <tr>\n\t<td align=\"left\">\n\t\tResult page: <span class=\"yourSearchNaviBar\" macro=\"yourSearch naviBar"+
"\"></span>\n\t</td>\n\t<td align=\"right\"><span macro=\"yourSearch version\"></span>, <span macro=\"yourSearc"+
"h copyright\"></span>\n\t</td>\n  </tr>\n</tbody></table>\n<!-- end of the 'tiddlers found' case ========="+
"================================== -->\n</span>\n\n\n<!-- The \"No tiddlers found\" case ================="+
"========================== -->\n<span macro=\"yourSearch if not found\">\n<table class=\"summary\" border="+
"\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody>\n  <tr>\n\t<td align=\"left\">\n\t\tYourSearch Resu"+
"lt: No tiddlers found for <span class=\"yourSearchQuery\" macro=\"yourSearch query\"></span>.\n\t</td>\n\t<t"+
"d class=\"yourSearchButtons\" align=\"right\">\n\t\t<span macro=\"yourSearch newTiddlerButton\"></span>\n\t\t<span macro=\"yourSearch linkButton 'YourSearch Options'"+
" options 'Configure YourSearch'\"></span>\n\t\t<span macro=\"yourSearch linkButton 'YourSearch Help' help"+
" 'Get help how to use YourSearch'\"></span>\n\t\t<span macro=\"yourSearch closeButton\"></span>\n\t</td>\n  <"+

config.shadowTiddlers["YourSearchItemTemplate"] = 
"<!--\n{{{\n-->\n<span class='yourSearchNumber' macro='foundTiddler number'></span>\n<span class='yourSea"+
"rchTitle' macro='foundTiddler title'/></span>&nbsp;-&nbsp;\n<span class='yourSearchTags' macro='found"+
"Tiddler field tags 50'/></span>\n<span macro=\"yourSearch if previewText\"><div class='yourSearchText' macro='fo"+
"undTiddler field text 250'/></div></span>\n<!--\n}}}\n-->";

config.shadowTiddlers["YourSearch"] = "<<tiddler [[YourSearch Help]]>>";

config.shadowTiddlers["YourSearch Result"] = "The popup-like window displaying the result of a YourSearch query.";

// Install YourSearch

// Overwrite the TiddlyWiki search handler and verify after a while 
// that nobody else has overwritten it.
config.macros.search.handler = myMacroSearchHandler;

var checkForOtherHijacker = function() {
	// Check that still our search handler is installed
    if (config.macros.search.handler != myMacroSearchHandler) {
"Message from YourSearchPlugin:\n\n\nAnother plugin has disabled the 'Your Search' features.\n\n\nYou may "+
"disable the other plugin or change the load order of \nthe plugins (by changing the names of the tidd"+
"lers)\nto enable the 'Your Search' features.");

setTimeout(checkForOtherHijacker, 5000);

// === Public API =================================

abego.YourSearch.getStandardRankFunction = function() {
	return standardRankFunction;

abego.YourSearch.getRankFunction = function() {
	return abego.YourSearch.getStandardRankFunction();

abego.YourSearch.getCurrentTiddler = function() {
	return currentTiddler;

abego.YourSearch.closeResult = function() {

// Returns an array of tiddlers that matched the last search
abego.YourSearch.getFoundTiddlers = function() {
	return lastResults;

// The last Search query (TiddlerQuery), or null
abego.YourSearch.getQuery = function() {
	return lastQuery;

abego.YourSearch.onShowResult = function(useOldResult) {
	highlightHack = lastQuery ? lastQuery.getMarkRegExp() : null;
	if (!useOldResult)
	if (!resultElement) {
		resultElement = createTiddlyElement(document.body,"div",yourSearchResultID,"yourSearchResult");
	} else if (resultElement.parentNode != document.body) {
	highlightHack = null;

} // of "install only once"
// Used Globals (for JSLint) ==============

// ... JavaScript Core
/*global 	alert,clearTimeout,confirm */
// ... TiddlyWiki Core
/*global 	Tiddler, applyHtmlMacros, clearMessage, createTiddlyElement, createTiddlyButton, createTiddlyText, ensureVisible ,findPosX, highlightHack, findPosY,findWindowWidth, invokeMacro, saveChanges, refreshElements, story */
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005-2010 ([[www.abego-software.de|http://www.abego-software.de]])

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.