summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxue <>2006-05-09 12:11:38 +0000
committerxue <>2006-05-09 12:11:38 +0000
commitf4de82bcdafba51e4eed9cae6b2d3e5375ffd115 (patch)
tree08f98e1763e87f0639961c6da33224082345c7c3
parent92dca3315f083f00dcff610ea207af52284d0616 (diff)
-rw-r--r--.gitattributes52
-rw-r--r--HISTORY2
-rw-r--r--buildscripts/texbuilder/build.php59
-rw-r--r--buildscripts/texbuilder/create_index.php90
-rw-r--r--buildscripts/texbuilder/prado3_quick_start.tex4
-rw-r--r--demos/quickstart/protected/application.xml3
-rw-r--r--demos/quickstart/protected/controls/Layout.tpl3
-rw-r--r--demos/quickstart/protected/controls/SearchBox.php41
-rw-r--r--demos/quickstart/protected/controls/SearchBox.tpl3
-rw-r--r--demos/quickstart/protected/controls/TopicList.tpl10
-rw-r--r--demos/quickstart/protected/index/Zend/Exception.php28
-rw-r--r--demos/quickstart/protected/index/Zend/LICENSE.txt52
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Exception.php34
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene.php569
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer.php94
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common.php73
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text.php76
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text/CaseInsensitive.php43
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Token.php170
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter.php45
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter/LowerCase.php55
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Document.php109
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Exception.php34
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Field.php134
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Index/FieldInfo.php43
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentInfo.php412
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentWriter.php491
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Index/Term.php70
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Index/TermInfo.php77
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Index/Writer.php308
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query.php98
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/MultiTerm.php437
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Phrase.php424
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Term.php126
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryHit.php106
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryParser.php140
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryToken.php102
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryTokenizer.php162
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity.php551
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity/Default.php99
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight.php59
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/MultiTerm.php133
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Phrase.php138
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Term.php144
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory.php118
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory/Filesystem.php269
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File.php376
-rw-r--r--demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File/Filesystem.php170
-rw-r--r--demos/quickstart/protected/index/Zend/Search/TODO.txt14
-rw-r--r--demos/quickstart/protected/index/ZendSearch.php52
-rw-r--r--demos/quickstart/protected/index/data/_0.cfsbin0 -> 329302 bytes
-rw-r--r--demos/quickstart/protected/index/data/deletablebin0 -> 4 bytes
-rw-r--r--demos/quickstart/protected/index/data/segmentsbin0 -> 27 bytes
-rw-r--r--demos/quickstart/protected/pages/Advanced/Assets.page10
-rw-r--r--demos/quickstart/protected/pages/Advanced/Auth.page8
-rw-r--r--demos/quickstart/protected/pages/Advanced/Error.page10
-rw-r--r--demos/quickstart/protected/pages/Advanced/I18N.page28
-rw-r--r--demos/quickstart/protected/pages/Advanced/Logging.page8
-rw-r--r--demos/quickstart/protected/pages/Advanced/MasterContent.page2
-rw-r--r--demos/quickstart/protected/pages/Advanced/Performance.page12
-rw-r--r--demos/quickstart/protected/pages/Advanced/Scripts.page397
-rw-r--r--demos/quickstart/protected/pages/Advanced/Scripts1.page561
-rw-r--r--demos/quickstart/protected/pages/Advanced/Scripts2.page253
-rw-r--r--demos/quickstart/protected/pages/Advanced/Scripts3.page32
-rw-r--r--demos/quickstart/protected/pages/Advanced/Security.page8
-rw-r--r--demos/quickstart/protected/pages/Advanced/State.page10
-rw-r--r--demos/quickstart/protected/pages/Advanced/Themes.page12
-rw-r--r--demos/quickstart/protected/pages/Configurations/AppConfig.page2
-rw-r--r--demos/quickstart/protected/pages/Configurations/Overview.page2
-rw-r--r--demos/quickstart/protected/pages/Configurations/PageConfig.page2
-rw-r--r--demos/quickstart/protected/pages/Configurations/Templates1.page10
-rw-r--r--demos/quickstart/protected/pages/Configurations/Templates2.page16
-rw-r--r--demos/quickstart/protected/pages/Configurations/Templates3.page14
-rw-r--r--demos/quickstart/protected/pages/Controls/Button.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/CheckBox.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/ColorPicker.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/Data.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/DataGrid.page24
-rw-r--r--demos/quickstart/protected/pages/Controls/DataList.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/DatePicker.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/Expression.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/FileUpload.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/Head.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/HiddenField.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/HtmlArea.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/HyperLink.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/Image.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/ImageButton.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/ImageMap.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/InlineFrame.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/JavascriptLogger.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/Label.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/LinkButton.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/List.page12
-rw-r--r--demos/quickstart/protected/pages/Controls/Literal.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/MultiView.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/NewControl.page18
-rw-r--r--demos/quickstart/protected/pages/Controls/Panel.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/PlaceHolder.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/RadioButton.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/Repeater.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/SafeHtml.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/Statements.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/Table.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/TextBox.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/TextHighlighter.page2
-rw-r--r--demos/quickstart/protected/pages/Controls/Validation.page16
-rw-r--r--demos/quickstart/protected/pages/Controls/Wizard.page16
-rw-r--r--demos/quickstart/protected/pages/Fundamentals/Applications.page8
-rw-r--r--demos/quickstart/protected/pages/Fundamentals/Architecture.page2
-rw-r--r--demos/quickstart/protected/pages/Fundamentals/Components.page16
-rw-r--r--demos/quickstart/protected/pages/Fundamentals/Controls.page10
-rw-r--r--demos/quickstart/protected/pages/Fundamentals/Hangman.page2
-rw-r--r--demos/quickstart/protected/pages/Fundamentals/Modules.page12
-rw-r--r--demos/quickstart/protected/pages/Fundamentals/Pages.page6
-rw-r--r--demos/quickstart/protected/pages/Fundamentals/Services.page4
-rw-r--r--demos/quickstart/protected/pages/GettingStarted/AboutPrado.page10
-rw-r--r--demos/quickstart/protected/pages/GettingStarted/HelloWorld.page2
-rw-r--r--demos/quickstart/protected/pages/GettingStarted/Installation.page2
-rw-r--r--demos/quickstart/protected/pages/GettingStarted/Introduction.page2
-rw-r--r--demos/quickstart/protected/pages/GettingStarted/Upgrading.page14
-rw-r--r--demos/quickstart/protected/pages/Search.page28
-rw-r--r--demos/quickstart/protected/pages/Search.php53
-rw-r--r--demos/quickstart/protected/pages/ViewSource.page12
-rw-r--r--demos/quickstart/protected/pages/ViewSource.php90
-rw-r--r--demos/quickstart/themes/PradoSoft/style.css33
-rw-r--r--framework/3rdParty/readme.html9
-rw-r--r--framework/Exceptions/messages.txt2
-rw-r--r--framework/IO/TTextWriter.php2
-rw-r--r--framework/Web/UI/TTemplateManager.php23
-rw-r--r--framework/Web/UI/WebControls/TButton.php2
131 files changed, 8550 insertions, 239 deletions
diff --git a/.gitattributes b/.gitattributes
index 81846ece..b70928f5 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -19,6 +19,7 @@ buildscripts/phing/tasks/ZendCodeAnalyzerTask.php -text
buildscripts/phpbuilder/build.php -text
buildscripts/setup.php -text
buildscripts/texbuilder/build.php -text
+buildscripts/texbuilder/create_index.php -text
buildscripts/texbuilder/prado3_quick_start.tex -text
demos/composer/index.php -text
demos/composer/index2.php -text
@@ -67,8 +68,53 @@ demos/quickstart/protected/controls/RunBar.php -text
demos/quickstart/protected/controls/RunBar.tpl -text
demos/quickstart/protected/controls/SampleLayout.php -text
demos/quickstart/protected/controls/SampleLayout.tpl -text
+demos/quickstart/protected/controls/SearchBox.php -text
+demos/quickstart/protected/controls/SearchBox.tpl -text
demos/quickstart/protected/controls/TopicList.php -text
demos/quickstart/protected/controls/TopicList.tpl -text
+demos/quickstart/protected/index/Zend/Exception.php -text
+demos/quickstart/protected/index/Zend/LICENSE.txt -text
+demos/quickstart/protected/index/Zend/Search/Exception.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text/CaseInsensitive.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Token.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter/LowerCase.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Document.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Exception.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Field.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Index/FieldInfo.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentInfo.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentWriter.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Index/Term.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Index/TermInfo.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Index/Writer.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/MultiTerm.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Phrase.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Term.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryHit.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryParser.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryToken.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryTokenizer.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity/Default.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/MultiTerm.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Phrase.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Term.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory/Filesystem.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File.php -text
+demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File/Filesystem.php -text
+demos/quickstart/protected/index/Zend/Search/TODO.txt -text
+demos/quickstart/protected/index/ZendSearch.php -text
+demos/quickstart/protected/index/data/_0.cfs -text
+demos/quickstart/protected/index/data/deletable -text
+demos/quickstart/protected/index/data/segments -text
demos/quickstart/protected/pages/Advanced/Assets.page -text
demos/quickstart/protected/pages/Advanced/Auth.page -text
demos/quickstart/protected/pages/Advanced/Error.page -text
@@ -99,6 +145,10 @@ demos/quickstart/protected/pages/Advanced/Samples/I18N/messages/tests.fr.xml -te
demos/quickstart/protected/pages/Advanced/Samples/I18N/messages/tests.pl.xml -text
demos/quickstart/protected/pages/Advanced/Samples/I18N/messages/tests.zh.xml -text
demos/quickstart/protected/pages/Advanced/Samples/I18N/zh_TW/Home.page -text
+demos/quickstart/protected/pages/Advanced/Scripts.page -text
+demos/quickstart/protected/pages/Advanced/Scripts1.page -text
+demos/quickstart/protected/pages/Advanced/Scripts2.page -text
+demos/quickstart/protected/pages/Advanced/Scripts3.page -text
demos/quickstart/protected/pages/Advanced/Security.page -text
demos/quickstart/protected/pages/Advanced/State.page -text
demos/quickstart/protected/pages/Advanced/Themes.page -text
@@ -294,6 +344,8 @@ demos/quickstart/protected/pages/GettingStarted/Upgrading.page -text
demos/quickstart/protected/pages/GettingStarted/directory.gif -text
demos/quickstart/protected/pages/GettingStarted/sequence.gif -text
demos/quickstart/protected/pages/GettingStarted/sequence.vsd -text
+demos/quickstart/protected/pages/Search.page -text
+demos/quickstart/protected/pages/Search.php -text
demos/quickstart/protected/pages/ViewSource.page -text
demos/quickstart/protected/pages/ViewSource.php -text
demos/quickstart/protected/pages/config.xml -text
diff --git a/HISTORY b/HISTORY
index d048d35b..1544f1a3 100644
--- a/HISTORY
+++ b/HISTORY
@@ -11,6 +11,8 @@ Version 3.0.1 June 1, 2006
ENH: Ticket#150 - TDataGrid and TDataList now render table section tags (Qiang)
ENH: Ticket#152 - constituent parts of TWizard are exposed (Qiang)
ENH: added sanity check to calling event handlers (Qiang)
+ENH: Added search for quickstart tutorials (Wei)
+ENH: added support to property tags for template owner control (Qiang)
CHG: Ticket#151 - URL format is modified to handle empty GET values (Qiang)
CHG: Ticket#153 - TAssetManager now ignores .svn directories (Qiang)
NEW: TTableHeaderRow, TTableFooterRow and table section support (Qiang)
diff --git a/buildscripts/texbuilder/build.php b/buildscripts/texbuilder/build.php
index 4e2d8be6..f0556e3f 100644
--- a/buildscripts/texbuilder/build.php
+++ b/buildscripts/texbuilder/build.php
@@ -10,6 +10,10 @@ $mainTexFile = dirname(__FILE__).'/prado3_quick_start.tex';
//page root location
$base = realpath(dirname(__FILE__).'/../../demos/quickstart/protected/pages/');
+//search index data directory
+$index_dir = realpath(dirname(__FILE__).'/../../demos/quickstart/protected/index/data/');
+
+
//list page into chapters
$pages['Getting Started'] = array(
'GettingStarted/Introduction.page',
@@ -94,6 +98,11 @@ $pages['Advanced Topics'] = array(
'Advanced/Error.page',
'Advanced/Performance.page');
+$pages['Client-side Scripting'] = array(
+ 'Advanced/Scripts.page',
+ 'Advanced/Scripts1.page',
+ 'Advanced/Scripts2.page',
+ 'Advanced/Scripts3.page');
//-------------- END CONFIG ------------------
@@ -273,12 +282,51 @@ function get_section_label($section)
return '\hypertarget{'.str_replace('/', '.', $section).'}{}';
}
+
+function set_header_id($content, $count)
+{
+ global $header_count;
+ $header_count = $count*100;
+ $content = preg_replace_callback('/<h1>/', "h1", $content);
+ $content = preg_replace_callback('/<h2>/', "h2", $content);
+ $content = preg_replace_callback('/<h3>/', "h3", $content);
+ return $content;
+}
+
+function h1($matches)
+{
+ global $header_count;
+ return "<h1 id=\"".(++$header_count)."\">";
+}
+
+function h2($matches)
+{
+ global $header_count;
+ return "<h2 id=\"".(++$header_count)."\">";
+}
+
+function h3($matches)
+{
+ global $header_count;
+ return "<h3 id=\"".(++$header_count)."\">";
+}
+
+$header_count = 0;
+
//--------------- BEGIN PROCESSING -------------------
+
+//--------------- Indexer -------------------
+
+require_once('create_index.php');
+$indexer = new quickstart_index($index_dir);
+
// ---------------- Create the Tex files ---------
$count = 1;
+$j = 1;
$current_path = '';
echo "Compiling .page files to Latex files\n\n";
+
foreach($pages as $chapter => $sections)
{
$content = '\chapter{'.$chapter.'}'.get_chapter_label($chapter);
@@ -289,8 +337,16 @@ foreach($pages as $chapter => $sections)
echo " Adding $section\n";
$page = $base.'/'.$section;
$current_path = $page;
+
+ //add id to <h1>, <h2>, <3>
+ $content = set_header_id(file_get_contents($page),$j++);
+ file_put_contents($page, $content);
+
$content .= get_section_label($section);
- $content .= parse_html($page,file_get_contents($page));
+ $file_content = file_get_contents($page);
+ $tex = parse_html($page,$file_content);
+ $content .= $tex;
+ $indexer->add($file_content,$section, filemtime($page));
}
//var_dump($content);
@@ -299,6 +355,7 @@ foreach($pages as $chapter => $sections)
echo "\n";
}
+$indexer->commit();
if($argc <= 1 && $count > 1)
{
diff --git a/buildscripts/texbuilder/create_index.php b/buildscripts/texbuilder/create_index.php
new file mode 100644
index 00000000..b451473d
--- /dev/null
+++ b/buildscripts/texbuilder/create_index.php
@@ -0,0 +1,90 @@
+<?php
+
+// Create quickstart search index
+$zend_path = realpath(dirname(__FILE__).'/../../demos/quickstart/protected/index');
+set_include_path(get_include_path().';'.$zend_path);
+require_once ('Zend/Search/Lucene.php');
+
+
+class quickstart_index
+{
+ private $_index;
+ private $_dir;
+
+ public function __construct($index_file)
+ {
+ $this->_index = new Zend_Search_Lucene($index_file, true);
+ $this->_dir = $index_file;
+ echo "Building search index...\n";
+ }
+
+ public function add($content, $section, $mtime)
+ {
+ foreach($this->split_headings($content) as $headers)
+ {
+ $doc = new Zend_Search_Lucene_Document();
+ $link = "index.php?page=".preg_replace('/\/|\\\/', '.', $section);
+ $link = str_replace('.page', '', $link).'#'.$headers['section'];
+
+ //unsearchable text
+ $doc->addField(Zend_Search_Lucene_Field::UnIndexed('link', $link));
+ $doc->addField(Zend_Search_Lucene_Field::UnIndexed('mtime', $mtime));
+ $doc->addField(Zend_Search_Lucene_Field::UnIndexed('title', $headers['title']));
+ $doc->addField(Zend_Search_Lucene_Field::UnIndexed('text', $headers['content']));
+
+ //searchable text
+ $doc->addField(Zend_Search_Lucene_Field::Keyword('page', strtolower($headers['title'])));
+ $body = strtolower($this->sanitize($headers['content'])).' '.strtolower($headers['title']);
+ $doc->addField(Zend_Search_Lucene_Field::Unstored('contents',$body));
+ $this->_index->addDocument($doc);
+ }
+ }
+
+ function sanitize($input)
+ {
+ return htmlentities(strip_tags( $input ));
+ }
+
+ public function index()
+ {
+ return $this->_index;
+ }
+
+ protected function split_headings($html)
+ {
+ $html = preg_replace('/<\/?com:TContent[^<]*>/', '', $html);
+
+ $html = preg_replace('/<b>([^<]*)<\/b>/', '$1', $html);
+ $html = preg_replace('/<i>([^<]*)<\/i>/', '$1', $html);
+ $html = preg_replace('/<tt>([^<]*)<\/tt>/', '$1', $html);
+
+ $html = preg_replace('/<h1([^>]*)>([^<]*)<\/h1>/', '<hh$1>$2</hh>', $html);
+ $html = preg_replace('/<h2([^>]*)>([^<]*)<\/h2>/', '<hh$1>$2</hh>', $html);
+ $html = preg_replace('/<h3([^>]*)>([^<]*)<\/h3>/', '<hh$1>$2</hh>', $html);
+
+
+ $sections = preg_split('/<hh[^>]*>([^<]+)<\/hh>/', $html,-1);
+ $headers = array();
+ preg_match_all('/<hh([^>]*)>([^<]+)<\/hh>/', $html, $headers);
+ $contents = array();
+ for($i = 1, $t = count($sections); $i < $t; $i++)
+ {
+ $content['title'] = trim($this->sanitize($headers[2][$i-1]));
+ $sec = array();
+ preg_match('/"([^"]*)"/', $headers[1][$i-1], $sec);
+ $content['section'] = str_replace('"', '',$sec[0]);
+ $content['content'] = trim($this->sanitize($sections[$i]));
+ $contents[] = $content;
+ }
+
+ return $contents;
+ }
+
+ public function commit()
+ {
+ $this->_index->commit();
+ $count = $this->_index->count();
+ echo "\nSaving search index ({$count}) to {$this->_dir}\n\n";
+ }
+}
+?> \ No newline at end of file
diff --git a/buildscripts/texbuilder/prado3_quick_start.tex b/buildscripts/texbuilder/prado3_quick_start.tex
index a4105685..d55e3462 100644
--- a/buildscripts/texbuilder/prado3_quick_start.tex
+++ b/buildscripts/texbuilder/prado3_quick_start.tex
@@ -114,5 +114,9 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\include{ch7}
\include{ch8}
\include{ch9}
+\include{ch10}
+\include{ch11}
+\include{ch12}
+\include{ch13}
\end{document}
diff --git a/demos/quickstart/protected/application.xml b/demos/quickstart/protected/application.xml
index cd6dd01e..abac1088 100644
--- a/demos/quickstart/protected/application.xml
+++ b/demos/quickstart/protected/application.xml
@@ -10,6 +10,9 @@
<route class="TFileLogRoute" />
</module>
-->
+ <module id="search"
+ class="Application.index.ZendSearch"
+ IndexDataDirectory="Application.index.data" />
</modules>
<paths>
<using namespace="Application.controls.*" />
diff --git a/demos/quickstart/protected/controls/Layout.tpl b/demos/quickstart/protected/controls/Layout.tpl
index a69d8f50..6fbd1380 100644
--- a/demos/quickstart/protected/controls/Layout.tpl
+++ b/demos/quickstart/protected/controls/Layout.tpl
@@ -14,6 +14,9 @@
</div>
<com:TPanel ID="MainMenu" CssClass="mainmenu">
+<div style="float:left; color:black; margin-top:-5px">
+ <com:SearchBox />
+</div>
<a href="?">Home</a> |
<a href="http://www.pradosoft.com">PradoSoft.com</a> |
<a href="prado3_quick_start.pdf">PDF Version</a> |
diff --git a/demos/quickstart/protected/controls/SearchBox.php b/demos/quickstart/protected/controls/SearchBox.php
new file mode 100644
index 00000000..b579cd91
--- /dev/null
+++ b/demos/quickstart/protected/controls/SearchBox.php
@@ -0,0 +1,41 @@
+<?php
+
+class SearchBox extends TTemplateControl
+{
+ public function getText()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('search')->getText();
+ }
+
+ public function getTextBox()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('search');
+ }
+
+ public function getButton()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('find');
+ }
+
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ if(strlen($q = $this->Page->Request['q']) > 0)
+ $this->search->setText($q);
+ }
+
+ public function doSearch($sender, $param)
+ {
+ if(strlen($query = $this->search->getText()) >0)
+ {
+ $ps = $this->getApplication()->getPageService();
+ $page = $ps->constructUrl('Search', array('q' => $query));
+ $this->getApplication()->getResponse()->redirect($page);
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/quickstart/protected/controls/SearchBox.tpl b/demos/quickstart/protected/controls/SearchBox.tpl
new file mode 100644
index 00000000..b3339d19
--- /dev/null
+++ b/demos/quickstart/protected/controls/SearchBox.tpl
@@ -0,0 +1,3 @@
+<com:TLabel ForControl="search" Text="Search:" CssClass="searchLabel"/>
+<com:TTextBox ID="search" OnTextChanged="doSearch" CssClass="searchBox"/>
+<com:TButton ID="find" Text="Find" OnClick="doSearch" CssClass="searchButton"/> \ No newline at end of file
diff --git a/demos/quickstart/protected/controls/TopicList.tpl b/demos/quickstart/protected/controls/TopicList.tpl
index d2cde711..5ffc7098 100644
--- a/demos/quickstart/protected/controls/TopicList.tpl
+++ b/demos/quickstart/protected/controls/TopicList.tpl
@@ -78,4 +78,14 @@
</ul>
</div>
+<div class="topic">
+<div>Client-side Scripting</div>
+<ul>
+ <li><a href="?page=Advanced.Scripts">Introduction to Javascript</a></li>
+ <li><a href="?page=Advanced.Scripts1">Prototype Library Part I</a></li>
+ <li><a href="?page=Advanced.Scripts2">Prototype Library Part II</a></li>
+ <li><a href="?page=Advanced.Scripts3">Javascript Cookbook</a></li>
+</ul>
+</div>
+
</div> \ No newline at end of file
diff --git a/demos/quickstart/protected/index/Zend/Exception.php b/demos/quickstart/protected/index/Zend/Exception.php
new file mode 100644
index 00000000..ab5e4e95
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Exception.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * @package Zend
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Exception extends Exception
+{}
+
diff --git a/demos/quickstart/protected/index/Zend/LICENSE.txt b/demos/quickstart/protected/index/Zend/LICENSE.txt
new file mode 100644
index 00000000..bfd3ff11
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/LICENSE.txt
@@ -0,0 +1,52 @@
+-----------------------------------------------------------------------
+ The Zend Framework License, Version 1.0
+ Copyright (c) 2005 Zend Technologies USA, Inc. All rights reserved.
+-----------------------------------------------------------------------
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The names "Zend" and "Zend Framework" must not be used to endorse
+ or promote products derived from this software without prior
+ permission from Zend Technologies USA, Inc. For written
+ permission, please contact license@zend.com.
+
+ 4. Zend Technologies USA, Inc. may publish revised and/or new
+ versions of the license from time to time. Each version will
+ be given a distinguishing version number.
+ Once covered code has been published under a particular version
+ of the license, you may always continue to use it under the
+ terms of that version. You may also choose to use such covered
+ code under the terms of any subsequent version of the license
+ published by Zend Technologies USA, Inc. No one other than Zend
+ Technologies USA, Inc. has the right to modify the terms
+ applicable to covered code created under this License.
+
+ 5. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes the Zend Framework, freely available at
+ http://www.zend.com"
+
+THIS SOFTWARE IS PROVIDED BY ZEND TECHNOLOGIES USA, INC. ``AS IS'' AND
+ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZEND
+TECHNOLOGIES USA, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+-----------------------------------------------------------------------
diff --git a/demos/quickstart/protected/index/Zend/Search/Exception.php b/demos/quickstart/protected/index/Zend/Search/Exception.php
new file mode 100644
index 00000000..e0aa2221
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Exception.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * Framework base exception
+ */
+require_once 'Zend/Exception.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Exception extends Zend_Exception
+{}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene.php b/demos/quickstart/protected/index/Zend/Search/Lucene.php
new file mode 100644
index 00000000..700a8b8a
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene.php
@@ -0,0 +1,569 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Exception */
+require_once 'Zend/Search/Lucene/Exception.php';
+
+/** Zend_Search_Lucene_Document */
+require_once 'Zend/Search/Lucene/Document.php';
+
+/** Zend_Search_Lucene_Storage_Directory */
+require_once 'Zend/Search/Lucene/Storage/Directory/Filesystem.php';
+
+/** Zend_Search_Lucene_Index_Term */
+require_once 'Zend/Search/Lucene/Index/Term.php';
+
+/** Zend_Search_Lucene_Index_TermInfo */
+require_once 'Zend/Search/Lucene/Index/TermInfo.php';
+
+/** Zend_Search_Lucene_Index_SegmentInfo */
+require_once 'Zend/Search/Lucene/Index/SegmentInfo.php';
+
+/** Zend_Search_Lucene_Index_FieldInfo */
+require_once 'Zend/Search/Lucene/Index/FieldInfo.php';
+
+/** Zend_Search_Lucene_Index_Writer */
+require_once 'Zend/Search/Lucene/Index/Writer.php';
+
+/** Zend_Search_Lucene_Search_QueryParser */
+require_once 'Zend/Search/Lucene/Search/QueryParser.php';
+
+/** Zend_Search_Lucene_Search_QueryHit */
+require_once 'Zend/Search/Lucene/Search/QueryHit.php';
+
+/** Zend_Search_Lucene_Search_Similarity */
+require_once 'Zend/Search/Lucene/Search/Similarity.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene
+{
+ /**
+ * File system adapter.
+ *
+ * @var Zend_Search_Lucene_Storage_Directory
+ */
+ private $_directory = null;
+
+ /**
+ * File system adapter closing option
+ *
+ * @var boolean
+ */
+ private $_closeDirOnExit = true;
+
+ /**
+ * Writer for this index, not instantiated unless required.
+ *
+ * @var Zend_Search_Lucene_Index_Writer
+ */
+ private $_writer = null;
+
+ /**
+ * Array of Zend_Search_Lucene_Index_SegmentInfo objects for this index.
+ *
+ * @var array Zend_Search_Lucene_Index_SegmentInfo
+ */
+ private $_segmentInfos = array();
+
+ /**
+ * Number of documents in this index.
+ *
+ * @var integer
+ */
+ private $_docCount = 0;
+
+
+ /**
+ * Opens the index.
+ *
+ * IndexReader constructor needs Directory as a parameter. It should be
+ * a string with a path to the index folder or a Directory object.
+ *
+ * @param mixed $directory
+ * @throws Zend_Search_Lucene_Exception
+ */
+ public function __construct($directory = null, $create = false)
+ {
+ if ($directory === null) {
+ throw new Zend_Search_Exception('No index directory specified');
+ }
+
+ if ($directory instanceof Zend_Search_Lucene_Storage_Directory_Filesystem) {
+ $this->_directory = $directory;
+ $this->_closeDirOnExit = false;
+ } else {
+ $this->_directory = new Zend_Search_Lucene_Storage_Directory_Filesystem($directory);
+ $this->_closeDirOnExit = true;
+ }
+
+ if ($create) {
+ $this->_writer = new Zend_Search_Lucene_Index_Writer($this->_directory, true);
+ } else {
+ $this->_writer = null;
+ }
+
+ $this->_segmentInfos = array();
+
+ $segmentsFile = $this->_directory->getFileObject('segments');
+
+ $format = $segmentsFile->readInt();
+
+ if ($format != (int)0xFFFFFFFF) {
+ throw new Zend_Search_Lucene_Exception('Wrong segments file format');
+ }
+
+ // read version
+ $segmentsFile->readLong();
+
+ // read counter
+ $segmentsFile->readInt();
+
+ $segments = $segmentsFile->readInt();
+
+ $this->_docCount = 0;
+
+ // read segmentInfos
+ for ($count = 0; $count < $segments; $count++) {
+ $segName = $segmentsFile->readString();
+ $segSize = $segmentsFile->readInt();
+ $this->_docCount += $segSize;
+
+ $this->_segmentInfos[$count] =
+ new Zend_Search_Lucene_Index_SegmentInfo($segName,
+ $segSize,
+ $this->_directory);
+ }
+ }
+
+
+ /**
+ * Object destructor
+ */
+ public function __destruct()
+ {
+ $this->commit();
+
+ if ($this->_closeDirOnExit) {
+ $this->_directory->close();
+ }
+ }
+
+ /**
+ * Returns an instance of Zend_Search_Lucene_Index_Writer for the index
+ *
+ * @return Zend_Search_Lucene_Index_Writer
+ */
+ public function getIndexWriter()
+ {
+ if (!$this->_writer instanceof Zend_Search_Lucene_Index_Writer) {
+ $this->_writer = new Zend_Search_Lucene_Index_Writer($this->_directory);
+ }
+
+ return $this->_writer;
+ }
+
+
+ /**
+ * Returns the Zend_Search_Lucene_Storage_Directory instance for this index.
+ *
+ * @return Zend_Search_Lucene_Storage_Directory
+ */
+ public function getDirectory()
+ {
+ return $this->_directory;
+ }
+
+
+ /**
+ * Returns the total number of documents in this index.
+ *
+ * @return integer
+ */
+ public function count()
+ {
+ return $this->_docCount;
+ }
+
+
+ /**
+ * Performs a query against the index and returns an array
+ * of Zend_Search_Lucene_Search_QueryHit objects.
+ * Input is a string or Zend_Search_Lucene_Search_Query.
+ *
+ * @param mixed $query
+ * @return array ZSearchHit
+ */
+ public function find($query)
+ {
+ if (is_string($query)) {
+ $query = Zend_Search_Lucene_Search_QueryParser::parse($query);
+ }
+
+ if (!$query instanceof Zend_Search_Lucene_Search_Query) {
+ throw new Zend_Search_Lucene_Exception('Query must be a string or Zend_Search_Lucene_Search_Query object');
+ }
+
+ $this->commit();
+
+ $hits = array();
+ $scores = array();
+
+ $docNum = $this->count();
+ for( $count=0; $count < $docNum; $count++ ) {
+ $docScore = $query->score( $count, $this);
+ if( $docScore != 0 ) {
+ $hit = new Zend_Search_Lucene_Search_QueryHit($this);
+ $hit->id = $count;
+ $hit->score = $docScore;
+
+ $hits[] = $hit;
+ $scores[] = $docScore;
+ }
+ }
+ array_multisort($scores, SORT_DESC, SORT_REGULAR, $hits);
+
+ return $hits;
+ }
+
+
+ /**
+ * Returns a list of all unique field names that exist in this index.
+ *
+ * @param boolean $indexed
+ * @return array
+ */
+ public function getFieldNames($indexed = false)
+ {
+ $result = array();
+ foreach( $this->_segmentInfos as $segmentInfo ) {
+ $result = array_merge($result, $segmentInfo->getFields($indexed));
+ }
+ return $result;
+ }
+
+
+ /**
+ * Returns a Zend_Search_Lucene_Document object for the document
+ * number $id in this index.
+ *
+ * @param integer|Zend_Search_Lucene_Search_QueryHit $id
+ * @return Zend_Search_Lucene_Document
+ */
+ public function getDocument($id)
+ {
+ if ($id instanceof Zend_Search_Lucene_Search_QueryHit) {
+ /* @var $id Zend_Search_Lucene_Search_QueryHit */
+ $id = $id->id;
+ }
+
+ if ($id >= $this->_docCount) {
+ /**
+ * @todo exception here?
+ */
+ return null;
+ }
+
+ $segCount = 0;
+ $nextSegmentStartId = $this->_segmentInfos[ 0 ]->count();
+ while( $nextSegmentStartId <= $id ) {
+ $segCount++;
+ $nextSegmentStartId += $this->_segmentInfos[ $segCount ]->count();
+ }
+ $segmentStartId = $nextSegmentStartId - $this->_segmentInfos[ $segCount ]->count();
+
+ $fdxFile = $this->_segmentInfos[ $segCount ]->openCompoundFile('.fdx');
+ $fdxFile->seek( ($id-$segmentStartId)*8, SEEK_CUR );
+ $fieldValuesPosition = $fdxFile->readLong();
+
+ $fdtFile = $this->_segmentInfos[ $segCount ]->openCompoundFile('.fdt');
+ $fdtFile->seek( $fieldValuesPosition, SEEK_CUR );
+ $fieldCount = $fdtFile->readVInt();
+
+ $doc = new Zend_Search_Lucene_Document();
+ for( $count = 0; $count < $fieldCount; $count++ ) {
+ $fieldNum = $fdtFile->readVInt();
+ $bits = $fdtFile->readByte();
+
+ $fieldInfo = $this->_segmentInfos[ $segCount ]->getField($fieldNum);
+
+ if( !($bits & 2) ) { // Text data
+ $field = new Zend_Search_Lucene_Field($fieldInfo->name,
+ $fdtFile->readString(),
+ true,
+ $fieldInfo->isIndexed,
+ $bits & 1 );
+ } else {
+ $field = new Zend_Search_Lucene_Field($fieldInfo->name,
+ $fdtFile->readBinary(),
+ true,
+ $fieldInfo->isIndexed,
+ $bits & 1 );
+ }
+
+ $doc->addField($field);
+ }
+
+ return $doc;
+ }
+
+
+ /**
+ * Returns an array of all the documents which contain term.
+ *
+ * @param Zend_Search_Lucene_Index_Term $term
+ * @return array
+ */
+ public function termDocs(Zend_Search_Lucene_Index_Term $term)
+ {
+ $result = array();
+ $segmentStartDocId = 0;
+
+ foreach ($this->_segmentInfos as $segInfo) {
+ $termInfo = $segInfo->getTermInfo($term);
+
+ if (!$termInfo instanceof Zend_Search_Lucene_Index_TermInfo) {
+ $segmentStartDocId += $segInfo->count();
+ continue;
+ }
+
+ $frqFile = $segInfo->openCompoundFile('.frq');
+ $frqFile->seek($termInfo->freqPointer,SEEK_CUR);
+ $docId = 0;
+ for( $count=0; $count < $termInfo->docFreq; $count++ ) {
+ $docDelta = $frqFile->readVInt();
+ if( $docDelta % 2 == 1 ) {
+ $docId += ($docDelta-1)/2;
+ } else {
+ $docId += $docDelta/2;
+ // read freq
+ $frqFile->readVInt();
+ }
+ $result[] = $segmentStartDocId + $docId;
+ }
+
+ $segmentStartDocId += $segInfo->count();
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Returns an array of all term positions in the documents.
+ * Return array structure: array( docId => array( pos1, pos2, ...), ...)
+ *
+ * @param Zend_Search_Lucene_Index_Term $term
+ * @return array
+ */
+ public function termPositions(Zend_Search_Lucene_Index_Term $term)
+ {
+ $result = array();
+ $segmentStartDocId = 0;
+ foreach( $this->_segmentInfos as $segInfo ) {
+ $termInfo = $segInfo->getTermInfo($term);
+
+ if (!$termInfo instanceof Zend_Search_Lucene_Index_TermInfo) {
+ $segmentStartDocId += $segInfo->count();
+ continue;
+ }
+
+ $frqFile = $segInfo->openCompoundFile('.frq');
+ $frqFile->seek($termInfo->freqPointer,SEEK_CUR);
+ $freqs = array();
+ $docId = 0;
+
+ for( $count = 0; $count < $termInfo->docFreq; $count++ ) {
+ $docDelta = $frqFile->readVInt();
+ if( $docDelta % 2 == 1 ) {
+ $docId += ($docDelta-1)/2;
+ $freqs[ $docId ] = 1;
+ } else {
+ $docId += $docDelta/2;
+ $freqs[ $docId ] = $frqFile->readVInt();
+ }
+ }
+
+ $prxFile = $segInfo->openCompoundFile('.prx');
+ $prxFile->seek($termInfo->proxPointer,SEEK_CUR);
+ foreach ($freqs as $docId => $freq) {
+ $termPosition = 0;
+ $positions = array();
+
+ for ($count = 0; $count < $freq; $count++ ) {
+ $termPosition += $prxFile->readVInt();
+ $positions[] = $termPosition;
+ }
+ $result[ $segmentStartDocId + $docId ] = $positions;
+ }
+
+ $segmentStartDocId += $segInfo->count();
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Returns the number of documents in this index containing the $term.
+ *
+ * @param Zend_Search_Lucene_Index_Term $term
+ * @return integer
+ */
+ public function docFreq(Zend_Search_Lucene_Index_Term $term)
+ {
+ $result = 0;
+ foreach ($this->_segmentInfos as $segInfo) {
+ $termInfo = $segInfo->getTermInfo($term);
+ if ($termInfo !== null) {
+ $result += $termInfo->docFreq;
+ }
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Retrive similarity used by index reader
+ *
+ * @return Zend_Search_Lucene_Search_Similarity
+ */
+ public function getSimilarity()
+ {
+ return Zend_Search_Lucene_Search_Similarity::getDefault();
+ }
+
+
+ /**
+ * Returns a normalization factor for "field, document" pair.
+ *
+ * @param integer $id
+ * @param string $fieldName
+ * @return Zend_Search_Lucene_Document
+ */
+ public function norm( $id, $fieldName )
+ {
+ if( $id >= $this->_docCount )
+ return null;
+
+ $segCount = 0;
+ $nextSegmentStartId = $this->_segmentInfos[ 0 ]->count();
+ while( $nextSegmentStartId <= $id ) {
+ $segCount++;
+ $nextSegmentStartId += $this->_segmentInfos[ $segCount ]->count();
+ }
+
+ $segmentStartId = $nextSegmentStartId - $this->_segmentInfos[ $segCount ]->count();
+
+ return $this->_segmentInfos[ $segCount ]->norm($id - $segmentStartId, $fieldName);
+ }
+
+
+ /**
+ * Adds a document to this index.
+ *
+ * @param Zend_Search_Lucene_Document $document
+ */
+ public function addDocument(Zend_Search_Lucene_Document $document)
+ {
+ if (!$this->_writer instanceof Zend_Search_Lucene_Index_Writer) {
+ $this->_writer = new Zend_Search_Lucene_Index_Writer($this->_directory);
+ }
+
+ $this->_writer->addDocument($document);
+ }
+
+
+ /**
+ * Commit changes resulting from delete() or undeleteAll() operations.
+ *
+ * @todo delete() and undeleteAll processing.
+ */
+ public function commit()
+ {
+ if ($this->_writer !== null) {
+ foreach ($this->_writer->commit() as $segmentName => $segmentInfo) {
+ if ($segmentInfo !== null) {
+ $this->_segmentInfos[] = $segmentInfo;
+ $this->_docCount += $segmentInfo->count();
+ } else {
+ foreach ($this->_segmentInfos as $segId => $segInfo) {
+ if ($segInfo->getName() == $segmentName) {
+ unset($this->_segmentInfos[$segId]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ /*************************************************************************
+ @todo UNIMPLEMENTED
+ *************************************************************************/
+
+ /**
+ * Returns an array of all terms in this index.
+ *
+ * @todo Implementation
+ * @return array
+ */
+ public function terms()
+ {
+ return array();
+ }
+
+
+ /**
+ * Returns true if any documents have been deleted from this index.
+ *
+ * @todo Implementation
+ * @return boolean
+ */
+ public function hasDeletions()
+ {
+ return false;
+ }
+
+
+ /**
+ * Deletes a document from the index. $doc may contain a Zend_Search_Lucene_Document
+ * or the number of the document to delete.
+ *
+ * @todo Implementation
+ * @param mixed $item_to_del
+ */
+ public function delete($doc)
+ {}
+
+
+ /**
+ * Undeletes all documents currently marked as deleted in this index.
+ *
+ * @todo Implementation
+ */
+ public function undeleteAll()
+ {}
+} \ No newline at end of file
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer.php
new file mode 100644
index 00000000..8e234c16
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer.php
@@ -0,0 +1,94 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Analysis_Token */
+require_once 'Zend/Search/Lucene/Analysis/Token.php';
+
+/** Zend_Search_Lucene_Analysis_Analyzer_Common_Text */
+require_once 'Zend/Search/Lucene/Analysis/Analyzer/Common/Text.php';
+
+/** Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive */
+require_once 'Zend/Search/Lucene/Analysis/Analyzer/Common/Text/CaseInsensitive.php';
+
+
+
+/**
+ * An Analyzer is used to analyze text.
+ * It thus represents a policy for extracting index terms from text.
+ *
+ * Note:
+ * Lucene Java implementation is oriented to streams. It provides effective work
+ * with a huge documents (more then 20Mb).
+ * But engine itself is not oriented such documents.
+ * Thus Zend_Search_Lucene analysis API works with data strings and sets (arrays).
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+abstract class Zend_Search_Lucene_Analysis_Analyzer
+{
+ /**
+ * The Analyzer implementation used by default.
+ *
+ * @var Zend_Search_Lucene_Analysis_Analyzer
+ */
+ static private $_defaultImpl;
+
+ /**
+ * Tokenize text to a terms
+ * Returns array of Zend_Search_Lucene_Analysis_Token objects
+ *
+ * @param string $data
+ * @return array
+ */
+ abstract public function tokenize($data);
+
+
+ /**
+ * Set the default Analyzer implementation used by indexing code.
+ *
+ * @param Zend_Search_Lucene_Analysis_Analyzer $similarity
+ */
+ static public function setDefault(Zend_Search_Lucene_Analysis_Analyzer $analyzer)
+ {
+ self::$_defaultImpl = $analyzer;
+ }
+
+
+ /**
+ * Return the default Analyzer implementation used by indexing code.
+ *
+ * @return Zend_Search_Lucene_Analysis_Analyzer
+ */
+ static public function getDefault()
+ {
+ if (!self::$_defaultImpl instanceof Zend_Search_Lucene_Analysis_Analyzer) {
+ self::$_defaultImpl = new Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive();
+ }
+
+ return self::$_defaultImpl;
+ }
+
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common.php
new file mode 100644
index 00000000..5c61e5b5
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Analysis_Analyzer */
+require_once 'Zend/Search/Lucene/Analysis/Analyzer.php';
+
+
+/**
+ * Common implementation of the Zend_Search_Lucene_Analysis_Analyzer interface.
+ * There are several standard standard subclasses provided by Zend_Search_Lucene/Analysis
+ * subpackage: Zend_Search_Lucene_Analysis_Analyzer_Common_Text, ZSearchHTMLAnalyzer, ZSearchXMLAnalyzer.
+ *
+ * @todo ZSearchHTMLAnalyzer and ZSearchXMLAnalyzer implementation
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+abstract class Zend_Search_Lucene_Analysis_Analyzer_Common extends Zend_Search_Lucene_Analysis_Analyzer
+{
+ /**
+ * The set of Token filters applied to the Token stream.
+ * Array of Zend_Search_Lucene_Analysis_TokenFilter objects.
+ *
+ * @var array
+ */
+ private $_filters = array();
+
+ /**
+ * Add Token filter to the Analyzer
+ *
+ * @param Zend_Search_Lucene_Analysis_TokenFilter $filter
+ */
+ public function addFilter(Zend_Search_Lucene_Analysis_TokenFilter $filter)
+ {
+ $this->_filters[] = $filter;
+ }
+
+ /**
+ * Apply filters to the token.
+ *
+ * @param Zend_Search_Lucene_Analysis_Token $token
+ * @return Zend_Search_Lucene_Analysis_Token
+ */
+ public function normalize(Zend_Search_Lucene_Analysis_Token $token)
+ {
+ foreach ($this->_filters as $filter) {
+ $token = $filter->normalize($token);
+ }
+
+ return $token;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text.php
new file mode 100644
index 00000000..2a80c1f8
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Analysis_Analyzer_Common */
+require_once 'Zend/Search/Lucene/Analysis/Analyzer/Common.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+class Zend_Search_Lucene_Analysis_Analyzer_Common_Text extends Zend_Search_Lucene_Analysis_Analyzer_Common
+{
+ /**
+ * Tokenize text to a terms
+ * Returns array of Zend_Search_Lucene_Analysis_Token objects
+ *
+ * @param string $data
+ * @return array
+ */
+ public function tokenize($data)
+ {
+ $tokenStream = array();
+
+ $position = 0;
+ while ($position < strlen($data)) {
+ // skip white space
+ while ($position < strlen($data) && !ctype_alpha( $data{$position} )) {
+ $position++;
+ }
+
+ $termStartPosition = $position;
+
+ // read token
+ while ($position < strlen($data) && ctype_alpha( $data{$position} )) {
+ $position++;
+ }
+
+ // Empty token, end of stream.
+ if ($position == $termStartPosition) {
+ break;
+ }
+
+ $token = new Zend_Search_Lucene_Analysis_Token(substr($data,
+ $termStartPosition,
+ $position-$termStartPosition),
+ $termStartPosition,
+ $position);
+ $tokenStream[] = $this->normalize($token);
+ }
+
+ return $tokenStream;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text/CaseInsensitive.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text/CaseInsensitive.php
new file mode 100644
index 00000000..d77e38d5
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Analyzer/Common/Text/CaseInsensitive.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Analysis_Analyzer_Common_Text */
+require_once 'Zend/Search/Lucene/Analysis/Analyzer/Common/Text.php';
+
+/** Zend_Search_Lucene_Analysis_TokenFilter_LowerCase */
+require_once 'Zend/Search/Lucene/Analysis/TokenFilter/LowerCase.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+class Zend_Search_Lucene_Analysis_Analyzer_Common_Text_CaseInsensitive extends Zend_Search_Lucene_Analysis_Analyzer_Common_Text
+{
+ public function __construct()
+ {
+ $this->addFilter(new Zend_Search_Lucene_Analysis_TokenFilter_LowerCase());
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Token.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Token.php
new file mode 100644
index 00000000..a60d5d96
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/Token.php
@@ -0,0 +1,170 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage document
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Analysis_Token
+{
+ /**
+ * The text of the term.
+ *
+ * @var string
+ */
+ private $_termText;
+
+ /**
+ * Start in source text.
+ *
+ * @var integer
+ */
+ private $_startOffset;
+
+ /**
+ * End in source text
+ *
+ * @var integer
+ */
+ private $_endOffset;
+
+ /**
+ * Lexical type.
+ *
+ * @var string
+ */
+ private $_type;
+
+ /**
+ * The position of this token relative to the previous Token.
+ *
+ * The default value is one.
+ *
+ * Some common uses for this are:
+ * Set it to zero to put multiple terms in the same position. This is
+ * useful if, e.g., a word has multiple stems. Searches for phrases
+ * including either stem will match. In this case, all but the first stem's
+ * increment should be set to zero: the increment of the first instance
+ * should be one. Repeating a token with an increment of zero can also be
+ * used to boost the scores of matches on that token.
+ *
+ * Set it to values greater than one to inhibit exact phrase matches.
+ * If, for example, one does not want phrases to match across removed stop
+ * words, then one could build a stop word filter that removes stop words and
+ * also sets the increment to the number of stop words removed before each
+ * non-stop word. Then exact phrase queries will only match when the terms
+ * occur with no intervening stop words.
+ *
+ * @var integer
+ */
+ private $_positionIncrement;
+
+
+ /**
+ * Object constructor
+ *
+ * @param string $text
+ * @param integer $start
+ * @param integer $end
+ * @param string $type
+ */
+ public function __construct($text, $start, $end, $type = 'word' )
+ {
+ $this->_termText = $text;
+ $this->_startOffset = $start;
+ $this->_endOffset = $end;
+ $this->_type = $type;
+
+ $this->_positionIncrement = 1;
+ }
+
+
+ /**
+ * positionIncrement setter
+ *
+ * @param integer $positionIncrement
+ */
+ public function setPositionIncrement($positionIncrement)
+ {
+ $this->_positionIncrement = $positionIncrement;
+ }
+
+ /**
+ * Returns the position increment of this Token.
+ *
+ * @return integer
+ */
+ public function getPositionIncrement()
+ {
+ return $this->_positionIncrement;
+ }
+
+ /**
+ * Returns the Token's term text.
+ *
+ * @return string
+ */
+ public function getTermText()
+ {
+ return $this->_termText;
+ }
+
+ /**
+ * Returns this Token's starting offset, the position of the first character
+ * corresponding to this token in the source text.
+ *
+ * Note:
+ * The difference between getEndOffset() and getStartOffset() may not be equal
+ * to strlen(Zend_Search_Lucene_Analysis_Token::getTermText()), as the term text may have been altered
+ * by a stemmer or some other filter.
+ *
+ * @return integer
+ */
+ public function getStartOffset()
+ {
+ return $this->_startOffset;
+ }
+
+ /**
+ * Returns this Token's ending offset, one greater than the position of the
+ * last character corresponding to this token in the source text.
+ *
+ * @return integer
+ */
+ public function getEndOffset()
+ {
+ return $this->_endOffset;
+ }
+
+ /**
+ * Returns this Token's lexical type. Defaults to 'word'.
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter.php
new file mode 100644
index 00000000..9ea5125f
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Analysis_Token */
+require_once 'Zend/Search/Lucene/Analysis/Token.php';
+
+
+/**
+ * Token filter converts (normalizes) Token ore removes it from a token stream.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+abstract class Zend_Search_Lucene_Analysis_TokenFilter
+{
+ /**
+ * Normalize Token or remove it (if null is returned)
+ *
+ * @param Zend_Search_Lucene_Analysis_Token $srcToken
+ * @return Zend_Search_Lucene_Analysis_Token
+ */
+ abstract public function normalize(Zend_Search_Lucene_Analysis_Token $srcToken);
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter/LowerCase.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter/LowerCase.php
new file mode 100644
index 00000000..53585e21
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Analysis/TokenFilter/LowerCase.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Analysis_TokenFilter */
+require_once 'Zend/Search/Lucene/Analysis/TokenFilter.php';
+
+
+/**
+ * Lower case Token filter.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Analysis
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+class Zend_Search_Lucene_Analysis_TokenFilter_LowerCase extends Zend_Search_Lucene_Analysis_TokenFilter
+{
+ /**
+ * Normalize Token or remove it (if null is returned)
+ *
+ * @param Zend_Search_Lucene_Analysis_Token $srcToken
+ * @return Zend_Search_Lucene_Analysis_Token
+ */
+ public function normalize(Zend_Search_Lucene_Analysis_Token $srcToken)
+ {
+ $newToken = new Zend_Search_Lucene_Analysis_Token(strtolower( $srcToken->getTermText() ),
+ $srcToken->getStartOffset(),
+ $srcToken->getEndOffset(),
+ $srcToken->getType());
+
+ $newToken->setPositionIncrement($srcToken->getPositionIncrement());
+
+ return $newToken;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Document.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Document.php
new file mode 100644
index 00000000..29c0c2d9
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Document.php
@@ -0,0 +1,109 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage document
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Field */
+require_once 'Zend/Search/Lucene/Field.php';
+
+
+/**
+ * A Document is a set of fields. Each field has a name and a textual value.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage document
+ * @copyright Copyright (c) 2005-2006 Zend Technologies Inc. (http://www.zend.com)
+ * @license Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Document
+{
+
+ /**
+ * Associative array Zend_Search_Lucene_Field objects where the keys to the
+ * array are the names of the fields.
+ *
+ * @var array
+ */
+ protected $_fields = array();
+
+ public $boost = 1.0;
+
+
+ /**
+ * Proxy method for getFieldValue(), provides more convenient access to
+ * the string value of a field.
+ *
+ * @param $offset
+ * @return string
+ */
+ public function __get($offset)
+ {
+ return $this->getFieldValue($offset);
+ }
+
+
+ /**
+ * Add a field object to this document.
+ *
+ * @param Zend_Search_Lucene_Field $field
+ */
+ public function addField(Zend_Search_Lucene_Field $field)
+ {
+ $this->_fields[$field->name] = $field;
+ }
+
+
+ /**
+ * Return an array with the names of the fields in this document.
+ *
+ * @return array
+ */
+ public function getFieldNames()
+ {
+ return array_keys($this->_fields);
+ }
+
+
+ /**
+ * Returns Zend_Search_Lucene_Field object for a named field in this document.
+ *
+ * @param string $fieldName
+ * @return Zend_Search_Lucene_Field
+ */
+ public function getField($fieldName)
+ {
+ if (!array_key_exists($fieldName, $this->_fields)) {
+ throw new Zend_Search_Lucene_Exception("Field name \"$fieldName\" not found in document.");
+ }
+ return $this->_fields[$fieldName];
+ }
+
+
+ /**
+ * Returns the string value of a named field in this document.
+ *
+ * @see __get()
+ * @return string
+ */
+ public function getFieldValue($fieldName)
+ {
+ return $this->getField($fieldName)->stringValue;
+ }
+
+}
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Exception.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Exception.php
new file mode 100644
index 00000000..5f12c5f6
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Exception.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * Framework base exception
+ */
+require_once 'Zend/Search/Exception.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Exception extends Zend_Search_Exception
+{}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Field.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Field.php
new file mode 100644
index 00000000..cce6bfce
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Field.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage document
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * A field is a section of a Document. Each field has two parts,
+ * a name and a value. Values may be free text or they may be atomic
+ * keywords, which are not further processed. Such keywords may
+ * be used to represent dates, urls, etc. Fields are optionally
+ * stored in the index, so that they may be returned with hits
+ * on the document.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage document
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Field
+{
+ public $kind;
+
+ public $name = 'body';
+ public $stringValue = null;
+ public $isStored = false;
+ public $isIndexed = true;
+ public $isTokenized = true;
+ public $isBinary = false;
+
+ public $storeTermVector = false;
+
+ public $boost = 1.0;
+
+ public function __construct($name, $stringValue, $isStored, $isIndexed, $isTokenized, $isBinary = false)
+ {
+ $this->name = $name;
+ $this->stringValue = $stringValue;
+ $this->isStored = $isStored;
+ $this->isIndexed = $isIndexed;
+ $this->isTokenized = $isTokenized;
+ $this->isBinary = $isBinary;
+
+ $this->storeTermVector = false;
+ $this->boost = 1.0;
+ }
+
+
+ /**
+ * Constructs a String-valued Field that is not tokenized, but is indexed
+ * and stored. Useful for non-text fields, e.g. date or url.
+ *
+ * @param string $name
+ * @param string $value
+ * @return Zend_Search_Lucene_Field
+ */
+ static public function Keyword($name, $value)
+ {
+ return new self($name, $value, true, true, false);
+ }
+
+
+ /**
+ * Constructs a String-valued Field that is not tokenized nor indexed,
+ * but is stored in the index, for return with hits.
+ *
+ * @param string $name
+ * @param string $value
+ * @return Zend_Search_Lucene_Field
+ */
+ static public function UnIndexed($name, $value)
+ {
+ return new self($name, $value, true, false, false);
+ }
+
+
+ /**
+ * Constructs a Binary String valued Field that is not tokenized nor indexed,
+ * but is stored in the index, for return with hits.
+ *
+ * @param string $name
+ * @param string $value
+ * @return Zend_Search_Lucene_Field
+ */
+ static public function Binary($name, $value)
+ {
+ return new self($name, $value, true, false, false, true);
+ }
+
+ /**
+ * Constructs a String-valued Field that is tokenized and indexed,
+ * and is stored in the index, for return with hits. Useful for short text
+ * fields, like "title" or "subject". Term vector will not be stored for this field.
+ *
+ * @param string $name
+ * @param string $value
+ * @return Zend_Search_Lucene_Field
+ */
+ static public function Text($name, $value)
+ {
+ return new self($name, $value, true, true, true);
+ }
+
+
+ /**
+ * Constructs a String-valued Field that is tokenized and indexed,
+ * but that is not stored in the index.
+ *
+ * @param string $name
+ * @param string $value
+ * @return Zend_Search_Lucene_Field
+ */
+ static public function UnStored($name, $value)
+ {
+ return new self($name, $value, false, true, true);
+ }
+
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Index/FieldInfo.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/FieldInfo.php
new file mode 100644
index 00000000..eaca4ecf
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/FieldInfo.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Index_FieldInfo
+{
+ public $name;
+ public $isIndexed;
+ public $number;
+ public $storeTermVector;
+
+ public function __construct( $name, $isIndexed, $number, $storeTermVector )
+ {
+ $this->name = $name;
+ $this->isIndexed = $isIndexed;
+ $this->number = $number;
+ $this->storeTermVector = $storeTermVector;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentInfo.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentInfo.php
new file mode 100644
index 00000000..f5c596a0
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentInfo.php
@@ -0,0 +1,412 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Exception */
+require_once 'Zend/Search/Lucene/Exception.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Index_SegmentInfo
+{
+ /**
+ * Number of docs in a segment
+ *
+ * @var integer
+ */
+ private $_docCount;
+
+ /**
+ * Segment name
+ *
+ * @var string
+ */
+ private $_name;
+
+ /**
+ * Term Dictionary Index
+ * Array of the Zend_Search_Lucene_Index_Term objects
+ * Corresponding Zend_Search_Lucene_Index_TermInfo object stored in the $_termDictionaryInfos
+ *
+ * @var array
+ */
+ private $_termDictionary;
+
+ /**
+ * Term Dictionary Index TermInfos
+ * Array of the Zend_Search_Lucene_Index_TermInfo objects
+ *
+ * @var array
+ */
+ private $_termDictionaryInfos;
+
+ /**
+ * Segment fields. Array of Zend_Search_Lucene_Index_FieldInfo objects for this segment
+ *
+ * @var array
+ */
+ private $_fields;
+
+ /**
+ * Field positions in a dictionary.
+ * (Term dictionary contains filelds ordered by names)
+ *
+ * @var array
+ */
+ private $_fieldsDicPositions;
+
+
+ /**
+ * Associative array where the key is the file name and the value is data offset
+ * in a compound segment file (.csf).
+ *
+ * @var array
+ */
+ private $_segFiles;
+
+ /**
+ * File system adapter.
+ *
+ * @var Zend_Search_Lucene_Storage_Directory_Filesystem
+ */
+ private $_directory;
+
+ /**
+ * Normalization factors.
+ * An array fieldName => normVector
+ * normVector is a binary string.
+ * Each byte corresponds to an indexed document in a segment and
+ * encodes normalization factor (float value, encoded by
+ * Zend_Search_Lucene_Search_Similarity::encodeNorm())
+ *
+ * @var array
+ */
+ private $_norms = array();
+
+ /**
+ * Zend_Search_Lucene_Index_SegmentInfo constructor needs Segmentname,
+ * Documents count and Directory as a parameter.
+ *
+ * @param string $name
+ * @param integer $docCount
+ * @param Zend_Search_Lucene_Storage_Directory $directory
+ */
+ public function __construct($name, $docCount, $directory)
+ {
+ $this->_name = $name;
+ $this->_docCount = $docCount;
+ $this->_directory = $directory;
+ $this->_termDictionary = null;
+
+ $this->_segFiles = array();
+ $cfsFile = $this->_directory->getFileObject($name . '.cfs');
+ $segFilesCount = $cfsFile->readVInt();
+
+ for ($count = 0; $count < $segFilesCount; $count++) {
+ $dataOffset = $cfsFile->readLong();
+ $fileName = $cfsFile->readString();
+ $this->_segFiles[$fileName] = $dataOffset;
+ }
+
+ $fnmFile = $this->openCompoundFile('.fnm');
+ $fieldsCount = $fnmFile->readVInt();
+ $fieldNames = array();
+ $fieldNums = array();
+ $this->_fields = array();
+ for ($count=0; $count < $fieldsCount; $count++) {
+ $fieldName = $fnmFile->readString();
+ $fieldBits = $fnmFile->readByte();
+ $this->_fields[$count] = new Zend_Search_Lucene_Index_FieldInfo($fieldName,
+ $fieldBits & 1,
+ $count,
+ $fieldBits & 2 );
+ if ($fieldBits & 0x10) {
+ // norms are omitted for the indexed field
+ $this->_norms[$count] = str_repeat(chr(Zend_Search_Lucene_Search_Similarity::encodeNorm(1.0)), $docCount);
+ }
+
+ $fieldNums[$count] = $count;
+ $fieldNames[$count] = $fieldName;
+ }
+ array_multisort($fieldNames, SORT_ASC, SORT_REGULAR, $fieldNums);
+ $this->_fieldsDicPositions = array_flip($fieldNums);
+ }
+
+ /**
+ * Opens index file stoted within compound index file
+ *
+ * @param string $extension
+ * @throws Zend_Search_Lucene_Exception
+ * @return Zend_Search_Lucene_Storage_File
+ */
+ public function openCompoundFile($extension)
+ {
+ $filename = $this->_name . $extension;
+
+ if( !isset($this->_segFiles[ $filename ]) ) {
+ throw new Zend_Search_Lucene_Exception('Index compound file doesn\'t contain '
+ . $filename . ' file.' );
+ }
+
+ $file = $this->_directory->getFileObject( $this->_name.".cfs" );
+ $file->seek( $this->_segFiles[ $filename ] );
+ return $file;
+ }
+
+ /**
+ * Returns field index or -1 if field is not found
+ *
+ * @param string $fieldName
+ * @return integer
+ */
+ public function getFieldNum($fieldName)
+ {
+ foreach( $this->_fields as $field ) {
+ if( $field->name == $fieldName ) {
+ return $field->number;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Returns field info for specified field
+ *
+ * @param integer $fieldNum
+ * @return ZSearchFieldInfo
+ */
+ public function getField($fieldNum)
+ {
+ return $this->_fields[$fieldNum];
+ }
+
+ /**
+ * Returns array of fields.
+ * if $indexed parameter is true, then returns only indexed fields.
+ *
+ * @param boolean $indexed
+ * @return array
+ */
+ public function getFields($indexed = false)
+ {
+ $result = array();
+ foreach( $this->_fields as $field ) {
+ if( (!$indexed) || $field->isIndexed ) {
+ $result[ $field->name ] = $field->name;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the total number of documents in this segment.
+ *
+ * @return integer
+ */
+ public function count()
+ {
+ return $this->_docCount;
+ }
+
+
+ /**
+ * Loads Term dictionary from TermInfoIndex file
+ */
+ protected function _loadDictionary()
+ {
+ if ($this->_termDictionary !== null) {
+ return;
+ }
+
+ $this->_termDictionary = array();
+ $this->_termDictionaryInfos = array();
+
+ $tiiFile = $this->openCompoundFile('.tii');
+ $tiVersion = $tiiFile->readInt();
+ if ($tiVersion != (int)0xFFFFFFFE) {
+ throw new Zend_Search_Lucene_Exception('Wrong TermInfoIndexFile file format');
+ }
+
+ $indexTermCount = $tiiFile->readLong();
+ $tiiFile->readInt(); // IndexInterval
+ $skipInterval = $tiiFile->readInt();
+
+ $prevTerm = '';
+ $freqPointer = 0;
+ $proxPointer = 0;
+ $indexPointer = 0;
+ for ($count = 0; $count < $indexTermCount; $count++) {
+ $termPrefixLength = $tiiFile->readVInt();
+ $termSuffix = $tiiFile->readString();
+ $termValue = substr( $prevTerm, 0, $termPrefixLength ) . $termSuffix;
+
+ $termFieldNum = $tiiFile->readVInt();
+ $docFreq = $tiiFile->readVInt();
+ $freqPointer += $tiiFile->readVInt();
+ $proxPointer += $tiiFile->readVInt();
+ if( $docFreq >= $skipInterval ) {
+ $skipDelta = $tiiFile->readVInt();
+ } else {
+ $skipDelta = 0;
+ }
+
+ $indexPointer += $tiiFile->readVInt();
+
+ $this->_termDictionary[] = new Zend_Search_Lucene_Index_Term($termValue,$termFieldNum);
+ $this->_termDictionaryInfos[] =
+ new Zend_Search_Lucene_Index_TermInfo($docFreq, $freqPointer, $proxPointer, $skipDelta, $indexPointer);
+ $prevTerm = $termValue;
+ }
+ }
+
+
+ /**
+ * Return segment name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+
+ /**
+ * Scans terms dictionary and returns term info
+ *
+ * @param Zend_Search_Lucene_Index_Term $term
+ * @return Zend_Search_Lucene_Index_TermInfo
+ */
+ public function getTermInfo($term)
+ {
+ $this->_loadDictionary();
+
+ $searchField = $this->getFieldNum($term->field);
+
+ if ($searchField == -1) {
+ return null;
+ }
+ $searchDicField = $this->_fieldsDicPositions[$searchField];
+
+ // search for appropriate value in dictionary
+ $lowIndex = 0;
+ $highIndex = count($this->_termDictionary)-1;
+ while ($highIndex >= $lowIndex) {
+ // $mid = ($highIndex - $lowIndex)/2;
+ $mid = ($highIndex + $lowIndex) >> 1;
+ $midTerm = $this->_termDictionary[$mid];
+
+ $delta = $searchDicField - $this->_fieldsDicPositions[$midTerm->field];
+ if ($delta == 0) {
+ $delta = strcmp($term->text, $midTerm->text);
+ }
+
+ if ($delta < 0) {
+ $highIndex = $mid-1;
+ } elseif ($delta > 0) {
+ $lowIndex = $mid+1;
+ } else {
+ return $this->_termDictionaryInfos[$mid]; // We got it!
+ }
+ }
+
+ if ($highIndex == -1) {
+ // Term is out of the dictionary range
+ return null;
+ }
+
+ $prevPosition = $highIndex;
+ $prevTerm = $this->_termDictionary[$prevPosition];
+ $prevTermInfo = $this->_termDictionaryInfos[ $prevPosition ];
+
+ $tisFile = $this->openCompoundFile('.tis');
+ $tiVersion = $tisFile->readInt();
+ if ($tiVersion != (int)0xFFFFFFFE) {
+ throw new Zend_Search_Lucene_Exception('Wrong TermInfoFile file format');
+ }
+
+ $termCount = $tisFile->readLong();
+ $indexInterval = $tisFile->readInt();
+ $skipInterval = $tisFile->readInt();
+
+ $tisFile->seek($prevTermInfo->indexPointer - 20 /* header size*/, SEEK_CUR);
+
+ $termValue = $prevTerm->text;
+ $termFieldNum = $prevTerm->field;
+ $freqPointer = $prevTermInfo->freqPointer;
+ $proxPointer = $prevTermInfo->proxPointer;
+ for ($count = $prevPosition*$indexInterval + 1;
+ $count < $termCount &&
+ ( $this->_fieldsDicPositions[ $termFieldNum ] < $searchDicField ||
+ ($this->_fieldsDicPositions[ $termFieldNum ] == $searchDicField &&
+ strcmp($termValue, $term->text) < 0) );
+ $count++) {
+ $termPrefixLength = $tisFile->readVInt();
+ $termSuffix = $tisFile->readString();
+ $termFieldNum = $tisFile->readVInt();
+ $termValue = substr( $termValue, 0, $termPrefixLength ) . $termSuffix;
+
+ $docFreq = $tisFile->readVInt();
+ $freqPointer += $tisFile->readVInt();
+ $proxPointer += $tisFile->readVInt();
+ if( $docFreq >= $skipInterval ) {
+ $skipOffset = $tisFile->readVInt();
+ } else {
+ $skipOffset = 0;
+ }
+ }
+
+ if ($termFieldNum == $searchField && $termValue == $term->text) {
+ return new Zend_Search_Lucene_Index_TermInfo($docFreq, $freqPointer, $proxPointer, $skipOffset);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns normalization factor for specified documents
+ *
+ * @param integer $id
+ * @param string $fieldName
+ * @return string
+ */
+ public function norm($id, $fieldName)
+ {
+ $fieldNum = $this->getFieldNum($fieldName);
+
+ if ( !($this->_fields[$fieldNum]->isIndexed) ) {
+ return null;
+ }
+
+ if ( !isset( $this->_norms[$fieldNum] )) {
+ $fFile = $this->openCompoundFile('.f' . $fieldNum);
+ $this->_norms[$fieldNum] = $fFile->readBytes($this->_docCount);
+ }
+
+ return Zend_Search_Lucene_Search_Similarity::decodeNorm( ord($this->_norms[$fieldNum]{$id}) );
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentWriter.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentWriter.php
new file mode 100644
index 00000000..f90d6ed3
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/SegmentWriter.php
@@ -0,0 +1,491 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Exception */
+require_once 'Zend/Search/Lucene/Exception.php';
+
+/** Zend_Search_Lucene_Analysis_Analyzer */
+require_once 'Zend/Search/Lucene/Analysis/Analyzer.php';
+
+/** Zend_Search_Lucene_Index_SegmentInfo */
+require_once 'Zend/Search/Lucene/Index/SegmentInfo.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Index_SegmentWriter
+{
+ /**
+ * Expert: The fraction of terms in the "dictionary" which should be stored
+ * in RAM. Smaller values use more memory, but make searching slightly
+ * faster, while larger values use less memory and make searching slightly
+ * slower. Searching is typically not dominated by dictionary lookup, so
+ * tweaking this is rarely useful.
+ *
+ * @var integer
+ */
+ static public $indexInterval = 128;
+
+ /** Expert: The fraction of TermDocs entries stored in skip tables.
+ * Larger values result in smaller indexes, greater acceleration, but fewer
+ * accelerable cases, while smaller values result in bigger indexes,
+ * less acceleration and more
+ * accelerable cases. More detailed experiments would be useful here.
+ *
+ * 0x0x7FFFFFFF indicates that we don't use skip data
+ * Default value is 16
+ *
+ * @var integer
+ */
+ static public $skipInterval = 0x7FFFFFFF;
+
+ /**
+ * Number of docs in a segment
+ *
+ * @var integer
+ */
+ private $_docCount;
+
+ /**
+ * Segment name
+ *
+ * @var string
+ */
+ private $_name;
+
+ /**
+ * File system adapter.
+ *
+ * @var Zend_Search_Lucene_Storage_Directory
+ */
+ private $_directory;
+
+ /**
+ * List of the index files.
+ * Used for automatic compound file generation
+ *
+ * @var unknown_type
+ */
+ private $_files;
+
+ /**
+ * Term Dictionary
+ * Array of the Zend_Search_Lucene_Index_Term objects
+ * Corresponding Zend_Search_Lucene_Index_TermInfo object stored in the $_termDictionaryInfos
+ *
+ * @var array
+ */
+ private $_termDictionary;
+
+ /**
+ * Documents, which contain the term
+ *
+ * @var array
+ */
+ private $_termDocs;
+
+ /**
+ * Segment fields. Array of Zend_Search_Lucene_Index_FieldInfo objects for this segment
+ *
+ * @var array
+ */
+ private $_fields;
+
+ /**
+ * Normalization factors.
+ * An array fieldName => normVector
+ * normVector is a binary string.
+ * Each byte corresponds to an indexed document in a segment and
+ * encodes normalization factor (float value, encoded by
+ * Zend_Search_Lucene_Search_Similarity::encodeNorm())
+ *
+ * @var array
+ */
+ private $_norms;
+
+
+ /**
+ * '.fdx' file - Stored Fields, the field index.
+ *
+ * @var Zend_Search_Lucene_Storage_File
+ */
+ private $_fdxFile;
+
+ /**
+ * '.fdx' file - Stored Fields, the field data.
+ *
+ * @var Zend_Search_Lucene_Storage_File
+ */
+ private $_fdtFile;
+
+
+ /**
+ * Object constructor.
+ *
+ * @param Zend_Search_Lucene_Storage_Directory $directory
+ * @param string $name
+ */
+ public function __construct($directory, $name)
+ {
+ $this->_directory = $directory;
+ $this->_name = $name;
+ $this->_docCount = 0;
+
+ $this->_fields = array();
+ $this->_termDocs = array();
+ $this->_files = array();
+ $this->_norms = array();
+
+ $this->_fdxFile = null;
+ $this->_fdtFile = null;
+ }
+
+
+ /**
+ * Add field to the segment
+ *
+ * @param Zend_Search_Lucene_Field $field
+ */
+ private function _addFieldInfo(Zend_Search_Lucene_Field $field)
+ {
+ if (!isset($this->_fields[$field->name])) {
+ $this->_fields[$field->name] =
+ new Zend_Search_Lucene_Index_FieldInfo($field->name,
+ $field->isIndexed,
+ count($this->_fields),
+ $field->storeTermVector);
+ } else {
+ $this->_fields[$field->name]->isIndexed |= $field->isIndexed;
+ $this->_fields[$field->name]->storeTermVector |= $field->storeTermVector;
+ }
+ }
+
+
+ /**
+ * Adds a document to this segment.
+ *
+ * @param Zend_Search_Lucene_Document $document
+ * @throws Zend_Search_Lucene_Exception
+ */
+ public function addDocument(Zend_Search_Lucene_Document $document)
+ {
+ $storedFields = array();
+
+ foreach ($document->getFieldNames() as $fieldName) {
+ $field = $document->getField($fieldName);
+ $this->_addFieldInfo($field);
+
+ if ($field->storeTermVector) {
+ /**
+ * @todo term vector storing support
+ */
+ throw new Zend_Search_Lucene_Exception('Store term vector functionality is not supported yet.');
+ }
+
+ if ($field->isIndexed) {
+ if ($field->isTokenized) {
+ $tokenList = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($field->stringValue);
+ } else {
+ $tokenList = array();
+ $tokenList[] = new Zend_Search_Lucene_Analysis_Token($field->stringValue, 0, strlen($field->stringValue));
+ }
+
+ $position = 0;
+ foreach ($tokenList as $token) {
+ $term = new Zend_Search_Lucene_Index_Term($token->getTermText(), $field->name);
+ $termKey = $term->key();
+
+ if (!isset($this->_termDictionary[$termKey])) {
+ // New term
+ $this->_termDictionary[$termKey] = $term;
+ $this->_termDocs[$termKey] = array();
+ $this->_termDocs[$termKey][$this->_docCount] = array();
+ } else if (!isset($this->_termDocs[$termKey][$this->_docCount])) {
+ // Existing term, but new term entry
+ $this->_termDocs[$termKey][$this->_docCount] = array();
+ }
+ $position += $token->getPositionIncrement();
+ $this->_termDocs[$termKey][$this->_docCount][] = $position;
+ }
+ }
+
+ if ($field->isStored) {
+ $storedFields[] = $field;
+ }
+ }
+
+ if (count($storedFields) != 0) {
+ if (!isset($this->_fdxFile)) {
+ $this->_fdxFile = $this->_directory->createFile($this->_name . '.fdx');
+ $this->_fdtFile = $this->_directory->createFile($this->_name . '.fdt');
+
+ $this->_files[] = $this->_name . '.fdx';
+ $this->_files[] = $this->_name . '.fdt';
+ }
+
+ $this->_fdxFile->writeLong($this->_fdtFile->tell());
+
+ $this->_fdtFile->writeVInt(count($storedFields));
+ foreach ($storedFields as $field) {
+ $this->_fdtFile->writeVInt($this->_fields[$field->name]->number);
+ $this->_fdtFile->writeByte($field->isTokenized ? 0x01 : 0x00 |
+ $field->isBinary ? 0x02 : 0x00 |
+ 0x00 /* 0x04 - third bit, compressed (ZLIB) */ );
+ if ($field->isBinary) {
+ $this->_fdtFile->writeVInt(strlen($field->stringValue));
+ $this->_fdtFile->writeBytes($field->stringValue);
+ } else {
+ $this->_fdtFile->writeString($field->stringValue);
+ }
+ }
+ }
+
+ $this->_docCount++;
+ }
+
+
+ /**
+ * Dump Field Info (.fnm) segment file
+ */
+ private function _dumpFNM()
+ {
+ $fnmFile = $this->_directory->createFile($this->_name . '.fnm');
+ $fnmFile->writeVInt(count($this->_fields));
+
+ foreach ($this->_fields as $field) {
+ $fnmFile->writeString($field->name);
+ $fnmFile->writeByte(($field->isIndexed ? 0x01 : 0x00) |
+ ($field->storeTermVector ? 0x02 : 0x00) |
+// not supported yet 0x04 /* term positions are stored with the term vectors */ |
+// not supported yet 0x08 /* term offsets are stored with the term vectors */ |
+/* not supported yet */ 0x10 /* norms are omitted for the indexed field */
+ );
+ }
+
+ $this->_files[] = $this->_name . '.fnm';
+ }
+
+
+ /**
+ * Dump Term Dictionary segment file entry.
+ * Used to write entry to .tis or .tii files
+ *
+ * @param Zend_Search_Lucene_Storage_File $dicFile
+ * @param Zend_Search_Lucene_Index_Term $prevTerm
+ * @param Zend_Search_Lucene_Index_Term $term
+ * @param Zend_Search_Lucene_Index_TermInfo $prevTermInfo
+ * @param Zend_Search_Lucene_Index_TermInfo $termInfo
+ */
+ private function _dumpTermDictEntry(Zend_Search_Lucene_Storage_File $dicFile,
+ &$prevTerm, Zend_Search_Lucene_Index_Term $term,
+ &$prevTermInfo, Zend_Search_Lucene_Index_TermInfo $termInfo)
+ {
+ if (isset($prevTerm) && $prevTerm->field == $term->field) {
+ $prefixLength = 0;
+ while ($prefixLength < strlen($prevTerm->text) &&
+ $prefixLength < strlen($term->text) &&
+ $prevTerm->text{$prefixLength} == $term->text{$prefixLength}
+ ) {
+ $prefixLength++;
+ }
+ // Write preffix length
+ $dicFile->writeVInt($prefixLength);
+ // Write suffix
+ $dicFile->writeString( substr($term->text, $prefixLength) );
+ } else {
+ // Write preffix length
+ $dicFile->writeVInt(0);
+ // Write suffix
+ $dicFile->writeString($term->text);
+ }
+ // Write field number
+ $dicFile->writeVInt($term->field);
+ // DocFreq (the count of documents which contain the term)
+ $dicFile->writeVInt($termInfo->docFreq);
+
+ $prevTerm = $term;
+
+ if (!isset($prevTermInfo)) {
+ // Write FreqDelta
+ $dicFile->writeVInt($termInfo->freqPointer);
+ // Write ProxDelta
+ $dicFile->writeVInt($termInfo->proxPointer);
+ } else {
+ // Write FreqDelta
+ $dicFile->writeVInt($termInfo->freqPointer - $prevTermInfo->freqPointer);
+ // Write ProxDelta
+ $dicFile->writeVInt($termInfo->proxPointer - $prevTermInfo->proxPointer);
+ }
+ // Write SkipOffset - it's not 0 when $termInfo->docFreq > self::$skipInterval
+ if ($termInfo->skipOffset != 0) {
+ $dicFile->writeVInt($termInfo->skipOffset);
+ }
+
+ $prevTermInfo = $termInfo;
+ }
+
+ /**
+ * Dump Term Dictionary (.tis) and Term Dictionary Index (.tii) segment files
+ */
+ private function _dumpDictionary()
+ {
+ $tisFile = $this->_directory->createFile($this->_name . '.tis');
+ $tisFile->writeInt((int)0xFFFFFFFE);
+ $tisFile->writeLong(count($this->_termDictionary));
+ $tisFile->writeInt(self::$indexInterval);
+ $tisFile->writeInt(self::$skipInterval);
+
+ $tiiFile = $this->_directory->createFile($this->_name . '.tii');
+ $tiiFile->writeInt((int)0xFFFFFFFE);
+ $tiiFile->writeLong((int)((count($this->_termDictionary) - 1)/self::$indexInterval) + 1);
+ $tiiFile->writeInt(self::$indexInterval);
+ $tiiFile->writeInt(self::$skipInterval);
+
+ $frqFile = $this->_directory->createFile($this->_name . '.frq');
+ $prxFile = $this->_directory->createFile($this->_name . '.prx');
+
+ $termKeys = array_keys($this->_termDictionary);
+ sort($termKeys, SORT_STRING);
+
+ $termCount = 0;
+
+ $prevTerm = null;
+ $prevTermInfo = null;
+ $prevIndexTerm = null;
+ $prevIndexTermInfo = null;
+ $prevIndexPosition = 0;
+
+ foreach ($termKeys as $termId) {
+ $freqPointer = $frqFile->tell();
+ $proxPointer = $prxFile->tell();
+
+ $prevDoc = 0;
+ foreach ($this->_termDocs[$termId] as $docId => $termPositions) {
+ $docDelta = ($docId - $prevDoc)*2;
+ $prevDoc = $docId;
+ if (count($termPositions) > 1) {
+ $frqFile->writeVInt($docDelta);
+ $frqFile->writeVInt(count($termPositions));
+ } else {
+ $frqFile->writeVInt($docDelta + 1);
+ }
+
+ $prevPosition = 0;
+ foreach ($termPositions as $position) {
+ $prxFile->writeVInt($position - $prevPosition);
+ $prevPosition = $position;
+ }
+ }
+
+ if (count($this->_termDocs[$termId]) >= self::$skipInterval) {
+ /**
+ * @todo Write Skip Data to a freq file.
+ * It's not used now, but must be implemented to be compatible with Lucene
+ */
+ $skipOffset = $frqFile->tell() - $freqPointer;
+ } else {
+ $skipOffset = 0;
+ }
+
+ $term = new Zend_Search_Lucene_Index_Term($this->_termDictionary[$termId]->text,
+ $this->_fields[$this->_termDictionary[$termId]->field]->number);
+ $termInfo = new Zend_Search_Lucene_Index_TermInfo(count($this->_termDocs[$termId]),
+ $freqPointer, $proxPointer, $skipOffset);
+
+ $this->_dumpTermDictEntry($tisFile, $prevTerm, $term, $prevTermInfo, $termInfo);
+
+ if ($termCount % self::$indexInterval == 0) {
+ $this->_dumpTermDictEntry($tiiFile, $prevIndexTerm, $term, $prevIndexTermInfo, $termInfo);
+
+ $indexPosition = $tisFile->tell();
+ $tiiFile->writeVInt($indexPosition - $prevIndexPosition);
+ $prevIndexPosition = $indexPosition;
+ }
+ $termCount++;
+ }
+
+ $this->_files[] = $this->_name . '.tis';
+ $this->_files[] = $this->_name . '.tii';
+ $this->_files[] = $this->_name . '.frq';
+ $this->_files[] = $this->_name . '.prx';
+ }
+
+
+ /**
+ * Generate compound index file
+ */
+ private function _generateCFS()
+ {
+ $cfsFile = $this->_directory->createFile($this->_name . '.cfs');
+ $cfsFile->writeVInt(count($this->_files));
+
+ $dataOffsetPointers = array();
+ foreach ($this->_files as $fileName) {
+ $dataOffsetPointers[$fileName] = $cfsFile->tell();
+ $cfsFile->writeLong(0); // write dummy data
+ $cfsFile->writeString($fileName);
+ }
+
+ foreach ($this->_files as $fileName) {
+ // Get actual data offset
+ $dataOffset = $cfsFile->tell();
+ // Seek to the data offset pointer
+ $cfsFile->seek($dataOffsetPointers[$fileName]);
+ // Write actual data offset value
+ $cfsFile->writeLong($dataOffset);
+ // Seek back to the end of file
+ $cfsFile->seek($dataOffset);
+
+ $dataFile = $this->_directory->getFileObject($fileName);
+ $cfsFile->writeBytes($dataFile->readBytes($this->_directory->fileLength($fileName)));
+
+ $this->_directory->deleteFile($fileName);
+ }
+ }
+
+
+ /**
+ * Close segment, write it to disk and return segment info
+ *
+ * @return Zend_Search_Lucene_Index_SegmentInfo
+ */
+ public function close()
+ {
+ if ($this->_docCount == 0) {
+ return null;
+ }
+
+ $this->_dumpFNM();
+ $this->_dumpDictionary();
+
+ $this->_generateCFS();
+
+ return new Zend_Search_Lucene_Index_SegmentInfo($this->_name,
+ $this->_docCount,
+ $this->_directory);
+ }
+
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Index/Term.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/Term.php
new file mode 100644
index 00000000..e30ce587
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/Term.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * A Term represents a word from text. This is the unit of search. It is
+ * composed of two elements, the text of the word, as a string, and the name of
+ * the field that the text occured in, an interned string.
+ *
+ * Note that terms may represent more than words from text fields, but also
+ * things like dates, email addresses, urls, etc.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Index_Term
+{
+ /**
+ * Field name or field number (depending from context)
+ *
+ * @var mixed
+ */
+ public $field;
+
+ /**
+ * Term value
+ *
+ * @var string
+ */
+ public $text;
+
+
+ /**
+ * @todo docblock
+ */
+ public function __construct( $text, $field = 'contents' )
+ {
+ $this->field = $field;
+ $this->text = $text;
+ }
+
+
+ /**
+ * @todo docblock
+ */
+ public function key()
+ {
+ return $this->field . chr(0) . $this->text;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Index/TermInfo.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/TermInfo.php
new file mode 100644
index 00000000..ddef721d
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/TermInfo.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * A Zend_Search_Lucene_Index_TermInfo represents a record of information stored for a term.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Index_TermInfo
+{
+ /**
+ * The number of documents which contain the term.
+ *
+ * @var integer
+ */
+ public $docFreq;
+
+ /**
+ * Data offset in a Frequencies file.
+ *
+ * @var integer
+ */
+ public $freqPointer;
+
+ /**
+ * Data offset in a Positions file.
+ *
+ * @var integer
+ */
+ public $proxPointer;
+
+ /**
+ * ScipData offset in a Frequencies file.
+ *
+ * @var integer
+ */
+ public $skipOffset;
+
+ /**
+ * Term offset of the _next_ term in a TermDictionary file.
+ * Used only for Term Index
+ *
+ * @var integer
+ */
+ public $indexPointer;
+
+ public function __construct($docFreq, $freqPointer, $proxPointer, $skipOffset, $indexPointer = null)
+ {
+ $this->docFreq = $docFreq;
+ $this->freqPointer = $freqPointer;
+ $this->proxPointer = $proxPointer;
+ $this->skipOffset = $skipOffset;
+ $this->indexPointer = $indexPointer;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Index/Writer.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/Writer.php
new file mode 100644
index 00000000..da4af000
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Index/Writer.php
@@ -0,0 +1,308 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Index_SegmentWriter */
+require_once 'Zend/Search/Lucene/Index/SegmentWriter.php';
+
+/** Zend_Search_Lucene_Index_SegmentInfo */
+require_once 'Zend/Search/Lucene/Index/SegmentInfo.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Index_Writer
+{
+ /**
+ * @todo Implement segment merger
+ * @todo Implement mergeFactor, minMergeDocs, maxMergeDocs usage.
+ * @todo Implement Analyzer substitution
+ * @todo Implement Zend_Search_Lucene_Storage_DirectoryRAM and Zend_Search_Lucene_Storage_FileRAM to use it for
+ * temporary index files
+ * @todo Directory lock processing
+ */
+
+ /**
+ * File system adapter.
+ *
+ * @var Zend_Search_Lucene_Storage_Directory
+ */
+ private $_directory = null;
+
+
+ /**
+ * Index version
+ * Counts how often the index has been changed by adding or deleting docs
+ *
+ * @var integer
+ */
+ private $_version;
+
+ /**
+ * Segment name counter.
+ * Used to name new segments .
+ *
+ * @var integer
+ */
+ private $_segmentNameCounter;
+
+ /**
+ * Number of the segments in the index
+ *
+ * @var inteher
+ */
+ private $_segments;
+
+ /**
+ * Determines how often segment indices
+ * are merged by addDocument().
+ *
+ * @var integer
+ */
+ public $mergeFactor;
+
+ /**
+ * Determines the minimal number of documents required before
+ * the buffered in-memory documents are merging and a new Segment
+ * is created.
+ *
+ * @var integer
+ */
+ public $minMergeDocs;
+
+ /**
+ * Determines the largest number of documents ever merged by addDocument().
+ *
+ * @var integer
+ */
+ public $maxMergeDocs;
+
+ /**
+ * List of the segments, created by index writer
+ * Array of Zend_Search_Lucene_Index_SegmentInfo objects
+ *
+ * @var array
+ */
+ private $_newSegments;
+
+ /**
+ * Current segment to add documents
+ *
+ * @var Zend_Search_Lucene_Index_SegmentWriter
+ */
+ private $_currentSegment;
+
+ /**
+ * Opens the index for writing
+ *
+ * IndexWriter constructor needs Directory as a parameter. It should be
+ * a string with a path to the index folder or a Directory object.
+ * Second constructor parameter create is optional - true to create the
+ * index or overwrite the existing one.
+ *
+ * @param Zend_Search_Lucene_Storage_Directory $directory
+ * @param boolean $create
+ */
+ public function __construct(Zend_Search_Lucene_Storage_Directory $directory, $create = false)
+ {
+ $this->_directory = $directory;
+
+ if ($create) {
+ foreach ($this->_directory->fileList() as $file) {
+ if ($file == 'deletable' ||
+ $file == 'segments' ||
+ substr($file, strlen($file)-4) == '.cfs') {
+ $this->_directory->deleteFile($file);
+ }
+ }
+ $segmentsFile = $this->_directory->createFile('segments');
+ $segmentsFile->writeInt((int)0xFFFFFFFF);
+ // write version
+ $segmentsFile->writeLong(0);
+ // write name counter
+ $segmentsFile->writeInt(0);
+ // write segment counter
+ $segmentsFile->writeInt(0);
+
+ $deletableFile = $this->_directory->createFile('deletable');
+ // write counter
+ $deletableFile->writeInt(0);
+
+ $this->_version = 0;
+ $this->_segmentNameCounter = 0;
+ $this->_segments = 0;
+ } else {
+ $segmentsFile = $this->_directory->getFileObject('segments');
+ $format = $segmentsFile->readInt();
+ if ($format != (int)0xFFFFFFFF) {
+ throw new Zend_Search_Lucene_Exception('Wrong segments file format');
+ }
+
+ // read version
+ $this->_version = $segmentsFile->readLong();
+ // read counter
+ $this->_segmentNameCounter = $segmentsFile->readInt();
+ // read segment counter
+ $this->_segments = $segmentsFile->readInt();
+ }
+
+ $this->_newSegments = array();
+ $this->_currentSegment = null;
+ }
+
+ /**
+ * Adds a document to this index.
+ *
+ * @param Zend_Search_Lucene_Document $document
+ */
+ public function addDocument(Zend_Search_Lucene_Document $document)
+ {
+ if ($this->_currentSegment === null) {
+ $this->_currentSegment =
+ new Zend_Search_Lucene_Index_SegmentWriter($this->_directory, $this->_newSegmentName());
+ }
+ $this->_currentSegment->addDocument($document);
+ $this->_version++;
+ }
+
+
+
+ /**
+ * Update segments file by adding current segment to a list
+ * @todo !!!!!Finish the implementation
+ *
+ * @throws Zend_Search_Lucene_Exception
+ */
+ private function _updateSegments()
+ {
+ $segmentsFile = $this->_directory->getFileObject('segments');
+ $newSegmentFile = $this->_directory->createFile('segments.new');
+
+ $newSegmentFile->writeInt((int)0xFFFFFFFF);
+ $newSegmentFile->writeLong($this->_version);
+ $newSegmentFile->writeInt($this->_segmentNameCounter);
+ $newSegmentFile->writeInt($this->_segments + count($this->_newSegments));
+
+ $segmentsFile->seek(20);
+ $newSegmentFile->writeBytes($segmentsFile->readBytes($this->_directory->fileLength('segments') - 20));
+
+ foreach ($this->_newSegments as $segmentName => $segmentInfo) {
+ $newSegmentFile->writeString($segmentName);
+ $newSegmentFile->writeInt($segmentInfo->count());
+ }
+
+ $this->_directory->renameFile('segments.new', 'segments');
+ }
+
+
+ /**
+ * Commit current changes
+ * returns array of new segments
+ *
+ * @return array
+ */
+ public function commit()
+ {
+ if ($this->_currentSegment !== null) {
+ $newSegment = $this->_currentSegment->close();
+ if ($newSegment !== null) {
+ $this->_newSegments[$newSegment->getName()] = $newSegment;
+ }
+ $this->_currentSegment = null;
+ }
+
+ if (count($this->_newSegments) != 0) {
+ $this->_updateSegments();
+ }
+
+ $result = $this->_newSegments;
+ $this->_newSegments = array();
+
+ return $result;
+ }
+
+
+ /**
+ * Merges the provided indexes into this index.
+ *
+ * @param array $readers
+ * @return void
+ */
+ public function addIndexes($readers)
+ {
+ /**
+ * @todo implementation
+ */
+ }
+
+
+ /**
+ * Returns the number of documents currently in this index.
+ *
+ * @return integer
+ */
+ public function docCount($readers)
+ {
+ /**
+ * @todo implementation
+ */
+ }
+
+
+ /**
+ * Flushes all changes to an index and closes all associated files.
+ *
+ */
+ public function close()
+ {
+ /**
+ * @todo implementation
+ */
+ }
+
+
+ /**
+ * Merges all segments together into a single segment, optimizing
+ * an index for search.
+ *
+ * return void
+ */
+ public function optimize()
+ {
+ /**
+ * @todo implementation
+ */
+ }
+
+ /**
+ * Get name for new segment
+ *
+ * @return string
+ */
+ private function _newSegmentName()
+ {
+ return '_' . base_convert($this->_segmentNameCounter++, 10, 36);
+ }
+
+}
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query.php
new file mode 100644
index 00000000..dd8698e8
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+abstract class Zend_Search_Lucene_Search_Query
+{
+
+ /**
+ * query boost factor
+ *
+ * @var float
+ */
+ private $_boost = 1.0;
+
+ /**
+ * Query weight
+ *
+ * @var Zend_Search_Lucene_Search_Weight
+ */
+ protected $_weight;
+
+
+ /**
+ * Gets the boost for this clause. Documents matching
+ * this clause will (in addition to the normal weightings) have their score
+ * multiplied by boost. The boost is 1.0 by default.
+ *
+ * @return float
+ */
+ public function getBoost()
+ {
+ return $this->_boost;
+ }
+
+ /**
+ * Sets the boost for this query clause to $boost.
+ *
+ * @param float $boost
+ */
+ public function setBoost($boost)
+ {
+ $this->_boost = $boost;
+ }
+
+ /**
+ * Score specified document
+ *
+ * @param integer $docId
+ * @param Zend_Search_Lucene $reader
+ * @return float
+ */
+ abstract public function score($docId, $reader);
+
+ /**
+ * Constructs an appropriate Weight implementation for this query.
+ *
+ * @param Zend_Search_Lucene $reader
+ * @return Zend_Search_Lucene_Search_Weight
+ */
+ abstract protected function _createWeight($reader);
+
+ /**
+ * Constructs an initializes a Weight for a query.
+ *
+ * @param Zend_Search_Lucene $reader
+ */
+ protected function _initWeight($reader)
+ {
+ $this->_weight = $this->_createWeight($reader);
+ $sum = $this->_weight->sumOfSquaredWeights();
+ $queryNorm = $reader->getSimilarity()->queryNorm($sum);
+ $this->_weight->normalize($queryNorm);
+ }
+
+} \ No newline at end of file
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/MultiTerm.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/MultiTerm.php
new file mode 100644
index 00000000..4a99c0f7
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/MultiTerm.php
@@ -0,0 +1,437 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Search_Query */
+require_once 'Zend/Search/Lucene/Search/Query.php';
+
+/** Zend_Search_Lucene_Search_Weight_MultiTerm */
+require_once 'Zend/Search/Lucene/Search/Weight/MultiTerm.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_Query_MultiTerm extends Zend_Search_Lucene_Search_Query
+{
+
+ /**
+ * Terms to find.
+ * Array of Zend_Search_Lucene_Index_Term
+ *
+ * @var array
+ */
+ private $_terms = array();
+
+ /**
+ * Term signs.
+ * If true then term is required.
+ * If false then term is prohibited.
+ * If null then term is neither prohibited, nor required
+ *
+ * If array is null then all terms are required
+ *
+ * @var array
+ */
+
+ private $_signs = array();
+
+ /**
+ * Result vector.
+ * Bitset or array of document IDs
+ * (depending from Bitset extension availability).
+ *
+ * @var mixed
+ */
+ private $_resVector = null;
+
+ /**
+ * Terms positions vectors.
+ * Array of Arrays:
+ * term1Id => (docId => array( pos1, pos2, ... ), ...)
+ * term2Id => (docId => array( pos1, pos2, ... ), ...)
+ *
+ * @var array
+ */
+ private $_termsPositions = array();
+
+
+ /**
+ * A score factor based on the fraction of all query terms
+ * that a document contains.
+ * float for conjunction queries
+ * array of float for non conjunction queries
+ *
+ * @var mixed
+ */
+ private $_coord = null;
+
+
+ /**
+ * Terms weights
+ * array of Zend_Search_Lucene_Search_Weight
+ *
+ * @var array
+ */
+ private $_weights = array();
+
+
+ /**
+ * Class constructor. Create a new multi-term query object.
+ *
+ * @param array $terms Array of Zend_Search_Lucene_Index_Term objects
+ * @param array $signs Array of signs. Sign is boolean|null.
+ * @return void
+ */
+ public function __construct($terms = null, $signs = null)
+ {
+ /**
+ * @todo Check contents of $terms and $signs before adding them.
+ */
+ if (is_array($terms)) {
+ $this->_terms = $terms;
+
+ $this->_signs = null;
+ // Check if all terms are required
+ if (is_array($signs)) {
+ foreach ($signs as $sign ) {
+ if ($sign !== true) {
+ $this->_signs = $signs;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Add a $term (Zend_Search_Lucene_Index_Term) to this query.
+ *
+ * The sign is specified as:
+ * TRUE - term is required
+ * FALSE - term is prohibited
+ * NULL - term is neither prohibited, nor required
+ *
+ * @param Zend_Search_Lucene_Index_Term $term
+ * @param boolean|null $sign
+ * @return void
+ */
+ public function addTerm(Zend_Search_Lucene_Index_Term $term, $sign=null) {
+ $this->_terms[] = $term;
+
+ /**
+ * @todo This is not good. Sometimes $this->_signs is an array, sometimes
+ * it is null, even when there are terms. It will be changed so that
+ * it is always an array.
+ */
+ if ($this->_signs === null) {
+ if ($sign !== null) {
+ $this->_signs = array();
+ foreach ($this->_terms as $term) {
+ $this->_signs[] = null;
+ }
+ $this->_signs[] = $sign;
+ }
+ } else {
+ $this->_signs[] = $sign;
+ }
+ }
+
+
+ /**
+ * Returns query term
+ *
+ * @return array
+ */
+ public function getTerms()
+ {
+ return $this->_terms;
+ }
+
+
+ /**
+ * Return terms signs
+ *
+ * @return array
+ */
+ public function getSigns()
+ {
+ return $this->_signs;
+ }
+
+
+ /**
+ * Set weight for specified term
+ *
+ * @param integer $num
+ * @param Zend_Search_Lucene_Search_Weight_Term $weight
+ */
+ public function setWeight($num, $weight)
+ {
+ $this->_weights[$num] = $weight;
+ }
+
+
+ /**
+ * Constructs an appropriate Weight implementation for this query.
+ *
+ * @param Zend_Search_Lucene $reader
+ * @return Zend_Search_Lucene_Search_Weight
+ */
+ protected function _createWeight($reader)
+ {
+ return new Zend_Search_Lucene_Search_Weight_MultiTerm($this, $reader);
+ }
+
+
+ /**
+ * Calculate result vector for Conjunction query
+ * (like '+something +another')
+ *
+ * @param Zend_Search_Lucene $reader
+ */
+ private function _calculateConjunctionResult($reader)
+ {
+ if (extension_loaded('bitset')) {
+ foreach( $this->_terms as $termId=>$term ) {
+ if($this->_resVector === null) {
+ $this->_resVector = bitset_from_array($reader->termDocs($term));
+ } else {
+ $this->_resVector = bitset_intersection(
+ $this->_resVector,
+ bitset_from_array($reader->termDocs($term)) );
+ }
+
+ $this->_termsPositions[$termId] = $reader->termPositions($term);
+ }
+ } else {
+ foreach( $this->_terms as $termId=>$term ) {
+ if($this->_resVector === null) {
+ $this->_resVector = array_flip($reader->termDocs($term));
+ } else {
+ $termDocs = array_flip($reader->termDocs($term));
+ foreach($this->_resVector as $key=>$value) {
+ if (!isset( $termDocs[$key] )) {
+ unset( $this->_resVector[$key] );
+ }
+ }
+ }
+
+ $this->_termsPositions[$termId] = $reader->termPositions($term);
+ }
+ }
+ }
+
+
+ /**
+ * Calculate result vector for non Conjunction query
+ * (like '+something -another')
+ *
+ * @param Zend_Search_Lucene $reader
+ */
+ private function _calculateNonConjunctionResult($reader)
+ {
+ if (extension_loaded('bitset')) {
+ $required = null;
+ $neither = bitset_empty();
+ $prohibited = bitset_empty();
+
+ foreach ($this->_terms as $termId => $term) {
+ $termDocs = bitset_from_array($reader->termDocs($term));
+
+ if ($this->_signs[$termId] === true) {
+ // required
+ if ($required !== null) {
+ $required = bitset_intersection($required, $termDocs);
+ } else {
+ $required = $termDocs;
+ }
+ } elseif ($this->_signs[$termId] === false) {
+ // prohibited
+ $prohibited = bitset_union($prohibited, $termDocs);
+ } else {
+ // neither required, nor prohibited
+ $neither = bitset_union($neither, $termDocs);
+ }
+
+ $this->_termsPositions[$termId] = $reader->termPositions($term);
+ }
+
+ if ($required === null) {
+ $required = $neither;
+ }
+ $this->_resVector = bitset_intersection( $required,
+ bitset_invert($prohibited, $reader->count()) );
+ } else {
+ $required = null;
+ $neither = array();
+ $prohibited = array();
+
+ foreach ($this->_terms as $termId => $term) {
+ $termDocs = array_flip($reader->termDocs($term));
+
+ if ($this->_signs[$termId] === true) {
+ // required
+ if ($required !== null) {
+ // substitute for bitset_intersection
+ foreach ($required as $key => $value) {
+ if (!isset( $termDocs[$key] )) {
+ unset($required[$key]);
+ }
+ }
+ } else {
+ $required = $termDocs;
+ }
+ } elseif ($this->_signs[$termId] === false) {
+ // prohibited
+ // substitute for bitset_union
+ foreach ($termDocs as $key => $value) {
+ $prohibited[$key] = $value;
+ }
+ } else {
+ // neither required, nor prohibited
+ // substitute for bitset_union
+ foreach ($termDocs as $key => $value) {
+ $neither[$key] = $value;
+ }
+ }
+
+ $this->_termsPositions[$termId] = $reader->termPositions($term);
+ }
+
+ if ($required === null) {
+ $required = $neither;
+ }
+
+ foreach ($required as $key=>$value) {
+ if (isset( $prohibited[$key] )) {
+ unset($required[$key]);
+ }
+ }
+ $this->_resVector = $required;
+ }
+ }
+
+
+ /**
+ * Score calculator for conjunction queries (all terms are required)
+ *
+ * @param integer $docId
+ * @param Zend_Search_Lucene $reader
+ * @return float
+ */
+ public function _conjunctionScore($docId, $reader)
+ {
+ if ($this->_coord === null) {
+ $this->_coord = $reader->getSimilarity()->coord(count($this->_terms),
+ count($this->_terms) );
+ }
+
+ $score = 0.0;
+
+ foreach ($this->_terms as $termId=>$term) {
+ $score += $reader->getSimilarity()->tf(count($this->_termsPositions[$termId][$docId]) ) *
+ $this->_weights[$termId]->getValue() *
+ $reader->norm($docId, $term->field);
+ }
+
+ return $score * $this->_coord;
+ }
+
+
+ /**
+ * Score calculator for non conjunction queries (not all terms are required)
+ *
+ * @param integer $docId
+ * @param Zend_Search_Lucene $reader
+ * @return float
+ */
+ public function _nonConjunctionScore($docId, $reader)
+ {
+ if ($this->_coord === null) {
+ $this->_coord = array();
+
+ $maxCoord = 0;
+ foreach ($this->_signs as $sign) {
+ if ($sign !== false /* not prohibited */) {
+ $maxCoord++;
+ }
+ }
+
+ for ($count = 0; $count <= $maxCoord; $count++) {
+ $this->_coord[$count] = $reader->getSimilarity()->coord($count, $maxCoord);
+ }
+ }
+
+ $score = 0.0;
+ $matchedTerms = 0;
+ foreach ($this->_terms as $termId=>$term) {
+ // Check if term is
+ if ($this->_signs[$termId] !== false && // not prohibited
+ isset($this->_termsPositions[$termId][$docId]) // matched
+ ) {
+ $matchedTerms++;
+ $score +=
+ $reader->getSimilarity()->tf(count($this->_termsPositions[$termId][$docId]) ) *
+ $this->_weights[$termId]->getValue() *
+ $reader->norm($docId, $term->field);
+ }
+ }
+
+ return $score * $this->_coord[$matchedTerms];
+ }
+
+ /**
+ * Score specified document
+ *
+ * @param integer $docId
+ * @param Zend_Search_Lucene $reader
+ * @return float
+ */
+ public function score($docId, $reader)
+ {
+ if($this->_resVector === null) {
+ if ($this->_signs === null) {
+ $this->_calculateConjunctionResult($reader);
+ } else {
+ $this->_calculateNonConjunctionResult($reader);
+ }
+
+ $this->_initWeight($reader);
+ }
+
+ if ( (extension_loaded('bitset')) ?
+ bitset_in($this->_resVector, $docId) :
+ isset($this->_resVector[$docId]) ) {
+ if ($this->_signs === null) {
+ return $this->_conjunctionScore($docId, $reader);
+ } else {
+ return $this->_nonConjunctionScore($docId, $reader);
+ }
+ } else {
+ return 0;
+ }
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Phrase.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Phrase.php
new file mode 100644
index 00000000..3e52666b
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Phrase.php
@@ -0,0 +1,424 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * Zend_Search_Lucene_Search_Query
+ */
+require_once 'Zend/Search/Lucene/Search/Query.php';
+
+/**
+ * Zend_Search_Lucene_Search_Weight_MultiTerm
+ */
+require_once 'Zend/Search/Lucene/Search/Weight/Phrase.php';
+
+
+/**
+ * A Query that matches documents containing a particular sequence of terms.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_Query_Phrase extends Zend_Search_Lucene_Search_Query
+{
+ /**
+ * Terms to find.
+ * Array of Zend_Search_Lucene_Index_Term objects.
+ *
+ * @var array
+ */
+ private $_terms;
+
+ /**
+ * Term positions (relative positions of terms within the phrase).
+ * Array of integers
+ *
+ * @var array
+ */
+ private $_offsets;
+
+ /**
+ * Sets the number of other words permitted between words in query phrase.
+ * If zero, then this is an exact phrase search. For larger values this works
+ * like a WITHIN or NEAR operator.
+ *
+ * The slop is in fact an edit-distance, where the units correspond to
+ * moves of terms in the query phrase out of position. For example, to switch
+ * the order of two words requires two moves (the first move places the words
+ * atop one another), so to permit re-orderings of phrases, the slop must be
+ * at least two.
+ * More exact matches are scored higher than sloppier matches, thus search
+ * results are sorted by exactness.
+ *
+ * The slop is zero by default, requiring exact matches.
+ *
+ * @var unknown_type
+ */
+ private $_slop;
+
+ /**
+ * Result vector.
+ * Bitset or array of document IDs
+ * (depending from Bitset extension availability).
+ *
+ * @var mixed
+ */
+ private $_resVector = null;
+
+ /**
+ * Terms positions vectors.
+ * Array of Arrays:
+ * term1Id => (docId => array( pos1, pos2, ... ), ...)
+ * term2Id => (docId => array( pos1, pos2, ... ), ...)
+ *
+ * @var array
+ */
+ private $_termsPositions = array();
+
+ /**
+ * Class constructor. Create a new prase query.
+ *
+ * @param string $field Field to search.
+ * @param array $terms Terms to search Array of strings.
+ * @param array $offsets Relative term positions. Array of integers.
+ * @throws Zend_Search_Lucene_Exception
+ */
+ public function __construct($terms = null, $offsets = null, $field = null)
+ {
+ $this->_slop = 0;
+
+ if (is_array($terms)) {
+ $this->_terms = array();
+ foreach ($terms as $termId => $termText) {
+ $this->_terms[$termId] = ($field !== null)? new Zend_Search_Lucene_Index_Term($termText, $field):
+ new Zend_Search_Lucene_Index_Term($termText);
+ }
+ } else if ($terms === null) {
+ $this->_terms = array();
+ } else {
+ throw new Zend_Search_Lucene_Exception('terms argument must be array of strings or null');
+ }
+
+ if (is_array($offsets)) {
+ if (count($this->_terms) != count($offsets)) {
+ throw new Zend_Search_Lucene_Exception('terms and offsets arguments must have the same size.');
+ }
+ $this->_offsets = $offsets;
+ } else if ($offsets === null) {
+ $this->_offsets = array();
+ foreach ($this->_terms as $termId => $term) {
+ $position = count($this->_offsets);
+ $this->_offsets[$termId] = $position;
+ }
+ } else {
+ throw new Zend_Search_Lucene_Exception('offsets argument must be array of strings or null');
+ }
+ }
+
+ /**
+ * Set slop
+ *
+ * @param integer $slop
+ */
+ public function setSlop($slop)
+ {
+ $this->_slop = $slop;
+ }
+
+
+ /**
+ * Get slop
+ *
+ * @return integer
+ */
+ public function getSlop()
+ {
+ return $this->_slop;
+ }
+
+
+ /**
+ * Adds a term to the end of the query phrase.
+ * The relative position of the term is specified explicitly or the one immediately
+ * after the last term added.
+ *
+ * @param Zend_Search_Lucene_Index_Term $term
+ * @param integer $position
+ */
+ public function addTerm(Zend_Search_Lucene_Index_Term $term, $position = null) {
+ if ((count($this->_terms) != 0)&&(end($this->_terms)->field != $term->field)) {
+ throw new Zend_Search_Lucene_Exception('All phrase terms must be in the same field: ' .
+ $term->field . ':' . $term->text);
+ }
+
+ $this->_terms[] = $term;
+ if ($position !== null) {
+ $this->_offsets[] = $position;
+ } else if (count($this->_offsets) != 0) {
+ $this->_offsets[] = end($this->_offsets) + 1;
+ } else {
+ $this->_offsets[] = 0;
+ }
+ }
+
+
+ /**
+ * Returns query term
+ *
+ * @return array
+ */
+ public function getTerms()
+ {
+ return $this->_terms;
+ }
+
+
+ /**
+ * Set weight for specified term
+ *
+ * @param integer $num
+ * @param Zend_Search_Lucene_Search_Weight_Term $weight
+ */
+ public function setWeight($num, $weight)
+ {
+ $this->_weights[$num] = $weight;
+ }
+
+
+ /**
+ * Constructs an appropriate Weight implementation for this query.
+ *
+ * @param Zend_Search_Lucene $reader
+ * @return Zend_Search_Lucene_Search_Weight
+ */
+ protected function _createWeight($reader)
+ {
+ return new Zend_Search_Lucene_Search_Weight_Phrase($this, $reader);
+ }
+
+
+ /**
+ * Calculate result vector
+ *
+ * @param Zend_Search_Lucene $reader
+ */
+ private function _calculateResult($reader)
+ {
+ if (extension_loaded('bitset')) {
+ foreach( $this->_terms as $termId=>$term ) {
+ if($this->_resVector === null) {
+ $this->_resVector = bitset_from_array($reader->termDocs($term));
+ } else {
+ $this->_resVector = bitset_intersection(
+ $this->_resVector,
+ bitset_from_array($reader->termDocs($term)) );
+ }
+
+ $this->_termsPositions[$termId] = $reader->termPositions($term);
+ }
+ } else {
+ foreach( $this->_terms as $termId=>$term ) {
+ if($this->_resVector === null) {
+ $this->_resVector = array_flip($reader->termDocs($term));
+ } else {
+ $termDocs = array_flip($reader->termDocs($term));
+ foreach($this->_resVector as $key=>$value) {
+ if (!isset( $termDocs[$key] )) {
+ unset( $this->_resVector[$key] );
+ }
+ }
+ }
+
+ $this->_termsPositions[$termId] = $reader->termPositions($term);
+ }
+ }
+ }
+
+
+ /**
+ * Score calculator for exact phrase queries (terms sequence is fixed)
+ *
+ * @param integer $docId
+ * @return float
+ */
+ public function _exactPhraseFreq($docId)
+ {
+ $freq = 0;
+
+ // Term Id with lowest cardinality
+ $lowCardTermId = null;
+
+ // Calculate $lowCardTermId
+ foreach ($this->_terms as $termId => $term) {
+ if ($lowCardTermId === null ||
+ count($this->_termsPositions[$termId][$docId]) <
+ count($this->_termsPositions[$lowCardTermId][$docId]) ) {
+ $lowCardTermId = $termId;
+ }
+ }
+
+ // Walk through positions of the term with lowest cardinality
+ foreach ($this->_termsPositions[$lowCardTermId][$docId] as $lowCardPos) {
+ // We expect phrase to be found
+ $freq++;
+
+ // Walk through other terms
+ foreach ($this->_terms as $termId => $term) {
+ if ($termId != $lowCardTermId) {
+ $expectedPosition = $lowCardPos +
+ ($this->_offsets[$termId] -
+ $this->_offsets[$lowCardTermId]);
+
+ if (!in_array($expectedPosition, $this->_termsPositions[$termId][$docId])) {
+ $freq--; // Phrase wasn't found.
+ break;
+ }
+ }
+ }
+ }
+
+ return $freq;
+ }
+
+ /**
+ * Score calculator for sloppy phrase queries (terms sequence is fixed)
+ *
+ * @param integer $docId
+ * @param Zend_Search_Lucene $reader
+ * @return float
+ */
+ public function _sloppyPhraseFreq($docId, Zend_Search_Lucene $reader)
+ {
+ $freq = 0;
+
+ $phraseQueue = array();
+ $phraseQueue[0] = array(); // empty phrase
+ $lastTerm = null;
+
+ // Walk through the terms to create phrases.
+ foreach ($this->_terms as $termId => $term) {
+ $queueSize = count($phraseQueue);
+ $firstPass = true;
+
+ // Walk through the term positions.
+ // Each term position produces a set of phrases.
+ foreach ($this->_termsPositions[$termId][$docId] as $termPosition ) {
+ if ($firstPass) {
+ for ($count = 0; $count < $queueSize; $count++) {
+ $phraseQueue[$count][$termId] = $termPosition;
+ }
+ } else {
+ for ($count = 0; $count < $queueSize; $count++) {
+ if ($lastTerm !== null &&
+ abs( $termPosition - $phraseQueue[$count][$lastTerm] -
+ ($this->_offsets[$termId] - $this->_offsets[$lastTerm])) > $this->_slop) {
+ continue;
+ }
+
+ $newPhraseId = count($phraseQueue);
+ $phraseQueue[$newPhraseId] = $phraseQueue[$count];
+ $phraseQueue[$newPhraseId][$termId] = $termPosition;
+ }
+
+ }
+
+ $firstPass = false;
+ }
+ $lastTerm = $termId;
+ }
+
+
+ foreach ($phraseQueue as $phrasePos) {
+ $minDistance = null;
+
+ for ($shift = -$this->_slop; $shift <= $this->_slop; $shift++) {
+ $distance = 0;
+ $start = reset($phrasePos) - reset($this->_offsets) + $shift;
+
+ foreach ($this->_terms as $termId => $term) {
+ $distance += abs($phrasePos[$termId] - $this->_offsets[$termId] - $start);
+
+ if($distance > $this->_slop) {
+ break;
+ }
+ }
+
+ if ($minDistance === null || $distance < $minDistance) {
+ $minDistance = $distance;
+ }
+ }
+
+ if ($minDistance <= $this->_slop) {
+ $freq += $reader->getSimilarity()->sloppyFreq($minDistance);
+ }
+ }
+
+ return $freq;
+ }
+
+
+ /**
+ * Score specified document
+ *
+ * @param integer $docId
+ * @param Zend_Search_Lucene $reader
+ * @return float
+ */
+ public function score($docId, $reader)
+ {
+ // optimize zero-term case
+ if (count($this->_terms) == 0) {
+ return 0;
+ }
+
+ if($this->_resVector === null) {
+ $this->_calculateResult($reader);
+ $this->_initWeight($reader);
+ }
+
+ if ( (extension_loaded('bitset')) ?
+ bitset_in($this->_resVector, $docId) :
+ isset($this->_resVector[$docId]) ) {
+ if ($this->_slop == 0) {
+ $freq = $this->_exactPhraseFreq($docId);
+ } else {
+ $freq = $this->_sloppyPhraseFreq($docId, $reader);
+ }
+
+/*
+ return $reader->getSimilarity()->tf($freq) *
+ $this->_weight->getValue() *
+ $reader->norm($docId, reset($this->_terms)->field);
+*/
+ if ($freq != 0) {
+ $tf = $reader->getSimilarity()->tf($freq);
+ $weight = $this->_weight->getValue();
+ $norm = $reader->norm($docId, reset($this->_terms)->field);
+
+ return $tf*$weight*$norm;
+ }
+ } else {
+ return 0;
+ }
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Term.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Term.php
new file mode 100644
index 00000000..d622f845
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Query/Term.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Search_Query */
+require_once 'Zend/Search/Lucene/Search/Query.php';
+
+/** Zend_Search_Lucene_Search_Weight_Term */
+require_once 'Zend/Search/Lucene/Search/Weight/Term.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_Query_Term extends Zend_Search_Lucene_Search_Query
+{
+ /**
+ * Term to find.
+ *
+ * @var Zend_Search_Lucene_Index_Term
+ */
+ private $_term;
+
+ /**
+ * Term sign.
+ * If true then term is required
+ * If false then term is prohibited.
+ *
+ * @var bool
+ */
+ private $_sign;
+
+ /**
+ * Documents vector.
+ * Bitset or array of document IDs
+ * (depending from Bitset extension availability).
+ *
+ * @var mixed
+ */
+ private $_docVector = null;
+
+ /**
+ * Term positions vector.
+ * Array: docId => array( pos1, pos2, ... )
+ *
+ * @var array
+ */
+ private $_termPositions;
+
+
+ /**
+ * Zend_Search_Lucene_Search_Query_Term constructor
+ *
+ * @param Zend_Search_Lucene_Index_Term $term
+ * @param boolean $sign
+ */
+ public function __construct( $term, $sign = true )
+ {
+ $this->_term = $term;
+ $this->_sign = $sign;
+ }
+
+
+ /**
+ * Constructs an appropriate Weight implementation for this query.
+ *
+ * @param Zend_Search_Lucene $reader
+ * @return Zend_Search_Lucene_Search_Weight
+ */
+ protected function _createWeight($reader)
+ {
+ return new Zend_Search_Lucene_Search_Weight_Term($this->_term, $this, $reader);
+ }
+
+ /**
+ * Score specified document
+ *
+ * @param integer $docId
+ * @param Zend_Search_Lucene $reader
+ * @return float
+ */
+ public function score( $docId, $reader )
+ {
+ if($this->_docVector===null) {
+ if (extension_loaded('bitset')) {
+ $this->_docVector = bitset_from_array( $reader->termDocs($this->_term) );
+ } else {
+ $this->_docVector = array_flip($reader->termDocs($this->_term));
+ }
+
+ $this->_termPositions = $reader->termPositions($this->_term);
+ $this->_initWeight($reader);
+ }
+
+ $match = extension_loaded('bitset') ? bitset_in($this->_docVector, $docId) :
+ isset($this->_docVector[$docId]);
+ if ($this->_sign && $match) {
+ return $reader->getSimilarity()->tf(count($this->_termPositions[$docId]) ) *
+ $this->_weight->getValue() *
+ $reader->norm($docId, $this->_term->field);
+ } else {
+ return 0;
+ }
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryHit.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryHit.php
new file mode 100644
index 00000000..65290a9e
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryHit.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_QueryHit
+{
+ /**
+ * Object handle of the index
+ * @var Zend_Search_Lucene
+ */
+ protected $_index = null;
+
+ /**
+ * Object handle of the document associated with this hit
+ * @var Zend_Search_Lucene_Document
+ */
+ protected $_document = null;
+
+ /**
+ * Number of the document in the index
+ * @var integer
+ */
+ public $id;
+
+ /**
+ * Score of the hit
+ * @var float
+ */
+ public $score;
+
+
+ /**
+ * Constructor - pass object handle of Zend_Search_Lucene index that produced
+ * the hit so the document can be retrieved easily from the hit.
+ *
+ * @param Zend_Search_Lucene $index
+ */
+
+ public function __construct(Zend_Search_Lucene $index)
+ {
+ $this->_index = $index;
+ }
+
+
+ /**
+ * Convenience function for getting fields from the document
+ * associated with this hit.
+ *
+ * @param string $offset
+ * @return string
+ */
+ public function __get($offset)
+ {
+ return $this->getDocument()->getFieldValue($offset);
+ }
+
+
+ /**
+ * Return the document object for this hit
+ *
+ * @return Zend_Search_Lucene_Document
+ */
+ public function getDocument()
+ {
+ if (!$this->_document instanceof Zend_Search_Lucene_Document) {
+ $this->_document = $this->_index->getDocument($this->id);
+ }
+
+ return $this->_document;
+ }
+
+
+ /**
+ * Return the index object for this hit
+ *
+ * @return Zend_Search_Lucene
+ */
+ public function getIndex()
+ {
+ return $this->_index;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryParser.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryParser.php
new file mode 100644
index 00000000..9387afca
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryParser.php
@@ -0,0 +1,140 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Search_QueryTokenizer */
+require_once 'Zend/Search/Lucene/Search/QueryTokenizer.php';
+
+/** Zend_Search_Lucene_Index_Term */
+require_once 'Zend/Search/Lucene/Index/Term.php';
+
+/** Zend_Search_Lucene_Search_Query_Term */
+require_once 'Zend/Search/Lucene/Search/Query/Term.php';
+
+/** Zend_Search_Lucene_Search_Query_MultiTerm */
+require_once 'Zend/Search/Lucene/Search/Query/MultiTerm.php';
+
+/** Zend_Search_Lucene_Search_Query_Phrase */
+require_once 'Zend/Search/Lucene/Search/Query/Phrase.php';
+
+
+/** Zend_Search_Lucene_Exception */
+require_once 'Zend/Search/Lucene/Exception.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_QueryParser
+{
+
+ /**
+ * Parses a query string, returning a Zend_Search_Lucene_Search_Query
+ *
+ * @param string $strQuery
+ * @return Zend_Search_Lucene_Search_Query
+ */
+ static public function parse($strQuery)
+ {
+ $tokens = new Zend_Search_Lucene_Search_QueryTokenizer($strQuery);
+
+ // Empty query
+ if (!$tokens->count()) {
+ throw new Zend_Search_Lucene_Exception('Syntax error: query string cannot be empty.');
+ }
+
+ // Term query
+ if ($tokens->count() == 1) {
+ if ($tokens->current()->type == Zend_Search_Lucene_Search_QueryToken::TOKTYPE_WORD) {
+ return new Zend_Search_Lucene_Search_Query_Term(new Zend_Search_Lucene_Index_Term($tokens->current()->text, 'contents'));
+ } else {
+ throw new Zend_Search_Lucene_Exception('Syntax error: query string must contain at least one word.');
+ }
+ }
+
+
+ /**
+ * MultiTerm Query
+ *
+ * Process each token that was returned by the tokenizer.
+ */
+ $terms = array();
+ $signs = array();
+ $prevToken = null;
+ $openBrackets = 0;
+ $field = 'contents';
+ foreach ($tokens as $token) {
+ switch ($token->type) {
+ case Zend_Search_Lucene_Search_QueryToken::TOKTYPE_WORD:
+ $terms[] = new Zend_Search_Lucene_Index_Term($token->text, $field);
+ $field = 'contents';
+ if ($prevToken !== null &&
+ $prevToken->type == Zend_Search_Lucene_Search_QueryToken::TOKTYPE_SIGN) {
+ if ($prevToken->text == "+") {
+ $signs[] = true;
+ } else {
+ $signs[] = false;
+ }
+ } else {
+ $signs[] = null;
+ }
+ break;
+ case Zend_Search_Lucene_Search_QueryToken::TOKTYPE_SIGN:
+ if ($prevToken !== null &&
+ $prevToken->type == Zend_Search_Lucene_Search_QueryToken::TOKTYPE_SIGN) {
+ throw new Zend_Search_Lucene_Exception('Syntax error: sign operator must be followed by a word.');
+ }
+ break;
+ case Zend_Search_Lucene_Search_QueryToken::TOKTYPE_FIELD:
+ $field = $token->text;
+ // let previous token to be signed as next $prevToken
+ $token = $prevToken;
+ break;
+ case Zend_Search_Lucene_Search_QueryToken::TOKTYPE_BRACKET:
+ $token->text=='(' ? $openBrackets++ : $openBrackets--;
+ }
+ $prevToken = $token;
+ }
+
+ // Finish up parsing: check the last token in the query for an opening sign or parenthesis.
+ if ($prevToken->type == Zend_Search_Lucene_Search_QueryToken::TOKTYPE_SIGN) {
+ throw new Zend_Search_Lucene_Exception('Syntax Error: sign operator must be followed by a word.');
+ }
+
+ // Finish up parsing: check that every opening bracket has a matching closing bracket.
+ if ($openBrackets != 0) {
+ throw new Zend_Search_Lucene_Exception('Syntax Error: mismatched parentheses, every opening must have closing.');
+ }
+
+ switch (count($terms)) {
+ case 0:
+ throw new Zend_Search_Lucene_Exception('Syntax error: bad term count.');
+ case 1:
+ return new Zend_Search_Lucene_Search_Query_Term($terms[0],$signs[0] !== false);
+ default:
+ return new Zend_Search_Lucene_Search_Query_MultiTerm($terms,$signs);
+ }
+ }
+
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryToken.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryToken.php
new file mode 100644
index 00000000..995e0d3c
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryToken.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Exception */
+require_once 'Zend/Search/Lucene/Exception.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_QueryToken
+{
+ /**
+ * Token type Word.
+ */
+ const TOKTYPE_WORD = 0;
+
+ /**
+ * Token type Field.
+ * Field indicator in 'field:word' pair
+ */
+ const TOKTYPE_FIELD = 1;
+
+ /**
+ * Token type Sign.
+ * '+' (required) or '-' (absentee) sign
+ */
+ const TOKTYPE_SIGN = 2;
+
+ /**
+ * Token type Bracket.
+ * '(' or ')'
+ */
+ const TOKTYPE_BRACKET = 3;
+
+
+ /**
+ * Token type.
+ *
+ * @var integer
+ */
+ public $type;
+
+ /**
+ * Token text.
+ *
+ * @var integer
+ */
+ public $text;
+
+
+ /**
+ * IndexReader constructor needs token type and token text as a parameters.
+ *
+ * @param $tokType integer
+ * @param $tokText string
+ */
+ public function __construct($tokType, $tokText)
+ {
+ switch ($tokType) {
+ case self::TOKTYPE_BRACKET:
+ // fall through to the next case
+ case self::TOKTYPE_FIELD:
+ // fall through to the next case
+ case self::TOKTYPE_SIGN:
+ // fall through to the next case
+ case self::TOKTYPE_WORD:
+ break;
+ default:
+ throw new Zend_Search_Lucene_Exception("Unrecognized token type \"$tokType\".");
+ }
+
+ if (!strlen($tokText)) {
+ throw new Zend_Search_Lucene_Exception('Token text must be supplied.');
+ }
+
+ $this->type = $tokType;
+ $this->text = $tokText;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryTokenizer.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryTokenizer.php
new file mode 100644
index 00000000..986f8899
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/QueryTokenizer.php
@@ -0,0 +1,162 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Search_QueryToken */
+require_once 'Zend/Search/Lucene/Search/QueryToken.php';
+
+/** Zend_Search_Lucene_Exception */
+require_once 'Zend/Search/Lucene/Exception.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_QueryTokenizer implements Iterator
+{
+ /**
+ * inputString tokens.
+ *
+ * @var array
+ */
+ protected $_tokens = array();
+
+ /**
+ * tokens pointer.
+ *
+ * @var integer
+ */
+ protected $_currToken = 0;
+
+
+ /**
+ * QueryTokenize constructor needs query string as a parameter.
+ *
+ * @param string $inputString
+ */
+ public function __construct($inputString)
+ {
+ if (!strlen($inputString)) {
+ throw new Zend_Search_Lucene_Exception('Cannot tokenize empty query string.');
+ }
+
+ $currentToken = '';
+ for ($count = 0; $count < strlen($inputString); $count++) {
+ if (ctype_alnum( $inputString{$count} )) {
+ $currentToken .= $inputString{$count};
+ } else {
+ // Previous token is finished
+ if (strlen($currentToken)) {
+ $this->_tokens[] = new Zend_Search_Lucene_Search_QueryToken(Zend_Search_Lucene_Search_QueryToken::TOKTYPE_WORD,
+ $currentToken);
+ $currentToken = '';
+ }
+
+ if ($inputString{$count} == '+' || $inputString{$count} == '-') {
+ $this->_tokens[] = new Zend_Search_Lucene_Search_QueryToken(Zend_Search_Lucene_Search_QueryToken::TOKTYPE_SIGN,
+ $inputString{$count});
+ } elseif ($inputString{$count} == '(' || $inputString{$count} == ')') {
+ $this->_tokens[] = new Zend_Search_Lucene_Search_QueryToken(Zend_Search_Lucene_Search_QueryToken::TOKTYPE_BRACKET,
+ $inputString{$count});
+ } elseif ($inputString{$count} == ':' && $this->count()) {
+ if ($this->_tokens[count($this->_tokens)-1]->type == Zend_Search_Lucene_Search_QueryToken::TOKTYPE_WORD) {
+ $this->_tokens[count($this->_tokens)-1]->type = Zend_Search_Lucene_Search_QueryToken::TOKTYPE_FIELD;
+ }
+ }
+ }
+ }
+
+ if (strlen($currentToken)) {
+ $this->_tokens[] = new Zend_Search_Lucene_Search_QueryToken(Zend_Search_Lucene_Search_QueryToken::TOKTYPE_WORD, $currentToken);
+ }
+ }
+
+
+ /**
+ * Returns number of tokens
+ *
+ * @return integer
+ */
+ public function count()
+ {
+ return count($this->_tokens);
+ }
+
+
+ /**
+ * Returns TRUE if a token exists at the current position.
+ *
+ * @return boolean
+ */
+ public function valid()
+ {
+ return $this->_currToken < $this->count();
+ }
+
+
+ /**
+ * Resets token stream.
+ *
+ * @return integer
+ */
+ public function rewind()
+ {
+ $this->_currToken = 0;
+ }
+
+
+ /**
+ * Returns the token at the current position or FALSE if
+ * the position does not contain a valid token.
+ *
+ * @return mixed
+ */
+ public function current()
+ {
+ return $this->valid() ? $this->_tokens[$this->_currToken] : false;
+ }
+
+
+ /**
+ * Returns next token
+ *
+ * @return Zend_Search_Lucene_Search_QueryToken
+ */
+ public function next()
+ {
+ return ++$this->_currToken;
+ }
+
+
+ /**
+ * Return the position of the current token.
+ *
+ * @return integer
+ */
+ public function key()
+ {
+ return $this->_currToken;
+ }
+
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity.php
new file mode 100644
index 00000000..8b758213
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity.php
@@ -0,0 +1,551 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Search_Similarity_Default */
+require_once 'Zend/Search/Lucene/Search/Similarity/Default.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+abstract class Zend_Search_Lucene_Search_Similarity
+{
+ /**
+ * The Similarity implementation used by default.
+ *
+ * @var Zend_Search_Lucene_Search_Similarity
+ */
+ static private $_defaultImpl;
+
+ /**
+ * Cache of decoded bytes.
+ * Array of floats
+ *
+ * @var array
+ */
+ static private $_normTable = array( 0 => 0.0,
+ 1 => 5.820766E-10,
+ 2 => 6.9849193E-10,
+ 3 => 8.1490725E-10,
+ 4 => 9.313226E-10,
+ 5 => 1.1641532E-9,
+ 6 => 1.3969839E-9,
+ 7 => 1.6298145E-9,
+ 8 => 1.8626451E-9,
+ 9 => 2.3283064E-9,
+ 10 => 2.7939677E-9,
+ 11 => 3.259629E-9,
+ 12 => 3.7252903E-9,
+ 13 => 4.656613E-9,
+ 14 => 5.5879354E-9,
+ 15 => 6.519258E-9,
+ 16 => 7.4505806E-9,
+ 17 => 9.313226E-9,
+ 18 => 1.1175871E-8,
+ 19 => 1.3038516E-8,
+ 20 => 1.4901161E-8,
+ 21 => 1.8626451E-8,
+ 22 => 2.2351742E-8,
+ 23 => 2.6077032E-8,
+ 24 => 2.9802322E-8,
+ 25 => 3.7252903E-8,
+ 26 => 4.4703484E-8,
+ 27 => 5.2154064E-8,
+ 28 => 5.9604645E-8,
+ 29 => 7.4505806E-8,
+ 30 => 8.940697E-8,
+ 31 => 1.0430813E-7,
+ 32 => 1.1920929E-7,
+ 33 => 1.4901161E-7,
+ 34 => 1.7881393E-7,
+ 35 => 2.0861626E-7,
+ 36 => 2.3841858E-7,
+ 37 => 2.9802322E-7,
+ 38 => 3.5762787E-7,
+ 39 => 4.172325E-7,
+ 40 => 4.7683716E-7,
+ 41 => 5.9604645E-7,
+ 42 => 7.1525574E-7,
+ 43 => 8.34465E-7,
+ 44 => 9.536743E-7,
+ 45 => 1.1920929E-6,
+ 46 => 1.4305115E-6,
+ 47 => 1.66893E-6,
+ 48 => 1.9073486E-6,
+ 49 => 2.3841858E-6,
+ 50 => 2.861023E-6,
+ 51 => 3.33786E-6,
+ 52 => 3.8146973E-6,
+ 53 => 4.7683716E-6,
+ 54 => 5.722046E-6,
+ 55 => 6.67572E-6,
+ 56 => 7.6293945E-6,
+ 57 => 9.536743E-6,
+ 58 => 1.1444092E-5,
+ 59 => 1.335144E-5,
+ 60 => 1.5258789E-5,
+ 61 => 1.9073486E-5,
+ 62 => 2.2888184E-5,
+ 63 => 2.670288E-5,
+ 64 => 3.0517578E-5,
+ 65 => 3.8146973E-5,
+ 66 => 4.5776367E-5,
+ 67 => 5.340576E-5,
+ 68 => 6.1035156E-5,
+ 69 => 7.6293945E-5,
+ 70 => 9.1552734E-5,
+ 71 => 1.0681152E-4,
+ 72 => 1.2207031E-4,
+ 73 => 1.5258789E-4,
+ 74 => 1.8310547E-4,
+ 75 => 2.1362305E-4,
+ 76 => 2.4414062E-4,
+ 77 => 3.0517578E-4,
+ 78 => 3.6621094E-4,
+ 79 => 4.272461E-4,
+ 80 => 4.8828125E-4,
+ 81 => 6.1035156E-4,
+ 82 => 7.324219E-4,
+ 83 => 8.544922E-4,
+ 84 => 9.765625E-4,
+ 85 => 0.0012207031,
+ 86 => 0.0014648438,
+ 87 => 0.0017089844,
+ 88 => 0.001953125,
+ 89 => 0.0024414062,
+ 90 => 0.0029296875,
+ 91 => 0.0034179688,
+ 92 => 0.00390625,
+ 93 => 0.0048828125,
+ 94 => 0.005859375,
+ 95 => 0.0068359375,
+ 96 => 0.0078125,
+ 97 => 0.009765625,
+ 98 => 0.01171875,
+ 99 => 0.013671875,
+ 100 => 0.015625,
+ 101 => 0.01953125,
+ 102 => 0.0234375,
+ 103 => 0.02734375,
+ 104 => 0.03125,
+ 105 => 0.0390625,
+ 106 => 0.046875,
+ 107 => 0.0546875,
+ 108 => 0.0625,
+ 109 => 0.078125,
+ 110 => 0.09375,
+ 111 => 0.109375,
+ 112 => 0.125,
+ 113 => 0.15625,
+ 114 => 0.1875,
+ 115 => 0.21875,
+ 116 => 0.25,
+ 117 => 0.3125,
+ 118 => 0.375,
+ 119 => 0.4375,
+ 120 => 0.5,
+ 121 => 0.625,
+ 122 => 0.75,
+ 123 => 0.875,
+ 124 => 1.0,
+ 125 => 1.25,
+ 126 => 1.5,
+ 127 => 1.75,
+ 128 => 2.0,
+ 129 => 2.5,
+ 130 => 3.0,
+ 131 => 3.5,
+ 132 => 4.0,
+ 133 => 5.0,
+ 134 => 6.0,
+ 135 => 7.0,
+ 136 => 8.0,
+ 137 => 10.0,
+ 138 => 12.0,
+ 139 => 14.0,
+ 140 => 16.0,
+ 141 => 20.0,
+ 142 => 24.0,
+ 143 => 28.0,
+ 144 => 32.0,
+ 145 => 40.0,
+ 146 => 48.0,
+ 147 => 56.0,
+ 148 => 64.0,
+ 149 => 80.0,
+ 150 => 96.0,
+ 151 => 112.0,
+ 152 => 128.0,
+ 153 => 160.0,
+ 154 => 192.0,
+ 155 => 224.0,
+ 156 => 256.0,
+ 157 => 320.0,
+ 158 => 384.0,
+ 159 => 448.0,
+ 160 => 512.0,
+ 161 => 640.0,
+ 162 => 768.0,
+ 163 => 896.0,
+ 164 => 1024.0,
+ 165 => 1280.0,
+ 166 => 1536.0,
+ 167 => 1792.0,
+ 168 => 2048.0,
+ 169 => 2560.0,
+ 170 => 3072.0,
+ 171 => 3584.0,
+ 172 => 4096.0,
+ 173 => 5120.0,
+ 174 => 6144.0,
+ 175 => 7168.0,
+ 176 => 8192.0,
+ 177 => 10240.0,
+ 178 => 12288.0,
+ 179 => 14336.0,
+ 180 => 16384.0,
+ 181 => 20480.0,
+ 182 => 24576.0,
+ 183 => 28672.0,
+ 184 => 32768.0,
+ 185 => 40960.0,
+ 186 => 49152.0,
+ 187 => 57344.0,
+ 188 => 65536.0,
+ 189 => 81920.0,
+ 190 => 98304.0,
+ 191 => 114688.0,
+ 192 => 131072.0,
+ 193 => 163840.0,
+ 194 => 196608.0,
+ 195 => 229376.0,
+ 196 => 262144.0,
+ 197 => 327680.0,
+ 198 => 393216.0,
+ 199 => 458752.0,
+ 200 => 524288.0,
+ 201 => 655360.0,
+ 202 => 786432.0,
+ 203 => 917504.0,
+ 204 => 1048576.0,
+ 205 => 1310720.0,
+ 206 => 1572864.0,
+ 207 => 1835008.0,
+ 208 => 2097152.0,
+ 209 => 2621440.0,
+ 210 => 3145728.0,
+ 211 => 3670016.0,
+ 212 => 4194304.0,
+ 213 => 5242880.0,
+ 214 => 6291456.0,
+ 215 => 7340032.0,
+ 216 => 8388608.0,
+ 217 => 1.048576E7,
+ 218 => 1.2582912E7,
+ 219 => 1.4680064E7,
+ 220 => 1.6777216E7,
+ 221 => 2.097152E7,
+ 222 => 2.5165824E7,
+ 223 => 2.9360128E7,
+ 224 => 3.3554432E7,
+ 225 => 4.194304E7,
+ 226 => 5.0331648E7,
+ 227 => 5.8720256E7,
+ 228 => 6.7108864E7,
+ 229 => 8.388608E7,
+ 230 => 1.00663296E8,
+ 231 => 1.17440512E8,
+ 232 => 1.34217728E8,
+ 233 => 1.6777216E8,
+ 234 => 2.01326592E8,
+ 235 => 2.34881024E8,
+ 236 => 2.68435456E8,
+ 237 => 3.3554432E8,
+ 238 => 4.02653184E8,
+ 239 => 4.69762048E8,
+ 240 => 5.3687091E8,
+ 241 => 6.7108864E8,
+ 242 => 8.0530637E8,
+ 243 => 9.395241E8,
+ 244 => 1.07374182E9,
+ 245 => 1.34217728E9,
+ 246 => 1.61061274E9,
+ 247 => 1.87904819E9,
+ 248 => 2.14748365E9,
+ 249 => 2.68435456E9,
+ 250 => 3.22122547E9,
+ 251 => 3.75809638E9,
+ 252 => 4.2949673E9,
+ 253 => 5.3687091E9,
+ 254 => 6.4424509E9,
+ 255 => 7.5161928E9 );
+
+
+ /**
+ * Set the default Similarity implementation used by indexing and search
+ * code.
+ *
+ * @param Zend_Search_Lucene_Search_Similarity $similarity
+ */
+ static public function setDefault(Zend_Search_Lucene_Search_Similarity $similarity)
+ {
+ self::$_defaultImpl = $similarity;
+ }
+
+
+ /**
+ * Return the default Similarity implementation used by indexing and search
+ * code.
+ *
+ * @return Zend_Search_Lucene_Search_Similarity
+ */
+ static public function getDefault()
+ {
+ if (!self::$_defaultImpl instanceof Zend_Search_Lucene_Search_Similarity) {
+ self::$_defaultImpl = new Zend_Search_Lucene_Search_Similarity_Default();
+ }
+
+ return self::$_defaultImpl;
+ }
+
+
+ /**
+ * Computes the normalization value for a field given the total number of
+ * terms contained in a field. These values, together with field boosts, are
+ * stored in an index and multipled into scores for hits on each field by the
+ * search code.
+ *
+ * Matches in longer fields are less precise, so implemenations of this
+ * method usually return smaller values when 'numTokens' is large,
+ * and larger values when 'numTokens' is small.
+ *
+ * That these values are computed under
+ * IndexWriter::addDocument(Document) and stored then using
+ * encodeNorm(float). Thus they have limited precision, and documents
+ * must be re-indexed if this method is altered.
+ *
+ * fieldName - name of field
+ * numTokens - the total number of tokens contained in fields named
+ * 'fieldName' of 'doc'.
+ * Returns a normalization factor for hits on this field of this document
+ *
+ * @param string $fieldName
+ * @param integer $numTokens
+ * @return float
+ */
+ abstract public function lengthNorm($fieldName, $numTokens);
+
+ /**
+ * Computes the normalization value for a query given the sum of the squared
+ * weights of each of the query terms. This value is then multipled into the
+ * weight of each query term.
+ *
+ * This does not affect ranking, but rather just attempts to make scores
+ * from different queries comparable.
+ *
+ * sumOfSquaredWeights - the sum of the squares of query term weights
+ * Returns a normalization factor for query weights
+ *
+ * @param float $sumOfSquaredWeights
+ * @return float
+ */
+ abstract public function queryNorm($sumOfSquaredWeights);
+
+
+ /**
+ * Decodes a normalization factor stored in an index.
+ *
+ * @param integer $byte
+ * @return float
+ */
+ static public function decodeNorm($byte)
+ {
+ return self::$_normTable[$byte & 0xFF];
+ }
+
+
+ /**
+ * Encodes a normalization factor for storage in an index.
+ *
+ * The encoding uses a five-bit exponent and three-bit mantissa, thus
+ * representing values from around 7x10^9 to 2x10^-9 with about one
+ * significant decimal digit of accuracy. Zero is also represented.
+ * Negative numbers are rounded up to zero. Values too large to represent
+ * are rounded down to the largest representable value. Positive values too
+ * small to represent are rounded up to the smallest positive representable
+ * value.
+ *
+ * @param float $f
+ * @return integer
+ */
+ static function encodeNorm($f)
+ {
+ return self::_floatToByte($f);
+ }
+
+ /**
+ * Float to byte conversion
+ *
+ * @param integer $b
+ * @return float
+ */
+ static private function _floatToByte($f)
+ {
+ // round negatives up to zero
+ if ($f <= 0.0) {
+ return 0;
+ }
+
+ // search for appropriate value
+ $lowIndex = 0;
+ $highIndex = 255;
+ while ($highIndex >= $lowIndex) {
+ // $mid = ($highIndex - $lowIndex)/2;
+ $mid = ($highIndex + $lowIndex) >> 1;
+ $delta = $f - self::$_normTable[$mid];
+
+ if ($delta < 0) {
+ $highIndex = $mid-1;
+ } elseif ($delta > 0) {
+ $lowIndex = $mid+1;
+ } else {
+ return $mid; // We got it!
+ }
+ }
+
+ // round to closest value
+ if ($highIndex != 255 &&
+ $f - self::$_normTable[$highIndex] > self::$_normTable[$highIndex+1] - $f ) {
+ return $highIndex + 1;
+ } else {
+ return $highIndex;
+ }
+ }
+
+
+ /**
+ * Computes a score factor based on a term or phrase's frequency in a
+ * document. This value is multiplied by the idf(Term, Searcher)
+ * factor for each term in the query and these products are then summed to
+ * form the initial score for a document.
+ *
+ * Terms and phrases repeated in a document indicate the topic of the
+ * document, so implementations of this method usually return larger values
+ * when 'freq' is large, and smaller values when 'freq'
+ * is small.
+ *
+ * freq - the frequency of a term within a document
+ * Returns a score factor based on a term's within-document frequency
+ *
+ * @param float $freq
+ * @return float
+ */
+ abstract public function tf($freq);
+
+ /**
+ * Computes the amount of a sloppy phrase match, based on an edit distance.
+ * This value is summed for each sloppy phrase match in a document to form
+ * the frequency that is passed to tf(float).
+ *
+ * A phrase match with a small edit distance to a document passage more
+ * closely matches the document, so implementations of this method usually
+ * return larger values when the edit distance is small and smaller values
+ * when it is large.
+ *
+ * distance - the edit distance of this sloppy phrase match
+ * Returns the frequency increment for this match
+ *
+ * @param integer $distance
+ * @return float
+ */
+ abstract public function sloppyFreq($distance);
+
+
+ /**
+ * Computes a score factor for a simple term or a phrase.
+ *
+ * The default implementation is:
+ * return idfFreq(searcher.docFreq(term), searcher.maxDoc());
+ *
+ * input - the term in question or array of terms
+ * reader - reader the document collection being searched
+ * Returns a score factor for the term
+ *
+ * @param mixed $input
+ * @param Zend_Search_Lucene $reader
+ * @return a score factor for the term
+ */
+ public function idf($input, $reader)
+ {
+ if (!is_array($input)) {
+ return $this->idfFreq($reader->docFreq($input), $reader->count());
+ } else {
+ $idf = 0.0;
+ foreach ($input as $term) {
+ $idf += $this->idfFreq($reader->docFreq($term), $reader->count());
+ }
+ return $idf;
+ }
+ }
+
+ /**
+ * Computes a score factor based on a term's document frequency (the number
+ * of documents which contain the term). This value is multiplied by the
+ * tf(int) factor for each term in the query and these products are
+ * then summed to form the initial score for a document.
+ *
+ * Terms that occur in fewer documents are better indicators of topic, so
+ * implemenations of this method usually return larger values for rare terms,
+ * and smaller values for common terms.
+ *
+ * docFreq - the number of documents which contain the term
+ * numDocs - the total number of documents in the collection
+ * Returns a score factor based on the term's document frequency
+ *
+ * @param integer $docFreq
+ * @param integer $numDocs
+ * @return float
+ */
+ abstract public function idfFreq($docFreq, $numDocs);
+
+ /**
+ * Computes a score factor based on the fraction of all query terms that a
+ * document contains. This value is multiplied into scores.
+ *
+ * The presence of a large portion of the query terms indicates a better
+ * match with the query, so implemenations of this method usually return
+ * larger values when the ratio between these parameters is large and smaller
+ * values when the ratio between them is small.
+ *
+ * overlap - the number of query terms matched in the document
+ * maxOverlap - the total number of terms in the query
+ * Returns a score factor based on term overlap with the query
+ *
+ * @param integer $overlap
+ * @param integer $maxOverlap
+ * @return float
+ */
+ abstract public function coord($overlap, $maxOverlap);
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity/Default.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity/Default.php
new file mode 100644
index 00000000..1551d8bd
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Similarity/Default.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_Similarity_Default extends Zend_Search_Lucene_Search_Similarity
+{
+
+ /**
+ * Implemented as '1/sqrt(numTerms)'.
+ *
+ * @param string $fieldName
+ * @param integer numTerms
+ * @return float
+ */
+ public function lengthNorm($fieldName, $numTerms)
+ {
+ return 1.0/sqrt($numTerms);
+ }
+
+ /**
+ * Implemented as '1/sqrt(sumOfSquaredWeights)'.
+ *
+ * @param float $sumOfSquaredWeights
+ * @return float
+ */
+ public function queryNorm($sumOfSquaredWeights)
+ {
+ return 1.0/sqrt($sumOfSquaredWeights);
+ }
+
+ /**
+ * Implemented as 'sqrt(freq)'.
+ *
+ * @param float $freq
+ * @return float
+ */
+ public function tf($freq)
+ {
+ return sqrt($freq);
+ }
+
+ /**
+ * Implemented as '1/(distance + 1)'.
+ *
+ * @param integer $distance
+ * @return float
+ */
+ public function sloppyFreq($distance)
+ {
+ return 1.0/($distance + 1);
+ }
+
+ /**
+ * Implemented as 'log(numDocs/(docFreq+1)) + 1'.
+ *
+ * @param integer $docFreq
+ * @param integer $numDocs
+ * @return float
+ */
+ public function idfFreq($docFreq, $numDocs)
+ {
+ return log($numDocs/(float)($docFreq+1)) + 1.0;
+ }
+
+ /**
+ * Implemented as 'overlap/maxOverlap'.
+ *
+ * @param integer $overlap
+ * @param integer $maxOverlap
+ * @return float
+ */
+ public function coord($overlap, $maxOverlap)
+ {
+ return $overlap/(float)$maxOverlap;
+ }
+}
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight.php
new file mode 100644
index 00000000..2d5b7a72
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * Calculate query weights and build query scorers.
+ *
+ * A Weight is constructed by a query Query->createWeight().
+ * The sumOfSquaredWeights() method is then called on the top-level
+ * query to compute the query normalization factor Similarity->queryNorm(float).
+ * This factor is then passed to normalize(float). At this point the weighting
+ * is complete.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+abstract class Zend_Search_Lucene_Search_Weight
+{
+ /**
+ * The weight for this query.
+ *
+ * @return float
+ */
+ abstract public function getValue();
+
+ /**
+ * The sum of squared weights of contained query clauses.
+ *
+ * @return float
+ */
+ abstract public function sumOfSquaredWeights();
+
+ /**
+ * Assigns the query normalization factor to this.
+ *
+ * @param $norm
+ */
+ abstract public function normalize($norm);
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/MultiTerm.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/MultiTerm.php
new file mode 100644
index 00000000..69528ba4
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/MultiTerm.php
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Search_Weight */
+require_once 'Zend/Search/Lucene/Search/Weight.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_Weight_MultiTerm extends Zend_Search_Lucene_Search_Weight
+{
+ /**
+ * IndexReader.
+ *
+ * @var Zend_Search_Lucene
+ */
+ private $_reader;
+
+ /**
+ * The query that this concerns.
+ *
+ * @var Zend_Search_Lucene_Search_Query_MultiTerm
+ */
+ private $_query;
+
+ /**
+ * Query terms weights
+ * Array of Zend_Search_Lucene_Search_Weight_Term
+ *
+ * @var array
+ */
+ private $_weights;
+
+
+ /**
+ * Zend_Search_Lucene_Search_Weight_MultiTerm constructor
+ * query - the query that this concerns.
+ * reader - index reader
+ *
+ * @param Zend_Search_Lucene_Search_Query_MultiTerm $query
+ * @param Zend_Search_Lucene $reader
+ */
+ public function __construct($query, $reader)
+ {
+ $this->_query = $query;
+ $this->_reader = $reader;
+ $this->_weights = array();
+
+ $signs = $query->getSigns();
+
+ foreach ($query->getTerms() as $num => $term) {
+ if ($signs === null || $signs[$num] === null || $signs[$num]) {
+ $this->_weights[$num] = new Zend_Search_Lucene_Search_Weight_Term($term, $query, $reader);
+ $query->setWeight($num, $this->_weights[$num]);
+ }
+ }
+ }
+
+
+ /**
+ * The weight for this query
+ *
+ * @return float
+ */
+ public function getValue()
+ {
+ return $this->_query->getBoost();
+ }
+
+
+ /**
+ * The sum of squared weights of contained query clauses.
+ *
+ * @return float
+ */
+ public function sumOfSquaredWeights()
+ {
+ $sum = 0;
+ foreach ($this->_weights as $weight) {
+ // sum sub weights
+ $sum += $weight->sumOfSquaredWeights();
+ }
+
+ // boost each sub-weight
+ $sum *= $this->_query->getBoost() * $this->_query->getBoost();
+
+ // check for empty query (like '-something -another')
+ if ($sum == 0) {
+ $sum = 1.0;
+ }
+ return $sum;
+ }
+
+
+ /**
+ * Assigns the query normalization factor to this.
+ *
+ * @param float $queryNorm
+ */
+ public function normalize($queryNorm)
+ {
+ // incorporate boost
+ $queryNorm *= $this->_query->getBoost();
+
+ foreach ($this->_weights as $weight) {
+ $weight->normalize($queryNorm);
+ }
+ }
+}
+
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Phrase.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Phrase.php
new file mode 100644
index 00000000..77e94f28
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Phrase.php
@@ -0,0 +1,138 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * Zend_Search_Lucene_Search_Weight
+ */
+require_once 'Zend/Search/Lucene/Search/Weight.php';
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_Weight_Phrase extends Zend_Search_Lucene_Search_Weight
+{
+ /**
+ * IndexReader.
+ *
+ * @var Zend_Search_Lucene
+ */
+ private $_reader;
+
+ /**
+ * The query that this concerns.
+ *
+ * @var Zend_Search_Lucene_Search_Query_Phrase
+ */
+ private $_query;
+
+ /**
+ * Weight value
+ *
+ * @var float
+ */
+ private $_value;
+
+ /**
+ * Score factor
+ *
+ * @var float
+ */
+ private $_idf;
+
+ /**
+ * Normalization factor
+ *
+ * @var float
+ */
+ private $_queryNorm;
+
+
+ /**
+ * Query weight
+ *
+ * @var float
+ */
+ private $_queryWeight;
+
+
+ /**
+ * Zend_Search_Lucene_Search_Weight_Phrase constructor
+ *
+ * @param Zend_Search_Lucene_Search_Query_Phrase $query
+ * @param Zend_Search_Lucene $reader
+ */
+ public function __construct(Zend_Search_Lucene_Search_Query_Phrase $query, Zend_Search_Lucene $reader)
+ {
+ $this->_query = $query;
+ $this->_reader = $reader;
+ }
+
+
+ /**
+ * The weight for this query
+ *
+ * @return float
+ */
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+
+ /**
+ * The sum of squared weights of contained query clauses.
+ *
+ * @return float
+ */
+ public function sumOfSquaredWeights()
+ {
+ // compute idf
+ $this->_idf = $this->_reader->getSimilarity()->idf($this->_query->getTerms(), $this->_reader);
+
+ // compute query weight
+ $this->_queryWeight = $this->_idf * $this->_query->getBoost();
+
+ // square it
+ return $this->_queryWeight * $this->_queryWeight;
+ }
+
+
+ /**
+ * Assigns the query normalization factor to this.
+ *
+ * @param float $queryNorm
+ */
+ public function normalize($queryNorm)
+ {
+ $this->_queryNorm = $queryNorm;
+
+ // normalize query weight
+ $this->_queryWeight *= $queryNorm;
+
+ // idf for documents
+ $this->_value = $this->_queryWeight * $this->_idf;
+ }
+}
+
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Term.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Term.php
new file mode 100644
index 00000000..3e6102f3
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Search/Weight/Term.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Search_Weight */
+require_once 'Zend/Search/Lucene/Search/Weight.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Search_Weight_Term extends Zend_Search_Lucene_Search_Weight
+{
+ /**
+ * IndexReader.
+ *
+ * @var Zend_Search_Lucene
+ */
+ private $_reader;
+
+ /**
+ * Term
+ *
+ * @var Zend_Search_Lucene_Index_Term
+ */
+ private $_term;
+
+ /**
+ * The query that this concerns.
+ *
+ * @var Zend_Search_Lucene_Search_Query
+ */
+ private $_query;
+
+ /**
+ * Weight value
+ *
+ * @var float
+ */
+ private $_value;
+
+ /**
+ * Score factor
+ *
+ * @var float
+ */
+ private $_idf;
+
+ /**
+ * Normalization factor
+ *
+ * @var float
+ */
+ private $_queryNorm;
+
+
+ /**
+ * Query weight
+ *
+ * @var float
+ */
+ private $_queryWeight;
+
+
+ /**
+ * Zend_Search_Lucene_Search_Weight_Term constructor
+ * reader - index reader
+ *
+ * @param Zend_Search_Lucene $reader
+ */
+ public function __construct($term, $query, $reader)
+ {
+ $this->_term = $term;
+ $this->_query = $query;
+ $this->_reader = $reader;
+ }
+
+
+ /**
+ * The weight for this query
+ *
+ * @return float
+ */
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+
+ /**
+ * The sum of squared weights of contained query clauses.
+ *
+ * @return float
+ */
+ public function sumOfSquaredWeights()
+ {
+ // compute idf
+ $this->_idf = $this->_reader->getSimilarity()->idf($this->_term, $this->_reader);
+
+ // compute query weight
+ $this->_queryWeight = $this->_idf * $this->_query->getBoost();
+
+ // square it
+ return $this->_queryWeight * $this->_queryWeight;
+ }
+
+
+ /**
+ * Assigns the query normalization factor to this.
+ *
+ * @param float $queryNorm
+ */
+ public function normalize($queryNorm)
+ {
+ $this->_queryNorm = $queryNorm;
+
+ // normalize query weight
+ $this->_queryWeight *= $queryNorm;
+
+ // idf for documents
+ $this->_value = $this->_queryWeight * $this->_idf;
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory.php
new file mode 100644
index 00000000..48114a76
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory.php
@@ -0,0 +1,118 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+abstract class Zend_Search_Lucene_Storage_Directory
+{
+
+ /**
+ * Closes the store.
+ *
+ * @return void
+ */
+ abstract public function close();
+
+ /**
+ * Returns an array of strings, one for each file in the directory.
+ *
+ * @return array
+ */
+ abstract public function fileList();
+
+ /**
+ * Creates a new, empty file in the directory with the given $filename.
+ *
+ * @param string $filename
+ * @return Zend_Search_Lucene_Storage_File
+ */
+ abstract public function createFile($filename);
+
+
+ /**
+ * Removes an existing $filename in the directory.
+ *
+ * @param string $filename
+ * @return void
+ */
+ abstract public function deleteFile($filename);
+
+
+ /**
+ * Returns true if a file with the given $filename exists.
+ *
+ * @param string $filename
+ * @return boolean
+ */
+ abstract public function fileExists($filename);
+
+
+ /**
+ * Returns the length of a $filename in the directory.
+ *
+ * @param string $filename
+ * @return integer
+ */
+ abstract public function fileLength($filename);
+
+
+ /**
+ * Returns the UNIX timestamp $filename was last modified.
+ *
+ * @param string $filename
+ * @return integer
+ */
+ abstract public function fileModified($filename);
+
+
+ /**
+ * Renames an existing file in the directory.
+ *
+ * @param string $from
+ * @param string $to
+ * @return void
+ */
+ abstract public function renameFile($from, $to);
+
+
+ /**
+ * Sets the modified time of $filename to now.
+ *
+ * @param string $filename
+ * @return void
+ */
+ abstract public function touchFile($filename);
+
+
+ /**
+ * Returns a Zend_Search_Lucene_Storage_File object for a given $filename in the directory.
+ *
+ * @param string $filename
+ * @return Zend_Search_Lucene_Storage_File
+ */
+ abstract public function getFileObject($filename);
+
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory/Filesystem.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory/Filesystem.php
new file mode 100644
index 00000000..73d10659
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/Directory/Filesystem.php
@@ -0,0 +1,269 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Storage_Directory */
+require_once 'Zend/Search/Lucene/Storage/Directory.php';
+
+/** Zend_Search_Lucene_Storage_File_Filesystem */
+require_once 'Zend/Search/Lucene/Storage/File/Filesystem.php';
+
+
+/**
+ * FileSystem implementation of Directory abstraction.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+class Zend_Search_Lucene_Storage_Directory_Filesystem extends Zend_Search_Lucene_Storage_Directory
+{
+ /**
+ * Filesystem path to the directory
+ *
+ * @var string
+ */
+ private $_dirPath = null;
+
+ /**
+ * Cache for Zend_Search_Lucene_Storage_File_Filesystem objects
+ * Array: filename => Zend_Search_Lucene_Storage_File object
+ *
+ * @var array
+ * @throws Zend_Search_Lucene_Exception
+ */
+ private $_fileHandlers;
+
+
+ /**
+ * Utility function to recursive directory creation
+ *
+ * @param string $dir
+ * @param integer $mode
+ * @param boolean $recursive
+ * @return boolean
+ */
+
+ static public function mkdirs($dir, $mode = 0777, $recursive = true)
+ {
+ if (is_null($dir) || $dir === '') {
+ return false;
+ }
+ if (is_dir($dir) || $dir === '/') {
+ return true;
+ }
+ if (self::mkdirs(dirname($dir), $mode, $recursive)) {
+ return mkdir($dir, $mode);
+ }
+ return false;
+ }
+
+
+ /**
+ * Object constructor
+ * Checks if $path is a directory or tries to create it.
+ *
+ * @param string $path
+ * @throws Zend_Search_Lucene_Exception
+ */
+ public function __construct($path)
+ {
+ if (!is_dir($path)) {
+ if (file_exists($path)) {
+ throw new Zend_Search_Lucene_Exception('Path exists, but it\'s not a directory');
+ } else {
+ if (!self::mkdirs($path)) {
+ throw new Zend_Search_Lucene_Exception("Can't create directory '$path'.");
+ }
+ }
+ }
+ $this->_dirPath = $path;
+ $this->_fileHandlers = array();
+ }
+
+
+ /**
+ * Closes the store.
+ *
+ * @return void
+ */
+ public function close()
+ {
+ foreach ($this->_fileHandlers as $fileObject) {
+ $fileObject->close();
+ }
+
+ unset($this->_fileHandlers);
+ }
+
+
+ /**
+ * Returns an array of strings, one for each file in the directory.
+ *
+ * @return array
+ */
+ public function fileList()
+ {
+ $result = array();
+
+ $dirContent = opendir( $this->_dirPath );
+ while ($file = readdir($dirContent)) {
+ if (($file == '..')||($file == '.')) continue;
+
+ $fullName = $this->_dirPath . '/' . $file;
+
+ if( !is_dir($this->_dirPath . '/' . $file) ) {
+ $result[] = $file;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Creates a new, empty file in the directory with the given $filename.
+ *
+ * @param string $filename
+ * @return Zend_Search_Lucene_Storage_File
+ */
+ public function createFile($filename)
+ {
+ if (isset($this->_fileHandlers[$filename])) {
+ $this->_fileHandlers[$filename]->close();
+ }
+ unset($this->_fileHandlers[$filename]);
+ $this->_fileHandlers[$filename] = new Zend_Search_Lucene_Storage_File_Filesystem($this->_dirPath . '/' . $filename, 'w+b');
+ return $this->_fileHandlers[$filename];
+ }
+
+
+ /**
+ * Removes an existing $filename in the directory.
+ *
+ * @param string $filename
+ * @return void
+ */
+ public function deleteFile($filename)
+ {
+ if (isset($this->_fileHandlers[$filename])) {
+ $this->_fileHandlers[$filename]->close();
+ }
+ unset($this->_fileHandlers[$filename]);
+ unlink($this->_dirPath .'/'. $filename);
+ }
+
+
+ /**
+ * Returns true if a file with the given $filename exists.
+ *
+ * @param string $filename
+ * @return boolean
+ */
+ public function fileExists($filename)
+ {
+ return file_exists($this->_dirPath .'/'. $filename);
+ }
+
+
+ /**
+ * Returns the length of a $filename in the directory.
+ *
+ * @param string $filename
+ * @return integer
+ */
+ public function fileLength($filename)
+ {
+ if (isset( $this->_fileHandlers[$filename] )) {
+ return $this->_fileHandlers[$filename]->size();
+ }
+ return filesize($this->_dirPath .'/'. $filename);
+ }
+
+
+ /**
+ * Returns the UNIX timestamp $filename was last modified.
+ *
+ * @param string $filename
+ * @return integer
+ */
+ public function fileModified($filename)
+ {
+ return filemtime($this->_dirPath .'/'. $filename);
+ }
+
+
+ /**
+ * Renames an existing file in the directory.
+ *
+ * @param string $from
+ * @param string $to
+ * @return void
+ */
+ public function renameFile($from, $to)
+ {
+ if ($this->_fileHandlers[$from] !== null) {
+ $this->_fileHandlers[$from]->close();
+ }
+ unset($this->_fileHandlers[$from]);
+
+ if ($this->_fileHandlers[$to] !== null) {
+ $this->_fileHandlers[$to]->close();
+ }
+ unset($this->_fileHandlers[$to]);
+
+ if (file_exists($this->_dirPath . '/' . $to)) {
+ unlink($this->_dirPath . '/' . $to);
+ }
+
+ return @rename($this->_dirPath . '/' . $from, $this->_dirPath . '/' . $to);
+ }
+
+
+ /**
+ * Sets the modified time of $filename to now.
+ *
+ * @param string $filename
+ * @return void
+ */
+ public function touchFile($filename)
+ {
+ return touch($this->_dirPath .'/'. $filename);
+ }
+
+
+ /**
+ * Returns a Zend_Search_Lucene_Storage_File object for a given $filename in the directory.
+ *
+ * @param string $filename
+ * @return Zend_Search_Lucene_Storage_File
+ */
+ public function getFileObject($filename)
+ {
+ if (isset( $this->_fileHandlers[$filename] )) {
+ $this->_fileHandlers[$filename]->seek(0);
+ return $this->_fileHandlers[$filename];
+ }
+
+ $this->_fileHandlers[$filename] = new Zend_Search_Lucene_Storage_File_Filesystem($this->_dirPath . '/' . $filename, 'rb');
+ return $this->_fileHandlers[$filename];
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File.php
new file mode 100644
index 00000000..f62af33a
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File.php
@@ -0,0 +1,376 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+abstract class Zend_Search_Lucene_Storage_File
+{
+
+ /**
+ * Class constructor. Open the file.
+ */
+ abstract public function __construct($filename, $mode='r');
+
+
+ /**
+ * Reads $length number of bytes at the current position in the
+ * file and advances the file pointer.
+ *
+ * @param integer $length
+ * @return string
+ */
+ abstract protected function _fread($length=1);
+
+
+ /**
+ * Sets the file position indicator and advances the file pointer.
+ * The new position, measured in bytes from the beginning of the file,
+ * is obtained by adding offset to the position specified by whence,
+ * whose values are defined as follows:
+ * SEEK_SET - Set position equal to offset bytes.
+ * SEEK_CUR - Set position to current location plus offset.
+ * SEEK_END - Set position to end-of-file plus offset. (To move to
+ * a position before the end-of-file, you need to pass a negative value
+ * in offset.)
+ * Upon success, returns 0; otherwise, returns -1
+ *
+ * @param integer $offset
+ * @param integer $whence
+ * @return integer
+ */
+ abstract public function seek($offset, $whence=SEEK_SET);
+
+ /**
+ * Get file position.
+ *
+ * @return integer
+ */
+ abstract public function tell();
+
+ /**
+ * Writes $length number of bytes (all, if $length===null) to the end
+ * of the file.
+ *
+ * @param string $data
+ * @param integer $length
+ */
+ abstract protected function _fwrite($data, $length=null);
+
+
+ /**
+ * Reads a byte from the current position in the file
+ * and advances the file pointer.
+ *
+ * @return integer
+ */
+ public function readByte()
+ {
+ return ord($this->_fread(1));
+ }
+
+ /**
+ * Writes a byte to the end of the file.
+ *
+ * @param integer $byte
+ */
+ public function writeByte($byte)
+ {
+ return $this->_fwrite(chr($byte), 1);
+ }
+
+ /**
+ * Read num bytes from the current position in the file
+ * and advances the file pointer.
+ *
+ * @param integer $num
+ * @return string
+ */
+ public function readBytes($num)
+ {
+ return $this->_fread($num);
+ }
+
+ /**
+ * Writes num bytes of data (all, if $num===null) to the end
+ * of the file.
+ *
+ * @param string $data
+ * @param integer $num
+ */
+ public function writeBytes($data, $num=null)
+ {
+ $this->_fwrite($data, $num);
+ }
+
+
+ /**
+ * Reads an integer from the current position in the file
+ * and advances the file pointer.
+ *
+ * @return integer
+ */
+ public function readInt()
+ {
+ $str = $this->_fread(4);
+
+ return ord($str{0}) << 24 |
+ ord($str{1}) << 16 |
+ ord($str{2}) << 8 |
+ ord($str{3});
+ }
+
+
+ /**
+ * Writes an integer to the end of file.
+ *
+ * @param integer $value
+ */
+ public function writeInt($value)
+ {
+ settype($value, 'integer');
+ $this->_fwrite( chr($value>>24 & 0xFF) .
+ chr($value>>16 & 0xFF) .
+ chr($value>>8 & 0xFF) .
+ chr($value & 0xFF), 4 );
+ }
+
+
+ /**
+ * Returns a long integer from the current position in the file
+ * and advances the file pointer.
+ *
+ * @return integer
+ */
+ public function readLong()
+ {
+ $str = $this->_fread(8);
+
+ /**
+ * PHP uses long as largest integer. fseek() uses long for offset.
+ * long has 4 bytes in a lot of systems. 4 bytes are discarded to prevent
+ * conversion to float.
+ * So, largest index segment file is 2Gb
+ */
+ return /* ord($str{0}) << 56 | */
+ /* ord($str{1}) << 48 | */
+ /* ord($str{2}) << 40 | */
+ /* ord($str{3}) << 32 | */
+ ord($str{4}) << 24 |
+ ord($str{5}) << 16 |
+ ord($str{6}) << 8 |
+ ord($str{7});
+ }
+
+ /**
+ * Writes long integer to the end of file
+ *
+ * @param integer $value
+ */
+ public function writeLong($value)
+ {
+ /**
+ * PHP uses long as largest integer. fseek() uses long for offset.
+ * long has 4 bytes in a lot of systems. 4 bytes are discarded to prevent
+ * conversion to float.
+ * So, largest index segment file is 2Gb
+ */
+ settype($value, 'integer');
+ $this->_fwrite( "\x00\x00\x00\x00" .
+ chr($value>>24 & 0xFF) .
+ chr($value>>16 & 0xFF) .
+ chr($value>>8 & 0xFF) .
+ chr($value & 0xFF), 8 );
+ }
+
+
+
+ /**
+ * Returns a variable-length integer from the current
+ * position in the file and advances the file pointer.
+ *
+ * @return integer
+ */
+ public function readVInt()
+ {
+ $nextByte = ord($this->_fread(1));
+ $val = $nextByte & 0x7F;
+
+ for ($shift=7; ($nextByte & 0x80) != 0; $shift += 7) {
+ $nextByte = ord($this->_fread(1));
+ $val |= ($nextByte & 0x7F) << $shift;
+ }
+ return $val;
+ }
+
+ /**
+ * Writes a variable-length integer to the end of file.
+ *
+ * @param integer $value
+ */
+ public function writeVInt($value)
+ {
+ settype($value, 'integer');
+ while ($value > 0x7F) {
+ $this->_fwrite(chr( ($value & 0x7F)|0x80 ));
+ $value >>= 7;
+ }
+ $this->_fwrite(chr($value));
+ }
+
+
+ /**
+ * Reads a string from the current position in the file
+ * and advances the file pointer.
+ *
+ * @return string
+ */
+ public function readString()
+ {
+ $strlen = $this->readVInt();
+ if ($strlen == 0) {
+ return '';
+ } else {
+ /**
+ * This implementation supports only Basic Multilingual Plane
+ * (BMP) characters (from 0x0000 to 0xFFFF) and doesn't support
+ * "supplementary characters" (characters whose code points are
+ * greater than 0xFFFF)
+ * Java 2 represents these characters as a pair of char (16-bit)
+ * values, the first from the high-surrogates range (0xD800-0xDBFF),
+ * the second from the low-surrogates range (0xDC00-0xDFFF). Then
+ * they are encoded as usual UTF-8 characters in six bytes.
+ * Standard UTF-8 representation uses four bytes for supplementary
+ * characters.
+ */
+
+ $str_val = $this->_fread($strlen);
+
+ for ($count = 0; $count < $strlen; $count++ ) {
+ if (( ord($str_val{$count}) & 0xC0 ) == 0xC0) {
+ $addBytes = 1;
+ if (ord($str_val{$count}) & 0x20 ) {
+ $addBytes++;
+
+ // Never used. Java2 doesn't encode strings in four bytes
+ if (ord($str_val{$count}) & 0x10 ) {
+ $addBytes++;
+ }
+ }
+ $str_val .= $this->_fread($addBytes);
+ $strlen += $addBytes;
+
+ // Check for null character. Java2 encodes null character
+ // in two bytes.
+ if (ord($str_val{$count}) == 0xC0 &&
+ ord($str_val{$count+1}) == 0x80 ) {
+ $str_val{$count} = 0;
+ $str_val = substr($str_val,0,$count+1)
+ . substr($str_val,$count+2);
+ }
+ $count += $addBytes;
+ }
+ }
+
+ return $str_val;
+ }
+ }
+
+ /**
+ * Writes a string to the end of file.
+ *
+ * @param string $str
+ * @throws Zend_Search_Lucene_Exception
+ */
+ public function writeString($str)
+ {
+ /**
+ * This implementation supports only Basic Multilingual Plane
+ * (BMP) characters (from 0x0000 to 0xFFFF) and doesn't support
+ * "supplementary characters" (characters whose code points are
+ * greater than 0xFFFF)
+ * Java 2 represents these characters as a pair of char (16-bit)
+ * values, the first from the high-surrogates range (0xD800-0xDBFF),
+ * the second from the low-surrogates range (0xDC00-0xDFFF). Then
+ * they are encoded as usual UTF-8 characters in six bytes.
+ * Standard UTF-8 representation uses four bytes for supplementary
+ * characters.
+ */
+
+ // convert input to a string before iterating string characters
+ settype($str, 'string');
+
+ $chars = $strlen = strlen($str);
+ $containNullChars = false;
+
+ for ($count = 0; $count < $strlen; $count++ ) {
+ /**
+ * String is already in Java 2 representation.
+ * We should only calculate actual string length and replace
+ * \x00 by \xC0\x80
+ */
+ if ((ord($str{$count}) & 0xC0) == 0xC0) {
+ $addBytes = 1;
+ if (ord($str{$count}) & 0x20 ) {
+ $addBytes++;
+
+ // Never used. Java2 doesn't encode strings in four bytes
+ // and we dont't support non-BMP characters
+ if (ord($str{$count}) & 0x10 ) {
+ $addBytes++;
+ }
+ }
+ $chars -= $addBytes;
+
+ if (ord($str{$count}) == 0 ) {
+ $containNullChars = true;
+ }
+ $count += $addBytes;
+ }
+ }
+
+ if ($chars < 0) {
+ throw new Zend_Search_Lucene_Exception('Invalid UTF-8 string');
+ }
+
+ $this->writeVInt($chars);
+ if ($containNullChars) {
+ $this->_fwrite(str_replace($str, "\x00", "\xC0\x80"));
+ } else {
+ $this->_fwrite($str);
+ }
+ }
+
+
+ /**
+ * Reads binary data from the current position in the file
+ * and advances the file pointer.
+ *
+ * @return string
+ */
+ public function readBinary()
+ {
+ return $this->_fread($this->readVInt());
+ }
+} \ No newline at end of file
diff --git a/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File/Filesystem.php b/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File/Filesystem.php
new file mode 100644
index 00000000..fc6adcf5
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File/Filesystem.php
@@ -0,0 +1,170 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE, and
+ * is available through the world-wide-web at the following URL:
+ * http://www.zend.com/license/framework/1_0.txt. If you did not receive
+ * a copy of the Zend Framework license and are unable to obtain it
+ * through the world-wide-web, please send a note to license@zend.com
+ * so we can mail you a copy immediately.
+ *
+ * @package Zend_Search_Lucene
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ */
+
+
+/** Zend_Search_Lucene_Storage_File */
+require_once 'Zend/Search/Lucene/Storage/File.php';
+
+/** Zend_Search_Lucene_Exception */
+require_once 'Zend/Search/Lucene/Exception.php';
+
+
+/**
+ * @package Zend_Search_Lucene
+ * @subpackage Storage
+ * @copyright Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
+ *
+ */
+class Zend_Search_Lucene_Storage_File_Filesystem extends Zend_Search_Lucene_Storage_File
+{
+ /**
+ * Resource of the open file
+ *
+ * @var resource
+ */
+ private $_fileHandle;
+
+
+ /**
+ * Class constructor. Open the file.
+ *
+ * @param string $filename
+ * @param string $mode
+ */
+ public function __construct($filename, $mode='rb')
+ {
+ global $php_errormsg;
+
+ $trackErrors = ini_get( "track_errors");
+ ini_set('track_errors', '1');
+
+ $this->_fileHandle = @fopen($filename, $mode);
+
+ if ($this->_fileHandle===false) {
+ ini_set('track_errors', $trackErrors);
+ throw new Zend_Search_Lucene_Exception($php_errormsg);
+ }
+
+ ini_set('track_errors', $trackErrors);
+ }
+
+
+ /**
+ * Sets the file position indicator and advances the file pointer.
+ * The new position, measured in bytes from the beginning of the file,
+ * is obtained by adding offset to the position specified by whence,
+ * whose values are defined as follows:
+ * SEEK_SET - Set position equal to offset bytes.
+ * SEEK_CUR - Set position to current location plus offset.
+ * SEEK_END - Set position to end-of-file plus offset. (To move to
+ * a position before the end-of-file, you need to pass a negative value
+ * in offset.)
+ * Upon success, returns 0; otherwise, returns -1
+ *
+ * @param integer $offset
+ * @param integer $whence
+ * @return integer
+ */
+ public function seek($offset, $whence=SEEK_SET)
+ {
+ return fseek($this->_fileHandle, $offset, $whence);
+ }
+
+
+ /**
+ * Get file position.
+ *
+ * @return integer
+ */
+ public function tell()
+ {
+ return ftell($this->_fileHandle);
+ }
+
+
+ /**
+ * Close File object
+ */
+ public function close()
+ {
+ if ($this->_fileHandle !== null ) {
+ @fclose($this->_fileHandle);
+ $this->_fileHandle = null;
+ }
+ }
+
+ /**
+ * Get the size of the already opened file
+ *
+ * @return integer
+ */
+ public function size()
+ {
+ $position = ftell($this->_fileHandle);
+ fseek($this->_fileHandle, 0, SEEK_END);
+ $size = ftell($this->_fileHandle);
+ fseek($this->_fileHandle,$position);
+
+ return $size;
+ }
+
+ /**
+ * Read a $length bytes from the file and advance the file pointer.
+ *
+ * @param integer $length
+ * @return string
+ */
+ protected function _fread($length=1)
+ {
+ if ($length == 0) {
+ return '';
+ }
+
+ if ($length < 1024) {
+ return fread($this->_fileHandle, $length);
+ }
+
+ $data = '';
+ while ( $length > 0 && ($nextBlock = fread($this->_fileHandle, $length)) != false ) {
+ $data .= $nextBlock;
+ $length -= strlen($nextBlock);
+ }
+ return $data;
+ }
+
+
+ /**
+ * Writes $length number of bytes (all, if $length===null) to the end
+ * of the file.
+ *
+ * @param string $data
+ * @param integer $length
+ */
+ protected function _fwrite($data, $length=null)
+ {
+ if ($length === null ) {
+ fwrite($this->_fileHandle, $data);
+ } else {
+ fwrite($this->_fileHandle, $data, $length);
+ }
+ }
+}
+
diff --git a/demos/quickstart/protected/index/Zend/Search/TODO.txt b/demos/quickstart/protected/index/Zend/Search/TODO.txt
new file mode 100644
index 00000000..06f7b487
--- /dev/null
+++ b/demos/quickstart/protected/index/Zend/Search/TODO.txt
@@ -0,0 +1,14 @@
+@todo
+
+- Improve API: fix ZSearchMultiTermQuery($terms, $signs);
+
+- Analysis and indexing engine
+
+- Additional queries: phrase, wildcard, proximity, and range
+
+- Better class-level docblocks (most functions okay)
+
+- Some Windows issues(?) during indexing
+
+- Finish renaming classes to PEAR-like conventions
+
diff --git a/demos/quickstart/protected/index/ZendSearch.php b/demos/quickstart/protected/index/ZendSearch.php
new file mode 100644
index 00000000..136004de
--- /dev/null
+++ b/demos/quickstart/protected/index/ZendSearch.php
@@ -0,0 +1,52 @@
+<?php
+/*
+ * Created on 7/05/2006
+ */
+
+class ZendSearch extends TModule
+{
+ private $_data;
+ private $_ZF;
+ private $_search;
+
+ public function setIndexDataDirectory($path)
+ {
+ $this->_data = Prado::getPathOfNamespace($path);
+ }
+
+ public function getIndexDataDirectory()
+ {
+ return $this->_data;
+ }
+
+ public function setZendFramework($path)
+ {
+ $this->_ZF = Prado::getPathOfNamespace($path);
+ }
+
+ protected function importZendNamespace()
+ {
+ $zendBase = !is_null($this->_ZF) ? $this->_ZF.'.*' : 'Application.index.*';
+ $path = !is_null($this->_ZF) ? $this->_ZF.'.Zend.*' : 'Application.index.Zend.*';
+ Prado::using($zendBase);
+ Prado::setPathOfAlias('Zend', Prado::getPathOfNamespace($path));
+ }
+
+ protected function getZendSearch()
+ {
+ if(is_null($this->_search))
+ {
+ $this->importZendNamespace();
+ Prado::using('Zend.Search.Lucene');
+ $this->_search = new Zend_Search_Lucene($this->_data);
+ }
+ return $this->_search;
+ }
+
+ public function find($query)
+ {
+ return $this->getZendSearch()->find(strtolower($query));
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/quickstart/protected/index/data/_0.cfs b/demos/quickstart/protected/index/data/_0.cfs
new file mode 100644
index 00000000..f3cb1bfb
--- /dev/null
+++ b/demos/quickstart/protected/index/data/_0.cfs
Binary files differ
diff --git a/demos/quickstart/protected/index/data/deletable b/demos/quickstart/protected/index/data/deletable
new file mode 100644
index 00000000..593f4708
--- /dev/null
+++ b/demos/quickstart/protected/index/data/deletable
Binary files differ
diff --git a/demos/quickstart/protected/index/data/segments b/demos/quickstart/protected/index/data/segments
new file mode 100644
index 00000000..5509e664
--- /dev/null
+++ b/demos/quickstart/protected/index/data/segments
Binary files differ
diff --git a/demos/quickstart/protected/pages/Advanced/Assets.page b/demos/quickstart/protected/pages/Advanced/Assets.page
index d69a1bcd..e79f4ecf 100644
--- a/demos/quickstart/protected/pages/Advanced/Assets.page
+++ b/demos/quickstart/protected/pages/Advanced/Assets.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Assets</h1>
+<h1 id="5701">Assets</h1>
<p>
Assets are resource files (such as images, sounds, videos, CSS stylesheets, javascripts, etc.) that belong to specific component classes. Assets are meant to be provided to Web users. For better reusability and easier deployment of the corresponding component classes, assets should reside together with the component class files . For example, a toggle button may use two images, stored in file <tt>down.gif</tt> and <tt>up.gif</tt>, to show different toggle states. If we require the image files be stored under <tt>images</tt> directory under the Web server document root, it would be inconvenient for the users of the toggle button component, because each time they develop or deploy a new application, they would have to manually copy the image files to that specific directory. To eliminate this requirement, a directory relative to the component class file should be used for storing the image files. A common strategy is to use the directory containing the component class file to store the asset files.
</p>
@@ -8,7 +8,7 @@ Assets are resource files (such as images, sounds, videos, CSS stylesheets, java
Because directories containing component class files are normally inaccessible by Web users, PRADO implements an asset publishing scheme to make available the assets to Web users. An asset, after being published, will have a URL by which Web users can retrieve the asset file.
</p>
-<h2>Asset Publishing</h2>
+<h2 id="5702">Asset Publishing</h2>
<p>
PRADO provides several methods for publishing assets or directories containing assets:
</p>
@@ -21,7 +21,7 @@ PRADO provides several methods for publishing assets or directories containing a
BE AWARE: Be very careful with assets publishing, because it gives Web users access to files that were previously inaccessible to them. Make sure that you do not publish files that do not want Web users to see.
</p>
-<h2>Customization</h2>
+<h2 id="5703">Customization</h2>
<p>
Asset publishing is managed by the <tt>System.Web.TAssetManager</tt> module. By default, all published asset files are stored under the <tt>[AppEntryPath]/assets</tt> directory, where <tt>AppEntryPath</tt> refers to the directory containing the application entry script. Make sure the <tt>assets</tt> directory is writable by the Web server process. You may change this directory to another by configuring the <tt>BasePath</tt> and <tt>BaseUrl</tt> properties of the <tt>TAssetManager</tt> module in application configuration,
</p>
@@ -34,7 +34,7 @@ Asset publishing is managed by the <tt>System.Web.TAssetManager</tt> module. By
&lt;/modules&gt;
</com:TTextHighlighter>
-<h2>Performance</h2>
+<h2 id="5704">Performance</h2>
<p>
PRADO uses caching techniques to ensure the efficiency of asset publishing. Publishing an asset essentially requires file copy operation, which is expensive. To save unnecessary file copy operations, <tt>System.Web.TAssetManager</tt> only publishes an asset when it has a newer file modification time than the published file. When an application runs under the <tt>Performance</tt> mode, such timestamp checkings are also omitted.
</p>
@@ -42,7 +42,7 @@ PRADO uses caching techniques to ensure the efficiency of asset publishing. Publ
ADVISORY: Do not overuse asset publishing. The asset concept is mainly used to help better reuse and redistribute component classes. Normally, you should not use asset publishing for resources that are not bound to any component in an application. For example, you should not use asset publishing for images that are mainly used as design elements (e.g. logos, background images, etc.) Let Web server to directly serve these images will help improve the performance of your application.
</p>
-<h2>A Toggle Button Example</h2>
+<h2 id="5705">A Toggle Button Example</h2>
<p>
We now use the toggle button example to explain the usage of assets. The control uses two image files <tt>up.gif</tt> and <tt>down.gif</tt>, which are stored under the directory containing the control class file. When the button is in <tt>Up</tt> state, we would like to show the <tt>up.gif</tt> image. This can be done as follows,
</p>
diff --git a/demos/quickstart/protected/pages/Advanced/Auth.page b/demos/quickstart/protected/pages/Advanced/Auth.page
index ec876f54..3373644a 100644
--- a/demos/quickstart/protected/pages/Advanced/Auth.page
+++ b/demos/quickstart/protected/pages/Advanced/Auth.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Authentication and Authorization</h1>
+<h1 id="5501">Authentication and Authorization</h1>
<p>
Authentication is a process of verifying whether someone is who he claims he is. It usually involves a username and a password, but may include any other methods of demonstrating identity, such as a smart card, fingerprints, etc.
</p>
@@ -8,7 +8,7 @@ Authentication is a process of verifying whether someone is who he claims he is.
Authorization is finding out if the person, once identified, is permitted to manipulate specific resources. This is usually determined by finding out if that person is of a particular role that has access to the resources.
</p>
-<h2>How PRADO Auth Framework Works</h2>
+<h2 id="5502">How PRADO Auth Framework Works</h2>
<p>
PRADO provides an extensible authentication/authorization framework. As described in <a href="?page=Fundamentals.Applications">application lifecycles</a>, <tt>TApplication</tt> reserves several lifecycles for modules responsible for authentication and authorization. PRADO provides the <tt>TAuthManager</tt> module for such purposes. Developers can plug in their own auth modules easily. <tt>TAuthManager</tt> is designed to be used together with <tt>TUserManager</tt> module, which implements a read-only user database.
</p>
@@ -19,7 +19,7 @@ When a page request occurs, <tt>TAuthManager</tt> will try to restore user infor
During <tt>Authorization</tt> application lifecycle, which occurs after <tt>Authentication</tt> lifecycle, <tt>TAuthManager</tt> will verify if the current user has access to the requested page according to a set of authorization rules. The authorization is role-based, i.e., a user has access to a page if 1) the page explicitly states that the user has access; 2) or the user is of a particular role that has access to the page. If the user does not have access to the page, <tt>TAuthManager</tt> will redirect user browser to the login page which is specified by <tt>LoginPage</tt> property.
</p>
-<h2>Using PRADO Auth Framework</h2>
+<h2 id="5503">Using PRADO Auth Framework</h2>
<p>
To enable PRADO auth framework, add the <tt>TAuthManager</tt> module and <tt>TUserManager</tt> module to <a href="?page=Configurations.AppConfig">application configuration</a>,
</p>
@@ -75,7 +75,7 @@ When a page request is being processed, a list of authorization rules may be ava
In the above example, anonymous users will be denied from posting to <tt>PageID1</tt> and <tt>PageID2</tt>, while <tt>User1</tt> and <tt>User2</tt> and all users of role <tt>Role1</tt> can access the two pages (in both <tt>get</tt> and <tt>post</tt> methods).
</p>
-<h2>Using <tt>TUserManager</tt></h2>
+<h2 id="5504">Using <tt>TUserManager</tt></h2>
<p>
As aforementioned, <tt>TUserManager</tt> implements a read-only user database. The user information are specified in either application configuration or an external XML file.
</p>
diff --git a/demos/quickstart/protected/pages/Advanced/Error.page b/demos/quickstart/protected/pages/Advanced/Error.page
index 55217bfe..9d5e3037 100644
--- a/demos/quickstart/protected/pages/Advanced/Error.page
+++ b/demos/quickstart/protected/pages/Advanced/Error.page
@@ -1,11 +1,11 @@
<com:TContent ID="body" >
-<h1>Error Handling and Reporting</h1>
+<h1 id="6301">Error Handling and Reporting</h1>
<p>
PRADO provides a complete error handling and reporting framework based on the PHP 5 exception mechanism.
</p>
-<h2>Exception Classes</h2>
+<h2 id="6302">Exception Classes</h2>
<p>
Errors occur in a PRADO application may be classified into three categories: those caused by PHP script parsing, those caused by wrong code (such as calling an undefined function, setting an unknown property), and those caused by improper use of the Web application by client users (such as attempting to access restricted pages). PRADO is unable to deal with the first category of errors because they cannot be caughted in PHP code. PRADO provides an exception hierarchy to deal with the second and third categories.
</p>
@@ -32,18 +32,18 @@ Exceptions raised due to improper usage of the PRADO framework inherit from <tt>
Errors due to improper usage of the Web application by client users inherit from <tt>TApplicationException</tt>.
</p>
-<h2>Raising Exceptions</h2>
+<h2 id="6303">Raising Exceptions</h2>
<p>
Raising exceptions in PRADO has no difference than raising a normal PHP exception. The only thing matters is to raise the right exception. In general, exceptions meant to be shown to application users should use <tt>THttpException</tt>, while exceptions shown to developers should use other exception classes.
</p>
-<h2>Error Capturing and Reporting</h2>
+<h2 id="6304">Error Capturing and Reporting</h2>
<p>
Exceptions raised during the runtime of PRADO applications are captured by <tt>System.Exceptions.TErrorHandler</tt> module. Different output templates are used to display the captured exceptions. <tt>THttpException</tt> is assumed to contain error messages that are meant for application end users and thus uses a specific group of templates. For all other exceptions, a common template shown as follows is used for presenting the exceptions.
</p>
<a href="<%~ exception2.gif %>" target="_blank"><img src="<%~ exception.gif %>" alt="exception page" style="border:0px"/></a>
-<h2>Customizing Error Display</h2>
+<h2 id="6305">Customizing Error Display</h2>
<p>
Developers can customize the presentation of exception messages. By default, all error output templates are stored under <tt>framework/Exceptions/templates</tt>. The location can be changed by configuring <tt>TErrorHandler</tt> in application configuration,
</p>
diff --git a/demos/quickstart/protected/pages/Advanced/I18N.page b/demos/quickstart/protected/pages/Advanced/I18N.page
index 4a220c4c..5b1fafa0 100644
--- a/demos/quickstart/protected/pages/Advanced/I18N.page
+++ b/demos/quickstart/protected/pages/Advanced/I18N.page
@@ -1,9 +1,9 @@
<com:TContent ID="body" >
-<h1>Internationalization (I18N) and Localization (L10N)</h1>
+<h1 id="6201">Internationalization (I18N) and Localization (L10N)</h1>
<p>Many web application built with PHP will not have internationalization in mind when it was first written. It may be that it was not intended for use in languages and cultures. Internationalization is an important aspect due to the increase adoption of the Internet in many non-English speaking countries. The process of internationalization and localization will contain difficulties. Below are some general guidelines to internationalize an existing application.</p>
-<h2>Separate culture/locale sensitive data</h2>
+<h2 id="6203">Separate culture/locale sensitive data</h2>
<p>Identify and separate data that varies with culture. The most obvious are text/string/message. Other type of data should also be considered. The following list categorize some examples of culture sensitive data
</p>
@@ -25,7 +25,7 @@
<p>If possible all manner of text should be isolated and store in a persistence format. These text include, application error messages, hard coded strings in PHP files, emails, static HTML text, and text on form elements (e.g. buttons).</p>
-<h2>Configuration</h2>
+<h2 id="6204">Configuration</h2>
<p>To enable the localization features in Prado, you need to add a few configuration options in your <a href="?page=Configurations.AppConfig">application configuration</a>.
First you need to include the <tt>System.I18N.*</tt> namespace to your paths.
</p>
@@ -52,7 +52,7 @@ With <tt>cache</tt> enabled, translated messages are saved in the application <t
<p>With the configuration complete, we can now start to localize your application. If you have <tt>autosave</tt> enabled, after running your application with some localization activity (i.e. translating some text), you will see a directory and a <tt>messages.xml</tt> created within your <tt>source</tt> directory.</p>
-<h2>What to do with <tt>messages.xml</tt>?</h2>
+<h2 id="6205">What to do with <tt>messages.xml</tt>?</h2>
<p>The translation message catalogue file, if using <tt>type="XLIFF"</tt>, is a standardized translation message interchange XML format. You can edit the XML file using any UTF-8 aware editor. The format of the XML is something like the following.</p>
<com:TTextHighlighter Language="xml" CssClass="source">
@@ -77,7 +77,7 @@ With <tt>cache</tt> enabled, translated messages are saved in the application <t
Each translation message is wrapped within a <tt>trans-unit</tt> tag, where <tt>source</tt> is the original message, and <tt>target</tt> is the translated message. Editors such as <a href="http://www.heartsome.net/EN/xlfedit.html">Heartsome XLIFF Translation Editor</a> can help in editing these XML files.
-<h2>Setting and Changing Culture</h2>
+<h2 id="6206">Setting and Changing Culture</h2>
<p>Once globalization is enabled, you can access the globalization settings, such as, <tt>Culture</tt>, <tt>Charset</tt>, etc, using </p>
<com:TTextHighlighter CssClass="source">
$globalization = $this->getApplication()->getGlobalization();
@@ -98,9 +98,9 @@ Lastly, you can change the globalization settings on page by page basis using <a
&lt;%@ Application.Globalization.Culture="zh" %&gt;
</com:TTextHighlighter>
-<h2>Localizing your Prado application</h2>
+<h2 id="6207">Localizing your Prado application</h2>
There are two areas in your application that may need message or string localization, in PHP code and in the templates. To localize strings within PHP, use the <tt>localize</tt> function detailed below. To localize text in the template, use the <a href="#ttranslate">TTranslate</a> component.
-<h2>Using <tt>localize</tt> function to translate text within PHP</h2>
+<h2 id="6208">Using <tt>localize</tt> function to translate text within PHP</h2>
<p>The <tt>localize</tt> function searches for a translated string that matches original from your translation source. First, you need to locate all the hard coded text in PHP that are displayed or sent to the end user. The following example localizes the text of the <tt>$sender</tt> (assuming, say, the sender is a button). The original code before localization is as follows.
<com:TTextHighlighter CssClass="source">
@@ -118,7 +118,7 @@ function clickMe($sender,$param)
}
</com:TTextHighlighter>
-<h2>Compound Messages</h2>
+<h2 id="6209">Compound Messages</h2>
<p>Compound messages can contain variable data. For example, in the message "There are 12 users online.", the integer 12 may change depending on some data in your application. This is difficult to translate because the position of the variable data may be difference for different languages. In addition, different languages have their own rules for plurals (if any) and/or quantifiers. The following example can not be easily translated, because the sentence structure is fixed by hard coding the variable data within message.</p>
<com:TTextHighlighter CssClass="source">
@@ -137,9 +137,9 @@ The <tt>localize</tt> function does not solve the problem of localizing language
<p>The following sample demonstrates the basics of localization in Prado.</p>
<com:RunBar PagePath="Advanced.Samples.I18N.Home" />
-<h1>I18N Components</h1>
+<h1 id="6202">I18N Components</h1>
<a name="ttranslate"></a>
-<h2>TTranslate</h2>
+<h2 id="6210">TTranslate</h2>
<p>Messages and strings can be localized in PHP or in templates.
To translate a message or string in the template, use <tt>TTranslate</tt>.</p>
@@ -164,7 +164,7 @@ The time is {time}.
&lt;com:TLabel Text="&lt;%[ Hello World! ]%&gt;" /&gt;
</com:TTextHighlighter>
-<h2>TDateFormat</h2>
+<h2 id="6211">TDateFormat</h2>
<p>Formatting localized date and time is straight forward.</p>
<com:TTextHighlighter Language="prado" CssClass="source">
&lt;com:TDateFormat Value="12/01/2005" /&gt;
@@ -254,7 +254,7 @@ Format Pattern Result
<p>If the <tt>Value</tt> property is not specified, the current date and time is used.</p>
-<h2>TNumberFormat</h2>
+<h2 id="6212">TNumberFormat</h2>
<p>PRADO's Internationalization framework provide localized currency formatting and number formatting. Please note that the <tt>TNumberFormat</tt> component provides formatting only, it does not perform current conversion or exchange.</p>
<p>Numbers can be formatted as currency, percentage, decimal or scientific
@@ -310,7 +310,7 @@ Pattern Output
</com:TTextHighlighter>
</p>
-<h2>TTranslateParameter</h2>
+<h2 id="6213">TTranslateParameter</h2>
<p>Compound messages, i.e., string substitution, can be accomplished with <tt>TTranslateParameter</tt>.
In the following example, the strings "{greeting}" and "{name}" will be replace
with the values of "Hello" and "World", respectively.The substitution string must be enclose with "{" and "}". The parameters can be further translated by using <tt>TTranslate</tt>.
@@ -325,7 +325,7 @@ with the values of "Hello" and "World", respectively.The substitution string mus
<a name="choice-format"></a>
-<h2>TChoiceFormat</h2>
+<h2 id="6214">TChoiceFormat</h2>
<p>Using the <tt>localize</tt> function or <tt>TTranslate</tt> component to translate messages does not inform the translator the cardinality of the data required to determine the correct plural structure to use. It only informs them that there is a variable data, the data could be anything. Thus, the translator will be unable to determine with respect to the substitution data the correct plural, language structure or phrase to use . E.g. in English, to translate the sentence, "There are {number} of apples.", the resulting translation should be different depending on the <tt>number</tt> of apples.</p>
diff --git a/demos/quickstart/protected/pages/Advanced/Logging.page b/demos/quickstart/protected/pages/Advanced/Logging.page
index ec3e7879..5270a00d 100644
--- a/demos/quickstart/protected/pages/Advanced/Logging.page
+++ b/demos/quickstart/protected/pages/Advanced/Logging.page
@@ -1,12 +1,12 @@
<com:TContent ID="body" >
-<h1>Logging</h1>
+<h1 id="6101">Logging</h1>
<p>
PRADO provides a highly flexible and extensible logging functionality. Messages logged can be classified according to log levels and message categories. Using level and category filters, the messages can be further routed to different destinations, such as files, emails, browser windows, etc. The following diagram shows the basic architecture of PRADO logging mechanism,
</p>
<img src="<%~ logrouter.gif %>" alt="Log router" />
-<h2>Using Logging Functions</h2>
+<h2 id="6102">Using Logging Functions</h2>
<p>
The following two methods are provided for logging messages in PRADO,
</p>
@@ -18,7 +18,7 @@ Prado::trace($message, $category);
The difference between <tt>Prado::log()</tt> and <tt>Prado::trace()</tt> is that the latter automatically selects the log level according to the application mode. If the application is in <tt>Debug</tt> mode, stack trace information is appended to the messages. <tt>Prado::trace()</tt> is widely used in the core code of the PRADO framework.
</p>
-<h2>Message Routing</h2>
+<h2 id="6103">Message Routing</h2>
<p>
Messages logged using the above two functions are kept in memory. To make use of the messages, developers need to route them to specific destinations, such as files, emails, or browser windows. The message routing is managed by <tt>System.Util.TLogRouter</tt> module. When plugged into an application, it can route the messages to different destination in parallel. Currently, PRADO provides three types of routes:
</p>
@@ -44,7 +44,7 @@ To enable message routing, plug in and configure the <tt>TLogRouter</tt> module
In the above, the <tt>Levels</tt> and <tt>Categories</tt> specify the log and category filters to selectively retrieve the messages to the corresponding destinations.
</p>
-<h2>Message Filtering</h2>
+<h2 id="6104">Message Filtering</h2>
<p>
Messages can be filtered according to their log levels and categories. Each log message is associated with a log level and a category. With levels and categories, developers can selectively retrieve messages that they are interested on.
</p>
diff --git a/demos/quickstart/protected/pages/Advanced/MasterContent.page b/demos/quickstart/protected/pages/Advanced/MasterContent.page
index 0a68fe31..2698f865 100644
--- a/demos/quickstart/protected/pages/Advanced/MasterContent.page
+++ b/demos/quickstart/protected/pages/Advanced/MasterContent.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Master and Content</h1>
+<h1 id="5801">Master and Content</h1>
<p>
Pages in a Web application often share common portions. For example, all pages of this tutorial application share the same header and footer portions. If we repeatedly put header and footer in every page source file, it will be a maintenance headache if in future we want to something in the header or footer. To solve this problem, PRADO introduces the concept of master and content. It is essentially a decorator pattern, with content being decorated by master.
</p>
diff --git a/demos/quickstart/protected/pages/Advanced/Performance.page b/demos/quickstart/protected/pages/Advanced/Performance.page
index ed0cc6f8..d33c110b 100644
--- a/demos/quickstart/protected/pages/Advanced/Performance.page
+++ b/demos/quickstart/protected/pages/Advanced/Performance.page
@@ -1,11 +1,11 @@
<com:TContent ID="body" >
-<h1>Performance Tuning</h1>
+<h1 id="6401">Performance Tuning</h1>
<p>
Performance of Web applications is affected by many factors. Database access, file system operations, network bandwidth are all potential affecting factors. PRADO tries in every effort to reduce the performance impact caused by the framework.
</p>
-<h2>Caching</h2>
+<h2 id="6402">Caching</h2>
<p>
PRADO provides a generic caching technique used by in several core parts of the framework. For example, when caching is enabled, <tt>TTemplateManager</tt> will save parsed templates in cache and reuse them in the following requests, which saves time for parsing templates. The <tt>TThemeManager</tt> adopts the similar strategy to deal with theme parsing.
</p>
@@ -33,7 +33,7 @@ if($application->Cache) {
where <tt>$keyName</tt> should be a string that uniquely identifies the data item stored in cache.
</p>
-<h2>Using <tt>pradolite.php</tt></h2>
+<h2 id="6403">Using <tt>pradolite.php</tt></h2>
<p>
Including many PHP script files may impact application performance significantly. PRADO classes are stored in different files and when processing a page request, it may require including tens of class files.To alleviate this problem, in each PRADO release, a file named <tt>pradolite.php</tt> is also included. The file is a merge of all core PRADO class files with comments being stripped off and message logging removed.
</p>
@@ -41,7 +41,7 @@ Including many PHP script files may impact application performance significantly
To use <tt>pradolite.php</tt>, in your application entry script, replace the inclusion of <tt>prado.php</tt> with <tt>pradolite.php</tt>.
</p>
-<h2>Changing Application Mode</h2>
+<h2 id="6404">Changing Application Mode</h2>
<p>
Application mode also affects application performance. A PRADO application can be in one of the following modes: <tt>Off</tt>, <tt>Debug</tt>, <tt>Normal</tt> and <tt>Performance</tt>. The <tt>Debug</tt> mode should mainly be used during application development, while <tt>Normal</tt> mode is usually used in early stage after an application is deployed to ensure everything works correctly. After the application is proved to work stably for some period, the mode can be switched to <tt>Performance</tt> to further improve the performance.
</p>
@@ -57,7 +57,7 @@ To switch application mode, configure it in application configuration:
&lt;/application &gt;
</com:TTextHighlighter>
-<h2>Reducing Page Size</h2>
+<h2 id="6405">Reducing Page Size</h2>
<p>
By default, PRADO stores page state in hidden fields of the HTML output. The page state could be very large in size if complex controls, such as <tt>TDataGrid</tt>, is used. To reduce the size of the network transmitted page size, two strategies can be used.
</p>
@@ -76,7 +76,7 @@ Second, you may use a different page state storage. For example, page state may
Note, in the above the <tt>SpecialPage</tt> will use <tt>MyPersister2</tt> as its persister class, while the rest pages will use <tt>MyPersister1</tt>. Therefore, you can have different state persister strategies for different pages.
</p>
-<h2>Other Techniques</h2>
+<h2 id="6406">Other Techniques</h2>
<p>
Server caching techniques are proven to be very effective in improving the performance of PRADO applications. For example, we have observed that by using Zend Optimizer, the RPS (request per second) of a PRADO application can be increased by more than ten times. Of course, this is at the cost of stale output, while PRADO's caching techniques always ensure the correctness of the output.
</p>
diff --git a/demos/quickstart/protected/pages/Advanced/Scripts.page b/demos/quickstart/protected/pages/Advanced/Scripts.page
new file mode 100644
index 00000000..5921e865
--- /dev/null
+++ b/demos/quickstart/protected/pages/Advanced/Scripts.page
@@ -0,0 +1,397 @@
+<com:TContent ID="body" >
+<h1 id="6501">Introduction to Javascript</h1>
+This guide is based on the <a href="http://www.sergiopereira.com/articles/advjs.html">
+Quick guide to somewhat advanced JavaScript tour of some OO features</a> by Sergio Pereira.
+
+<h2 id="6502">Hey, I didn't know you could do that</h2>
+<p>
+ If you are a web developer and come from the same place I do, you have probably
+ used quite a bit of Javascript in your web pages, mostly as UI glue.
+</p>
+<p>
+
+ Until recently, I knew that Javascript had more OO capabilities than I was employing,
+ but I did not feel like I needed to use it. As the browsers started to support a more
+ standardized featureset of Javascript and the DOM, it became viable to write more
+ complex and functional code to run on the client. That helped giving birth to the
+ AJAX phenomena.
+</p>
+<p>
+ As we all start to learn what it takes to write our cool, AJAXy applications, we begin
+ to notice that the Javascript we used to know was really just the tip of the iceberg.
+ We now see Javascript being used beyond simple UI chores like input validation and frivolous
+ tasks. The client code now is far more advanced and layered, much like a real desktop
+ application or a client-server thick client. We see class libraries, object models,
+ hierarchies, patterns, and many other things we got used to seeing only in our server
+ side code.
+</p>
+<p>
+ In many ways we can say that suddenly the bar was put much higher than before. It takes
+ a heck lot more proficiency to write applications for the new Web and we need to improve
+ our Javascript skills to get there.
+ If you try to use many of the existing javascript libraries out there, like
+ <a href="http://prototype.conio.net/">Prototype.js</a>,
+ <a href="http://script.aculo.us/">Scriptaculous</a>,
+ <a href="http://moofx.mad4milk.net/">moo.fx</a>,
+ <a href="http://bennolan.com/behaviour/">Behaviour</a>,
+ <a href="http://developer.yahoo.net/yui/">YUI</a>,
+ etc you'll eventually find yourself reading the JS code. Maybe because you want
+ to learn how they do it, or because you're curious, or more often because that's the
+ only way to figure out how to use it, since documentation does not seem to be highly
+ regarded with most of these libraries. Whatever the case may be, you'll face some
+ kung-fu techniques that will be foreign and scary if you haven't seen anything like
+ that before.
+</p>
+
+<p>
+ The purpose of this article is precisely explaining the types of constructs that
+ many of us are not familiar with yet.
+</p>
+
+
+<h2 id="6503">JSON (JavaScript Object Notation)</h2>
+<p>
+ JavaScript Object Notation (<a href="http://www.json.org/">JSON</a>,) is one of the new
+ buzzwords popping up around the AJAX theme. JSON, simply put, is a way of
+ declaring an object in javascript. Let's see an example right away and note
+ how simple it is.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var myPet = { color: 'black', leg_count: 4, communicate: function(repeatCount){
+for(i=0;i&lt;repeatCount;i++) alert('Woof!');} };
+</com:TTextHighlighter>
+
+<p>
+ Let's just add little bit of formatting so it looks more like how we usually find out there:
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var myPet =
+{
+ color: 'black',
+ legCount: 4,
+ communicate: function(repeatCount)
+ {
+ for(i=0;i&lt;repeatCount;i++)
+ alert('Woof!');
+ }
+};
+</com:TTextHighlighter>
+<p>
+ Here we created a reference to an object with two properties (<tt>color</tt>
+ and <tt>legCount</tt>) and a method (<tt>communicate</tt>.)
+ It's not hard to figure out that the object's properties and methods
+ are defined as a comma delimited list. Each of the members is introduced by name, followed
+ by a colon and then the definition. In the case of the properties it is easy, just the value
+ of the property. The methods are created by assigning an anonymous function, which we will
+ explain better down the line.
+ After the object is created and assigned to the variable <tt>myPet</tt>,
+ we can use it like this:
+</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+alert('my pet is ' + myPet.color);
+alert('my pet has ' + myPet.legCount + ' legs');
+//if you are a dog, bark three times:
+myPet.communicate(3);
+</com:TTextHighlighter>
+<p>
+ You'll see JSON used pretty much everywhere in JS these days, as arguments to functions,
+ as return values, as server responses (in strings,) etc.
+</p>
+
+<h2 id="6504">What do you mean? A function is an object too?</h2>
+<p>
+ This might be unusual to developers that never thought about that, but in JS a function is
+ also an object. You can pass a function around as an argument to another function just like
+ you can pass a string, for example. This is extensively used and very handy.
+</p>
+
+<p>
+ Take a look at this example. We will pass functions to another function that will use them.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var myDog =
+{
+ bark: function()
+ {
+ alert('Woof!');
+ }
+};
+
+var myCat =
+{
+ meow: function()
+ {
+ alert('I am a lazy cat. I will not meow for you.');
+ }
+};
+
+function annoyThePet(petFunction)
+{
+ //let's see what the pet can do
+ petFunction();
+}
+
+//annoy the dog:
+annoyThePet(myDog.bark);
+//annoy the cat:
+annoyThePet(myCat.meow);
+</com:TTextHighlighter>
+<p>
+ Note that we pass myDog.bark and myCat.meow without appending parenthesis
+ <tt>"()"</tt> to them. If we did that we would not be passing
+ the function, rather we would be calling the method and passing the return value,
+ <tt>undefined</tt> in both cases here.
+</p>
+
+<p>
+ If you want to make my lazy cat start barking, you can easily do this:
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+myCat.meow = myDog.bark;
+myCat.meow(); //alerts 'Woof!'
+</com:TTextHighlighter>
+
+<h2 id="6505">Arrays, items, and object members</h2>
+<p>
+ The following two lines in JS do the same thing.
+</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var a = new Array();
+var b = [];
+</com:TTextHighlighter>
+<p>
+ As I'm sure you already know, you can access individual items in an array
+ by using the square brackets:
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var a = ['first', 'second', 'third'];
+var v1 = a[0];
+var v2 = a[1];
+var v3 = a[2];
+</com:TTextHighlighter>
+<p>
+
+ But you are not limited to numeric indices. You can access any member of a JS
+ object by using its name, in a string. The following example creates an empty
+ object, and adds some members by name.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var obj = {}; //new, empty object
+obj['member_1'] = 'this is the member value';
+obj['flag_2'] = false;
+obj['some_function'] = function(){ /* do something */};
+</com:TTextHighlighter>
+<p>
+ The above code has identical effect as the following:
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var obj =
+{
+ member_1:'this is the member value',
+ flag_2: false,
+ some_function: function(){ /* do something */}
+};
+</com:TTextHighlighter>
+
+<p>
+ In many ways, the idea of objects and associative arrays (hashes) in JS are not
+ distiguishable. The following two lines do the same thing too.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+obj.some_function();
+obj['some_function']();
+</com:TTextHighlighter>
+
+
+<h2 id="6506">Enough about objects, may I have a class now?</h2>
+<p>
+
+ The great power of object oriented programming languages derive from the use
+ of classes. I don't think I would have guessed how classes are defined in JS
+ using only my previous experience with other languages. Judge for yourself.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+//defining a new class called Pet
+var Pet = function(petName, age)
+{
+ this.name = petName;
+ this.age = age;
+};
+
+//let's create an object of the Pet class
+var famousDog = new Pet('Santa\'s Little Helper', 15);
+alert('This pet is called ' + famousDog.name);
+</com:TTextHighlighter>
+<p>
+ Let's see how we add a method to our <tt>Pet</tt> class. We will be using the
+ <tt>prototype</tt> property that all classes have. The <tt>prototype</tt>
+ property is an object that contains all the members that any object of the class will have.
+ Even the default JS classes, like <tt>String</tt>, <tt>Number</tt>,
+ and <tt>Date</tt> have a <tt>prototype</tt> object that we
+ can add methods and properties to and make any object of that class automatically gain this new member.
+</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+Pet.prototype.communicate = function()
+{
+ alert('I do not know what I should say, but my name is ' + this.name);
+};
+</com:TTextHighlighter>
+<p>
+ That's when a library like <a href="http://www.sergiopereira.com/articles/prototype.js.html">prototype.js</a> comes in
+ handy. If we are using prototype.js, we can make our code look cleaner (at least in my opinion.)
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var Pet = Class.create();
+Pet.prototype =
+{
+ //our 'constructor'
+ initialize: function(petName, age)
+ {
+ this.name = petName;
+ this.age = age;
+ },
+
+ communicate: function()
+ {
+ alert('I do not know what I should say, but my name is ' + this.name);
+ }
+};
+</com:TTextHighlighter>
+
+<h2 id="6507">Functions as arguments, an interesting pattern</h2>
+<p>
+ If you have never worked with languages that support closures
+ you may find the following idiom too funky.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var myArray = ['first', 'second', 'third'];
+myArray.each( function(item, index)
+{
+ alert('The item in the position #' + index + ' is:' + item);
+});
+</com:TTextHighlighter>
+<p>
+
+ Whoa! Let's explain what is going on here before you decide I've gone too
+ far and navigate to a better article than this one.
+</p>
+<p>
+ First of all, in the above example we are using the prototype.js library, which
+ adds the each function to the Array class. The each function accepts one
+ argument that is a function object. This function, in turn, will be called once
+ for each item in the array, passing two arguments when called, the item and the index
+ for the current item. Let's call this function our iterator function.
+ We could have also written the code like this.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+function myIterator(item, index)
+{
+ alert('The item in the position #' + index + ' is:' + item);
+}
+
+var myArray = ['first', 'second', 'third'];
+myArray.each( myIterator );
+</com:TTextHighlighter>
+<p>
+ But then we would not be doing like all the cool kids in school, right?
+ More seriously, though, this last format is simpler to understand but causes
+ us to jump around in the code looking for the myIterator function. It's nice
+ to have the logic of the iterator function right there in the same place
+ it's called. Also, in this case, we will not need the iterator function anywhere
+ else in our code, so we can transform it into an anonymous function without penalty.
+</p>
+
+<h2 id="6508">This is <tt>this</tt> but sometimes <tt>this</tt> is also that</h2>
+<p>
+
+ One of the most common troubles we have with JS when we start writing our code
+ it the use of the <tt>this</tt> keyword. It could be a real
+ tripwire.
+</p>
+<p>
+ As we mentioned before, a function is also an object in JS, and sometimes we
+ do not notice that we are passing a function around.
+</p>
+<p>
+ Take this code snippet as an example.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+function buttonClicked()
+{
+ alert('button ' + this.id + ' was clicked');
+}
+
+var myButton = document.getElementById('someButtonID');
+var myButton2 = document.getElementById('someOtherButtonID');
+myButton.onclick = buttonClicked;
+myButton2.onclick = buttonClicked;
+</com:TTextHighlighter>
+<p>
+ Because the buttonClicked function is defined outside any object we may tend to
+ think the <tt>this</tt> keyword will contain a reference to
+ the <tt>window</tt> or <tt>document</tt>
+ object (assuming this code is in the middle of an HTML page viewed in a browser.)
+</p>
+
+<p>
+ But when we run this code we see that it works as intended and displays the <tt>id</tt> of
+ the clicked button. What happened here is that we made the onclick method of each button contain the
+ <tt>buttonClicked</tt> object reference, replacing whatever was there before. Now
+ whenever the button is clicked, the browser will execute something similar to the following line.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+myButton.onclick();
+</com:TTextHighlighter>
+<p>
+
+ That isn't so confusing afterall, is it? But see what happens you start having other
+ objects to deal with and you want to act on these object upon events like the button's click.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var myHelper =
+{
+ formFields: [ ],
+ emptyAllFields: function()
+ {
+ for(i=0; i < this.formFields.length; i++)
+ {
+ var elementID = this.formFields[i];
+ var field = document.getElementById(elementID);
+ field.value = '';
+ }
+ }
+};
+
+//tell which form fields we want to work with
+myHelper.formFields.push('txtName');
+myHelper.formFields.push('txtEmail');
+myHelper.formFields.push('txtAddress');
+
+//clearing the text boxes:
+myHelper.emptyAllFields();
+
+var clearButton = document.getElementById('btnClear');
+clearButton.onclick = myHelper.emptyAllFields;
+</com:TTextHighlighter>
+<p>
+ So you think, nice, now I can click the Clear button on my page and those three text boxes
+ will be emptied. Then you try clicking the button only to get a runtime error. The error
+ will be related to (guess what?) the <tt>this</tt> keyword.
+ The problem is that <tt>this.formFields</tt> is not defined if
+ <tt>this</tt> containz a referece to the button, which is
+ precisely what's happening. One quick solution would be to rewrite our last line of code.
+</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+clearButton.onclick = function()
+{
+ myHelper.emptyAllFields();
+};
+</com:TTextHighlighter>
+<p>
+ That way we create a brand new function that calls our helper method within the helper object's context.
+</p>
+</com:TContent> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/Advanced/Scripts1.page b/demos/quickstart/protected/pages/Advanced/Scripts1.page
new file mode 100644
index 00000000..f11a2f9d
--- /dev/null
+++ b/demos/quickstart/protected/pages/Advanced/Scripts1.page
@@ -0,0 +1,561 @@
+<com:TContent ID="body" >
+
+<h1 id="6601">Developer Notes for prototype.js</h1>
+This guide is based on the <a href="http://www.sergiopereira.com/articles/prototype.js.html">
+Developer Notes for prototype.js</a> by Sergio Pereira.
+
+<h2 id="6603">What is that?</h2>
+<p>
+In case you haven't already used it, <a href="http://prototype.conio.net">prototype.js</a> is a
+ JavaScript library written by <a href="http://www.conio.net">Sam Stephenson</a>.
+ This amazingly well thought and well written piece of <b>standards-compliant</b> code takes a lot of
+ the burden associated with creating rich, highly interactive web pages that characterize the Web 2.0 off your back.
+</p>
+
+<p>
+ If you tried to use this library recently, you probably noticed that documentation is not one
+ of its strongest points. As many other developers before me, I got my head around prototype.js by
+ reading the source code and experimenting with it. I thought it would be nice to take notes while
+ I learned and share with everybody else.
+</p>
+<p>
+ As you read the examples and the reference, developers familiar with the Ruby
+ programming language will notice an intentional similarity between Ruby's
+ built-in classes and many of the extensions implemented by this library.
+</p>
+
+
+<h2 id="6604">Using the <tt>$()</tt> function</h2>
+<p>
+ The <tt>$()</tt> function is a handy shortcut to the all-too-frequent <tt>document.getElementById()</tt> function
+ of the DOM. Like the DOM function, this one returns the element that has the id passed as an argument.
+</p>
+
+<p>
+ Unlike the DOM function, though, this one goes further. You can pass more than one id and
+ <tt>$()</tt> will return an <tt>Array</tt> object with
+ all the requested elements. The example below should illustrate this.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+&lt;com:TClientScript UsingClientScripts="prado" /&gt;
+<div id="myDiv">
+ <p>This is a paragraph</p>
+</div>
+
+<div id="myOtherDiv">
+ <p>This is another paragraph</p>
+</div>
+
+<input type="button" value=Test1 onclick="test1();" />
+<input type="button" value=Test2 onclick="test2();" />
+
+<script type="text/javascript">
+/*<![CDATA[*/
+function test1()
+{
+ var d = $('myDiv');
+ alert(d.innerHTML);
+}
+
+function test2()
+{
+ var divs = $('myDiv','myOtherDiv');
+ for(i=0; i<divs.length; i++)
+ {
+ alert(divs[i].innerHTML);
+ }
+}
+/*]]>*/
+</script>
+</com:TTextHighlighter>
+<p>
+ Another nice thing about this function is that you can pass either the <tt>id</tt> string or the element object itself,
+ which makes this function very useful when creating other functions that can also take either form of argument.
+</p>
+
+<h2 id="6605">Using the <tt>$F()</tt> function</h2>
+
+<p>
+ The <tt>$F()</tt> function is a another welcome shortcut. It returns the value of any field input control,
+ like text boxes or drop-down lists. The function can take as argument either the element <tt>id</tt> or the element object itself.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+<input type="text" id="userName" value="Joe Doe" />
+<input type="button" value=Test3 onclick="test3();" />
+
+<script type="text/javascript">
+/*<![CDATA[*/
+function test3()
+{
+ alert($F('userName'));
+}
+/*]]>*/
+</script>
+</com:TTextHighlighter>
+
+<h2 id="6606">Using the <tt>$A()</tt> function</h2>
+
+<p>
+ The <tt>$A()</tt> function converts the single argument it receives
+ into an <tt>Array</tt> object.
+</p>
+<p>
+ This function, combined with the extensions for the Array class,
+ makes it easier to convert or copy any enumerable list into an
+ <tt>Array</tt> object. One suggested use is to convert DOM
+ <tt>NodeLists</tt> into regular arrays, which can be traversed
+ more efficiently. See example below.
+</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+<select id="lstEmployees" size="10" >
+ <option value="5">Buchanan, Steven</option>
+ <option value="8">Callahan, Laura</option>
+ <option value="1">Davolio, Nancy</option>
+</select>
+
+<input type="button" value="Show the options" onclick="showOptions();" />
+
+<script type="text/javascript">
+/*<![CDATA[*/
+function showOptions()
+{
+ var someNodeList = $('lstEmployees').options;
+ var nodes = $A(someNodeList);
+
+ nodes.each(function(node)
+ {
+ alert(node.nodeName + ': ' + node.innerHTML);
+ });
+}
+/*]]>*/
+</script>
+</com:TTextHighlighter>
+
+<h2 id="6607">Using the <tt>$H()</tt> function</h2>
+<p>
+ The <tt>$H()</tt> function converts
+ objects into enumerable Hash objects that
+ resemble associative arrays.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+function testHash()
+{
+ //let's create the object
+ var a =
+ {
+ first: 10,
+ second: 20,
+ third: 30
+ };
+
+ //now transform it into a hash
+ var h = $H(a);
+ alert(h.toQueryString());
+
+ //displays: first=10&amp;second=20&amp;third=30
+}
+</com:TTextHighlighter>
+
+<h2 id="6608">Enumerating... Wow! Damn! Wahoo!</h2>
+<p>
+ We are all familar with for loops. You know, create yourself an array, populate it with
+ elements of the same kind, create a loop control structure (for, foreach, while, repeat, etc,)
+ access each element sequentially, by its numeric index, and do something with the element.
+</p>
+<p>
+ When you come to think about it, almost every time you have an array in your code it
+ means that you'll be using that array in a loop sooner or later. Wouldn't it be nice
+ if the array objects had more functionality to deal with these iterations? Yes, it would,
+ and many programming languages provide such functionality in their arrays or equivalent
+ structures (like collections and lists.)
+</p>
+
+<p>
+ Well, it turns out that prototype.js gives us the Enumerable
+ object, which implements a plethora of tricks for us to use when dealing with iterable data.
+ The prototype.js library goes one step further and extends the
+ <tt>Array</tt> class with all the methods of <tt>Enumerable</tt>.
+</p>
+
+<a name="Loops"></a>
+<h3 id="6617">Loops and iterator</h3>
+<p>
+ In standard javascript, if you wanted to sequentially display the elements of an array,
+ you could very well write something like this.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+<script type="text/javascript">
+/*<![CDATA[*/
+function showList()
+{
+ var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Meg'];
+ for(i=0; i < simpsons.length; i++)
+ {
+ alert(simpsons[i]);
+ }
+}
+/*]]>*/
+</script>
+
+<input type="button" value="Show List" onclick="showList();" />
+</com:TTextHighlighter>
+<p>
+ With our new best friend, prototype.js, we can rewrite this loop like this.
+</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+function showList()
+{
+ var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Meg'];
+ simpsons.each( function(familyMember)
+ {
+ alert(familyMember);
+ });
+}
+</com:TTextHighlighter>
+<p>
+ You are probably thinking "big freaking deal...just a weird syntax for the same old thing."
+ Well, in the above example, yes, there's nothing too earth shattering going on. Afterall,
+ there's not much to be changed in such a drop-dead-simple example. But
+ keep reading, nonetheless.
+</p>
+<p>
+ Before we move on. Do you see this function that is being passed as an argument
+ to the <tt>each</tt> method? Let's start referring to it as an
+ <b>iterator</b> function.
+</p>
+
+<h3 id="6618">Your arrays on steroids</h3>
+
+<p>
+ Like we mentioned above, it's very common for all the elements in your array to be of
+ the same kind, with the same properties and methods. Let's see how we can take advantage
+ of iterator functions with our new souped-up arrays.
+</p>
+<p>
+ Finding an element according to a criteria.
+<p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+<script type="text/javascript">
+/*<![CDATA[*/
+function findEmployeeById(emp_id)
+{
+ var listBox = $('lstEmployees')
+ var options = $A(listBox.options);
+ var opt = options.find( function(employee)
+ {
+ return (employee.value == emp_id);
+ });
+
+ alert(opt.innerHTML); //displays the employee name
+}
+/*]]>*/
+</script>
+
+<select id="lstEmployees" size="10" >
+ <option value="5">Buchanan, Steven</option>
+ <option value="8">Callahan, Laura</option>
+ <option value="1">Davolio, Nancy</option>
+</select>
+
+<input type="button" value="Find Laura" onclick="findEmployeeById(8);" />
+</com:TTextHighlighter>
+
+<p>
+ Now let's kick it up another notch. See how we can filter out
+ items in arrays, then retrieve just a desired member from each
+ element.
+<p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+<script type="text/javascript">
+/*<![CDATA[*/
+function showLocalLinks(paragraph)
+{
+ paragraph = $(paragraph);
+ var links = $A(paragraph.getElementsByTagName('a'));
+
+ //find links that do not start with 'http'
+ var localLinks = links.findAll( function(link)
+ {
+ var start = link.href.substring(0,4);
+ return start !='http';
+ });
+
+ //now the link texts
+ var texts = localLinks.pluck('innerHTML');
+
+ //get them in a single string
+ var result = texts.inspect();
+ alert(result);
+}
+/*]]>*/
+</script>
+
+<p id="someText">
+ This &lt;a href="http://othersite.com/page.html"&gt;text&lt;/a&gt; has
+ a &lt;a href="#localAnchor"&gt;lot&lt;/a&gt; of
+ &lt;a href="#otherAnchor"&gt;links&lt;/a&gt;. Some are
+ &lt;a href="http://wherever.com/page.html"&gt;external&lt;/a&gt;
+ and some are &lt;a href="#someAnchor"&gt;local&lt;/a&gt;
+</p>
+<input type=button value="Find Local Links"
+ onclick="showLocalLinks('someText')" />
+</com:TTextHighlighter>
+<p>
+ It takes just a little bit of practice to get completely addicted to this syntax.
+ Next we will go through the available functions with the following example.
+</p>
+<h1 id="6602">Enumerable Functions</h1>
+The sample data for the following examples.
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var Fixtures =
+{
+ Products:
+ [
+ {name: 'Basecamp', company: '37signals', type: 'Project Management'},
+ {name: 'Shopify', company: 'JadedPixel', type: 'E-Commerce'},
+ {name: 'Mint', company: 'Shaun Inman',type: 'Statistics'}
+ ],
+
+ Artist:
+ [
+ 'As I Lay Dying',
+ '36 Crazyfist',
+ 'Shadows Fall',
+ 'Trivium',
+ 'In Flames'
+ ],
+
+ Numbers: [0, 1, 4, 5, 98, 32, 12, 9]
+};
+
+var F = Fixtures;
+</com:TTextHighlighter>
+
+<h2 id="6609"><tt>Enumerable.each</tt> function</h2>
+<p>I used to find myself writing a lot of for loops. Although,
+Prototype doesn’t by any means eliminate the need to do for loops,
+it does give you access to what I consider to be a cleaner, easier to read method in each.
+<com:TTextHighlighter Language="javascript" CssClass="source">
+for(var i = 0; i < F.Numbers.length; i++)
+{
+ Logger.info(F.Numbers[i]);
+}
+</com:TTextHighlighter>
+<p>
+The <tt>each</tt> function allows us to iterate over these objects Ruby style.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+F.Numbers.each(function(num)
+{
+ Logger.info(num);
+});
+
+//Output
+0
+1
+4
+5
+98
+32
+12
+9
+</com:TTextHighlighter>
+
+<p>The <tt>each</tt> function takes one argument, an <b>iterator</b> function.
+This iterator is invoked once for every item in the array, and that item
+along with the optional index is passed to the iterator. So if
+we also needed the index we could do something like the code below.
+</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+F.Numbers.each(function(num, index)
+{
+ Logger.info(index + ": " + num);
+});
+
+//Output
+0: 0
+1: 1
+2: 4
+3: 5
+4: 98
+5: 32
+6: 12
+7: 9
+</com:TTextHighlighter>
+
+<h2 id="6610">Hash key/value pairs</h2>
+<p>Hashes can be created by wrapping an Object (associative array) in
+<tt>$H()</tt> and can have their key/value pairs exposed.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+$H(F.Products[0]).each(function(product)
+{
+ Logger.info(product.key + ": " + product.value);
+});
+
+//Outputs
+name: Basecamp
+company: 37signals
+type: Project Management
+</com:TTextHighlighter>
+<p>
+We can also directly access the keys and values of a Hash without iterating over it.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+$H(F.Products[1]).keys();
+//Outputs name,company,type
+
+$H(F.Products[1]).values();
+//Outputs Shopify,JadedPixel,E-Commerce
+</com:TTextHighlighter>
+
+<h2 id="6611"><tt>Enumerable.collect</tt> function</h2>
+
+<p>The <tt>collect</tt> function allows you to iterate over an <tt>Array</tt> and return the
+results as a new array. Each item returned as a result of the iteration will be
+pushed onto the end of the new array.</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var companies = F.Products.collect(function(product)
+{
+ return product.company;
+});
+
+Logger.info(companies.join(', '));
+
+// Outputs
+// 37signals, JadedPixel, Shaun Inman
+</com:TTextHighlighter>
+
+<p>You can even join on the end of the block.</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+return F.Products.collect(function(product)
+{
+ return product.company;
+}).join(', ');
+</com:TTextHighlighter>
+
+<h2 id="6612"><tt>Enumerable.include</tt> function</h2>
+
+<p>The <tt>include</tt> function allows you to check if a value is included in an array
+and returns true or false depending on if a match was made. Assuming I put
+up a form asking the user to name some artist in my iTunes playlist,
+we could do something like the code below. Prime candidate for some conditional madness.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+return F.Artists.include('Britney Spears'); // returns false
+</com:TTextHighlighter>
+
+<h2 id="6613"><tt>Enumerable.inject</tt> function</h2>
+
+<p>The <tt>inject</tt> function is good for getting a collective sum from an array of
+values. For instance, to add up all the numbers.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var score = F.Numbers.inject(0, function(sum, value)
+{
+ return sum + value;
+});
+
+Logger.info(score);
+//Output 161
+</com:TTextHighlighter>
+
+<p>The first argument to <tt>inject</tt> is just an initial value that
+would be added to the sum, so if we added 1 instead of 0, the output would be 162.</p>
+
+<h2 id="6614"><tt>Enumerable.findAll</tt> function</h2>
+<p>
+When given an Array, the <tt>findAll</tt> function will return an array of
+items for which the iterator evaluated to true. Basically, it allows you to
+build a new array of values based on some search criteria.
+If we wanted to find all products whose type was “E-Commerce”
+we could do something like the code below.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var ecom = F.Products.findAll(function(product)
+{
+ return product.type == 'E-Commerce';
+});
+
+Logger.info(ecom[0].company + " produces " + ecom[0].name);
+
+//Outputs
+JadedPixel produces Shopify
+</com:TTextHighlighter>
+
+<p>Note that even if only one match is made, just as in this case,
+the result is still returned as an array. In that case,
+<tt>ecom.company</tt> would return <tt>undefined</tt>.</p>
+
+<h2 id="6615"><tt>Enumerable.detect</tt> function</h2>
+<p>Unlike the <tt>findAll</tt> function, the <tt>detect</tt> function will only
+return the first item for which the expression inside
+the iterator is true. So, if we wanted to find the first number that
+was greater than 5 we’d do something like the code below.
+</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+var low = F.Numbers.detect(function(num)
+{
+ return num > 5
+});
+
+Logger.info(low);
+//Outputs 98
+</com:TTextHighlighter>
+
+<p>Even though, there are other numbers above 5 in our array, detect
+only gives us the first match back.</p>
+
+<h2 id="6616"><tt>Enumerable.invoke</tt> function</h2>
+
+<p>The <tt>invoke</tt> function allows us to pass a method as a string and
+have that method invoked. For instance, if we wanted to sort
+our array of artists we’d do something like this:</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+[F.Artists].invoke('sort')
+//Outputs 36 Crazyfist,As I Lay Dying,In Flames,Shadows Fall,Trivium
+</com:TTextHighlighter>
+
+<p>Why not just use <tt>F.Artists.sort</tt>? Well, for the example above
+we could do just that, but here is where <tt>invoke</tt> shines.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+[F.Artists, F.Letters].invoke('sort');
+//Outputs 36 Crazyfist,As I Lay Dying,In Flames,...
+</com:TTextHighlighter>
+<p>So we invoked sort for each sub-array. Note that the code below will not work.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+F.Artists.invoke('sort');
+</com:TTextHighlighter>
+
+<p>The reason this will not work is because it is taking each item
+in that array and trying to apply sort to it, thus if we wrote it outright,
+it would look something like this:</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+"36 Crazy Fists".sort();
+</com:TTextHighlighter>
+<p>We could however do something like this:</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+F.Artists.invoke('toLowerCase');
+//Outputs 36 crazyfist,as i lay dying,in flames,shadows ...
+</com:TTextHighlighter>
+
+<p>
+Now, what about passing arguments to the <tt>invoke</tt> function?
+The first argument passed to <tt>invoke</tt> is the method to be invoked,
+and any other arguments beyond that will be passed as arguments to the invoked method.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+F.Artists.invoke('concat', " is awesome ")
+//Outputs
+36 Crazyfist is awesome ,As I Lay Dying is awesome ,...
+</com:TTextHighlighter>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/Advanced/Scripts2.page b/demos/quickstart/protected/pages/Advanced/Scripts2.page
new file mode 100644
index 00000000..6ee6a5d8
--- /dev/null
+++ b/demos/quickstart/protected/pages/Advanced/Scripts2.page
@@ -0,0 +1,253 @@
+<com:TContent ID="body" >
+<h1 id="6701">DOM Events and Javascript</h1>
+
+<h2 id="6702">Basic event handling</h2>
+
+<p>The syntax for working with events looks like the code below.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+Event.observe(element, name, observer, [useCapture]);
+</com:TTextHighlighter>
+
+<p>Assuming for a moment that we want to observe when a link was clicked,
+we could do the following:</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+// &lt;a id="clicker" href="http://foo.com"&gt;Click me&lt;/a&gt;
+Event.observe('clicker', 'click', function(event)
+{
+ alert('clicked!');
+});
+</com:TTextHighlighter>
+
+<p>If we wanted to get the element that fired the event, we'd do this:</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+Event.observe('clicker', 'click', function(event)
+{
+ alert(Event.element(event));
+});
+</com:TTextHighlighter>
+
+<h2 id="6703">Observing keystrokes</h2>
+
+<p>If we wanted to observe keystrokes for the entire document, we could do the following:</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+Event.observe(document, 'keypress', function(event)
+{
+ if(Event.keyCode(event) == Event.KEY_TAB)
+ alert('Tab Pressed');
+});
+</com:TTextHighlighter>
+
+<p>And lets say we wanted to keep track of what has been typed :</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+Event.observe('search', 'keypress', function(event)
+{
+ Element.update('search-results', $F(Event.element(event)));
+});
+</com:TTextHighlighter>
+
+<p>Prototype defines properties inside the event object for some
+of the more common keys, so feel free to dig around in Prototype to
+see which ones those are.</p>
+
+<p>A final note on keypress events; If you'd like to detect a
+left click you can use <tt>Event.isLeftClick(event)</tt>.</p>
+
+<h2 id="6704">Getting the coordinates of the mouse pointer</h2>
+
+<p>Drag and drop, dynamic element resizing, games, and
+much more all require the ability to track the X and Y location of
+the mouse. Prototype makes this fairly simple. The code below tracks
+the X and Y position of the mouse and spits out those values into
+an input box named mouse.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+Event.observe(document, 'mousemove', function(event)
+{
+ $('mouse').value = "X: " + Event.pointerX(event) +
+ "px Y: " + Event.pointerY(event) + "px";
+});
+</com:TTextHighlighter>
+
+<p>If we wanted to observe the mouse location when it was
+hovering over a certain element, we'd just change the document argument to
+the id or element that was relevant.</p>
+
+<h2 id="6705">Stopping Propagation</h2>
+
+<p><tt>Event.stop(event)</tt> will stop the propagation of an event .</p>
+
+<h2 id="6706">Events, Binding, and Objects</h2>
+
+<p>Everything has been fairly straight forward so far, but things
+start getting a little tricker when you need to work with events in
+and object-oriented environment. You have to deal with binding and funky
+looking syntax that might take a moment to get your head around.</p>
+
+<p>Lets look at some code so you can get a better understanding of what I'm talking about.</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+EventDispenser = Class.create();
+EventDispenser.prototype =
+{
+ initialize: function(list)
+ {
+ this.list = list;
+
+ // Observe clicks on our list items
+ $$(this.list + " li").each(function(item)
+ {
+ Event.observe(item, 'click', this.showTagName.bindEvent(this));
+ }.bind(this));
+
+ // Observe when a key on the keyboard is pressed.
+ // In the observer, we check for
+ // the tab key and alert a message if it is pressed.
+ Event.observe(document, 'keypress', this.onKeyPress.bindEvent(this));
+
+ // Observe our fake live search box. When a user types
+ // something into the box, the observer will take that
+ // value(-1) and update our search-results div with it.
+ Event.observe('search', 'keypress', this.onSearch.bindEvent(this));
+
+ Event.observe(document, 'mousemove', this.onMouseMove.bindEvent(this));
+ },
+
+ // Arbitrary functions to respond to events
+ showTagName: function(event)
+ {
+ alert(Event.element(event).tagName);
+ },
+
+ onKeyPress: function(event)
+ {
+ var code = event.keyCode;
+ if(code == Event.KEY_TAB)
+ alert('Tab key was pressed');
+ },
+
+ onSearch: function(event)
+ {
+ Element.update('search-results', $F(Event.element(event)));
+ },
+
+ onMouseMove: function(event)
+ {
+ $('mouse').value = "X: " + Event.pointerX(event) +
+ "px Y: " + Event.pointerY(event) + "px";
+ }
+}
+</com:TTextHighlighter>
+<p>Whoa! What's going on here? Well, we've defined our a
+custom class <tt>EventDispenser</tt>. We're going to be using this class
+to setup events for our document. Most of this code is a
+rewrite of the code we looked at earlier except this time, we
+are working from inside an object.</p>
+
+<p>Looking at the <tt>initialize</tt> method, we can really see how
+things are different now. Take a look at the code below:</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+// Observe clicks on our list items
+$$(this.list + " li").each(function(item)
+{
+ Event.observe(item, 'click', this.showTagName.bindEvent(this));
+}.bind(this));
+</com:TTextHighlighter>
+
+<p>We've got iterators, binding and all sorts of stuff going on.
+Lets break down what this chunk of code is doing.</p>
+
+<p>First we are hunting for a collection of elements based on
+it's Css selector. This uses the Prototype selector function <tt>$$()</tt>.
+After we've found the list items we are dealing with we send
+those into an each iteration where we will add our observers.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+Event.observe(item, 'click', this.showTagName.bindEvent(this));
+</com:TTextHighlighter>
+
+<p>Now looking at the code above, you'll notice the <tt>bindEvent</tt> function.
+This takes the method before it <tt>showTagName</tt> and treats it as the
+method that will be triggered when, in this case,
+someone clicks one of our list items.</p>
+
+<p>You'll also notice we pass this as an argument to the <tt>bindEvent</tt> function.
+This simply allows us to reference the object in context <tt>EventDispenser</tt>
+inside our function <tt>showTagName(event)</tt>. If the <tt>showTagName</tt> function
+requires additional parameters, you can attach them to the later parameters of <tt>bindEvent</tt>. For example</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+this.showTagName.bindEvent(this, param1, param2);
+
+//where the showTagName function is defined as
+showTime : function (event, param1, param2) { ... }
+</com:TTextHighlighter>
+
+<p>Moving on, you'll see <tt>bind(this)</tt> attached to our iterator function.
+This really has nothing to do with events, it is only here to allow me to
+use <tt>this</tt> inside the iterator. If we didn't use <tt>bind(this)</tt>, I couldn't
+reference the method <tt>showTagName</tt> inside the iterator.</p>
+
+<p>Ok, so we'll move on to looking at our methods that actually get
+called when an event occurs. Since we've been dealing with <tt>showTagName</tt>, lets look at it.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+showTagName: function(event)
+{
+ alert(Event.element(event).tagName);
+}
+</com:TTextHighlighter>
+
+<p>As you can see, this function accepts one argument--the event.
+In order for us to get the element which fired the event we need to
+pass that argument to <tt>Event.element</tt>. Now we can manipulate it at will.</p>
+
+<p>This covers the most confusing parts of our code. The text above is also
+relevant to the remaining parts of our code. If there is anything about
+this you don't understand, feel free to ask questions in the forum.</p>
+
+<h2 id="6707">Removing Event Listeners</h2>
+
+<p>This one threw me for a loop the first time I tried to use it.
+I tried something similar to what I did in the <tt>Event.observe</tt>
+call with the exception of using <tt>stopObserving</tt>, but nothing seemed
+to change. In other words, the code below does <b>NOT</b> work.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+$$(this.list + " li").each(function(item)
+{
+ Event.stopObserving(item, 'click', this.showTagName);
+}.bind(this));
+</com:TTextHighlighter>
+
+<p>What's the deal here? The reason this doesn't work is because there
+is no pointer to the observer. This means that when we passed <tt>this.showTagName</tt>
+in the <tt>Event.observe</tt> method before hand, we passed it as an
+anonymous function. We can't reference an anonymous function
+because it simply doesn't have a pointer.</p>
+
+<p>So how do we get the job done? All we need to do is give the
+observing function a pointer, or the jargon free version: Set a variable
+that points to <tt>this.showTagName</tt>. Ok, lets change our code a bit.</p>
+
+<com:TTextHighlighter Language="javascript" CssClass="source">
+this.showTagObserver = this.showTagName.bindEvent(this);
+
+// Observe clicks on our list items
+$$(this.list + " li").each(function(item)
+{
+ Event.observe(item, 'click', this.showTagObserver);
+}.bind(this));
+</com:TTextHighlighter>
+
+<p>Now we can remove the event listeners from our list like this:</p>
+<com:TTextHighlighter Language="javascript" CssClass="source">
+$$(this.list + " li").each(function(item)
+{
+ Event.stopObserving(item, 'click', this.showTagObserver);
+}.bind(this));
+</com:TTextHighlighter>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/Advanced/Scripts3.page b/demos/quickstart/protected/pages/Advanced/Scripts3.page
new file mode 100644
index 00000000..9883f43e
--- /dev/null
+++ b/demos/quickstart/protected/pages/Advanced/Scripts3.page
@@ -0,0 +1,32 @@
+<com:TContent ID="body" >
+<h1 id="6801">Javascript in Prado, Questions and Answers</h1>
+<h2 id="6802">How do I include the predefined javascript libraries?</h2>
+<ul><li>Adding libraries in the template
+<com:TTextHighlighter Language="prado" CssClass="source">
+&lt;com:TClientScript UsingPradoScripts="effects" /&gt;
+</com:TTextHighlighter>
+ </li>
+ <li>Adding libraries in PHP code
+ <com:TTextHighlighter Language="php" CssClass="source">
+$this->getPage()->getClientScript()->registerPradoScript("effects");
+ </com:TTextHighlighter>
+ </li>
+</ul>
+The available packaged libraries included in Prado are
+<ul>
+ <li><tt>prado</tt> : basic prado javascript framework based on Prototype</li>
+ <li><tt>effects</tt> : visual effects from script.aculo.us</li>
+ <li><tt>ajax</tt> : ajax and callback related based on Prototype</li>
+ <li><tt>validator</tt> : validation</li>
+ <li><tt>logger</tt> : javascript logger and object browser</li>
+ <li><tt>datepicker</tt> : datepicker</li>
+ <li><tt>rico</tt> : Rico library</li>
+ <li><tt>colorpicker</tt> : colorpicker</li>
+</ul>
+
+<p>The dependencies for each library are automatically resolved. Components
+that require a particular library will also automatically load the necessary libraries.
+For example, if you add a TDatePicker component on the page, the <tt>datapicker</tt>
+and its dependencies will be automatically included on the page.</p>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/Advanced/Security.page b/demos/quickstart/protected/pages/Advanced/Security.page
index b6de7251..9be7946a 100644
--- a/demos/quickstart/protected/pages/Advanced/Security.page
+++ b/demos/quickstart/protected/pages/Advanced/Security.page
@@ -1,8 +1,8 @@
<com:TContent ID="body" >
-<h1>Security</h1>
+<h1 id="5601">Security</h1>
-<h2>Viewstate Protection</h2>
+<h2 id="5602">Viewstate Protection</h2>
<p>
Viewstate lies at the heart of PRADO. Viewstate represents data that can be used to restore pages to the state that is last seen by end users before making the current request. By default, PRADO uses hidden fields to store viewstate information.
</p>
@@ -26,7 +26,7 @@ HMAC check requires a private key that should be secret to end users. Developers
HMAC check does not prevent end users from reading the viewstate content. An added security measure is to encrypt the viewstate information so that end users cannot decipher it. To enable viewstate encryption, set the <tt>EnableStateEncryption</tt> of pages to true. This can be done in <a href="?page=Configurations.PageConfig">page configurations</a> or in page code. Note, encrypting viewstate may degrade the application performance. A better strategy is to store viewstate on the server side, rather than the default hidden field.
</p>
-<h2>Cross Site Scripting Prevention</h2>
+<h2 id="5603">Cross Site Scripting Prevention</h2>
<p>
Cross site scripting (also known as XSS) occurs when a web application gathers malicious data from a user. Often attackers will inject JavaScript, VBScript, ActiveX, HTML, or Flash into a vulnerable application to fool other application users and gather data from them. For example, a poorly design forum system may display user input in forum posts without any checking. An attacker can then inject a piece of malicious JavaScript code into a post so that when other users read this post, the JavaScript runs unexpectedly on their computers.
</p>
@@ -37,7 +37,7 @@ One of the most important measures to prevent XSS attacks is to check user input
PRADO incorporates the work of <a href="http://pixel-apes.com/safehtml/">SafeHTML</a> and provides developers with a useful component called <tt>TSafeHtml</tt>. By enclosing content within a <tt>TSafeHtml</tt> component tag, the enclosed content are ensured to be safe to end users. In addition, the commonly used <tt>TTextBox</tt> has a <tt>SafeText</tt> property which contains user input that are ensured to be safe if displayed directly to end users.
</p>
-<h2>Cookie Attack Prevention</h2>
+<h2 id="5604">Cookie Attack Prevention</h2>
<p>
Protecting cookies from being attacked is of extreme important, as session IDs are commonly stored in cookies. If one gets hold of a session ID, he essentially owns all relevant session information.
</p>
diff --git a/demos/quickstart/protected/pages/Advanced/State.page b/demos/quickstart/protected/pages/Advanced/State.page
index 8d69d6a4..051090e9 100644
--- a/demos/quickstart/protected/pages/Advanced/State.page
+++ b/demos/quickstart/protected/pages/Advanced/State.page
@@ -1,11 +1,11 @@
<com:TContent ID="body" >
-<h1>Persistent State</h1>
+<h1 id="6001">Persistent State</h1>
<p>
Web applications often need to remember what an end user has done in previous page requests so that the new page request can be served accordingly. State persistence is to address this problem. Traditionally, if a page needs to keep track of user interactions, it will resort to session, cookie, or hidden fields. PRADO provides a new line of state persistence schemes, including view state, control state, and application state.
</p>
-<h2>View State</h2>
+<h2 id="6002">View State</h2>
<p>
View state lies at the heart of PRADO. With view state, Web pages become stateful and are capable of restoring pages to the state that end users interacted with before the current page request. Web programming thus resembles to Windows GUI programming, and developers can think continuously without worrying about the roundtrips between end users and the Web server. For example, with view state, a textbox control is able to detect if the user input changes the content in the textbox.
</p>
@@ -22,7 +22,7 @@ where <tt>$this</tt> refers to the control object, <tt>Caption</tt> is a unique
$caption = $this->getViewState('Caption');
</com:TTextHighlighter>
-<h2>Control State</h2>
+<h2 id="6003">Control State</h2>
<p>
Control state is like view state in every aspect except that control state cannot be disabled. Control state is intended to be used for storing crucial state information without which a page or control may not work properly.
</p>
@@ -34,7 +34,7 @@ $this->setControlState('Caption',$caption);
$caption = $this->getControlState('Caption');
</com:TTextHighlighter>
-<h2>Application State</h2>
+<h2 id="6004">Application State</h2>
<p>
Application state refers to data that is persistent across user sessions and page requests. A typical example of application state is the user visit counter. The counter value is persistent even if the current user session terminates. Note, view state and control state are lost if the user requests for a different page, while session state is lost if the user session terminates.
</p>
@@ -46,7 +46,7 @@ $application->setGlobalState('Caption',$caption);
$caption = $application->getGlobalState('Caption');
</com:TTextHighlighter>
-<h2>Session State</h2>
+<h2 id="6005">Session State</h2>
<p>
PRADO encapsulates the traditional session management in <tt>THttpSession</tt> module. The module can be accessed from within any component by using <tt>$this->Session</tt>, where <tt>$this</tt> refers to the component object.
</p>
diff --git a/demos/quickstart/protected/pages/Advanced/Themes.page b/demos/quickstart/protected/pages/Advanced/Themes.page
index ba5585e7..ef593c65 100644
--- a/demos/quickstart/protected/pages/Advanced/Themes.page
+++ b/demos/quickstart/protected/pages/Advanced/Themes.page
@@ -1,18 +1,18 @@
<com:TContent ID="body" >
-<h1>Themes and Skins</h1>
+<h1 id="5901">Themes and Skins</h1>
-<h2>Introduction</h2>
+<h2 id="5902">Introduction</h2>
<p>
Themes in Prado provide a way for developers to provide a consistent look-and-feel across an entire web application. A theme contains a list of initial values for properties of various control types. When applying a theme to a page, all controls on that page will receive the corresponding initial property values from the theme. This allows themes to interact with the rich property sets of the various PRADO controls, meaning that themes can be used to specify a large range of presentational properties that other theming methods (e.g. CSS) cannot. For example, themes could be used to specify the default page size of all data grids across an application by specifying a default value for the <tt>PageSize</tt> property of the <tt>TDataGrid</tt> control.
</p>
-<h2>Understanding Themes</h2>
+<h2 id="5903">Understanding Themes</h2>
<p>
A theme is a directory consists of skin files, javascript files and CSS files. Any javascript or CSS files contained in a theme will be registered with the page that the theme is applied to. A skin is a set of initial property values for a particular control type. A control type may have one or several skins, each identified by a unqiue <tt>SkinID</tt>. When applying a theme to a page, a skin is applied to a control if the control type and the <tt>SkinID</tt> value both match to those of the skin. Note, if a skin has an empty <tt>SkinID</tt> value, it will apply to all controls of the particular type whose <tt>SkinID</tt> is not set or empty. A skin file consists of one or several skins, for one or several control types. A theme is the union of skins defined in all skin files.
</p>
-<h2>Using Themes</h2>
+<h2 id="5904">Using Themes</h2>
<p>
To use a theme, you need to set the <tt>Theme</tt> property of the page with the theme name, which is the theme directory name. You may set it in either <a href="?page=Configurations.PageConfig">page configurations</a> or in the constructor or <tt>onPreInit()</tt> method of the page. You cannot set the property after <tt>onPreInit()</tt> because by that time, child controls of the page are already created (skins must be applied to controls right after they are created.)
</p>
@@ -29,7 +29,7 @@ This will apply the 'Blue' skin to the button. Note, the initial property values
To use the javascript files and CSS files contained in a theme, a <tt>THead</tt> control must be placed on the page template. This is because the theme will register those files with the page and <tt>THead</tt> is the right place to load those files.
</p>
-<h2>Theme Storage</h2>
+<h2 id="5905">Theme Storage</h2>
<p>
All themes by default must be placed under the <tt>[AppEntryPath]/themes</tt> directory, where <tt>AppEntryPath</tt> refers to the directory containing the application entry script. If you want to use a different directory, configure the <tt>BasePath</tt> and <tt>BaseUrl</tt> properties of the <tt>System.Web.UI.TThemeManager</tt> module in application configuration,
</p>
@@ -44,7 +44,7 @@ All themes by default must be placed under the <tt>[AppEntryPath]/themes</tt> di
&lt;/service&gt;
</com:TTextHighlighter>
-<h2>Creating Themes</h2>
+<h2 id="5906">Creating Themes</h2>
<p>
Creating a theme involves creating the theme directory and writing skin files (and possibly javascript and CSS files). The name of skin files must be terminated with <tt>.skin</tt>. The format of skin files are the same as that of control template files. Since skin files do not define parent-child presentational relationship among controls, you cannot place a component tag within another. And any static texts between component tags are discarded. To define the aforementioned 'Blue' skin for <tt>TButton</tt>, write the following in a skin file,
</p>
diff --git a/demos/quickstart/protected/pages/Configurations/AppConfig.page b/demos/quickstart/protected/pages/Configurations/AppConfig.page
index 48243200..e15f1eea 100644
--- a/demos/quickstart/protected/pages/Configurations/AppConfig.page
+++ b/demos/quickstart/protected/pages/Configurations/AppConfig.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Application Configurations</h1>
+<h1 id="1801">Application Configurations</h1>
<p>
Application configurations are used to specify the global behavior of an application. They include specification of path aliases, namespace usages, module and service configurations, and parameters.
</p>
diff --git a/demos/quickstart/protected/pages/Configurations/Overview.page b/demos/quickstart/protected/pages/Configurations/Overview.page
index 4a23f306..ec606941 100644
--- a/demos/quickstart/protected/pages/Configurations/Overview.page
+++ b/demos/quickstart/protected/pages/Configurations/Overview.page
@@ -1,5 +1,5 @@
<com:TContent ID="body" >
-<h1>Configuration Overview</h1>
+<h1 id="1401">Configuration Overview</h1>
<p>
PRADO uses configurations to glue together components into pages and applications. There are <a href="?page=Configurations.AppConfig">application configurations</a>, <a href="?page=Configurations.PageConfig">page configurations</a>, and <a href="?page=Configurations.Templates1">templates</a>.
</p>
diff --git a/demos/quickstart/protected/pages/Configurations/PageConfig.page b/demos/quickstart/protected/pages/Configurations/PageConfig.page
index 01710d59..b0ef5ccb 100644
--- a/demos/quickstart/protected/pages/Configurations/PageConfig.page
+++ b/demos/quickstart/protected/pages/Configurations/PageConfig.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Page Configurations</h1>
+<h1 id="1901">Page Configurations</h1>
<p>
Page configurations are mainly used by <tt>TPageService</tt> to modify or append the application configuration. As the name indicates, a page configuration is associated with a directory storing some page files. It is stored as an XML file named <tt>config.xml</tt>.
</p>
diff --git a/demos/quickstart/protected/pages/Configurations/Templates1.page b/demos/quickstart/protected/pages/Configurations/Templates1.page
index 483ef1d1..3f2fcc5e 100644
--- a/demos/quickstart/protected/pages/Configurations/Templates1.page
+++ b/demos/quickstart/protected/pages/Configurations/Templates1.page
@@ -1,5 +1,5 @@
<com:TContent ID="body" >
-<h1>Templates: Part I</h1>
+<h1 id="1501">Templates: Part I</h1>
<p>
Templates are used to specify the presentational layout of controls. A template can contain static text, components, or controls that contribute to the ultimate presentation of the associated control. By default, an instance of <tt>TTemplateControl</tt> or its subclass may automatically load and instantiate a template from a file whose name is the same as the control class name. For page templates, the file name suffix must be <tt>.page</tt>; for other regular template controls, the suffix is <tt>.tpl</tt>.
</p>
@@ -7,7 +7,7 @@ Templates are used to specify the presentational layout of controls. A template
</p>
<a name="ct"></a>
-<h2>Component Tags</h2>
+<h2 id="1502">Component Tags</h2>
<p>
A component tag specifies a component as part of the body content of the template control. If the component is a control, it usually will become a child or grand child of the template control, and its rendering result will be inserted at the place where it is appearing in the template.
</p>
@@ -43,7 +43,7 @@ PropertyValue
It is equivalent to <tt>...PropertyName="PropertyValue"...</tt> in every aspect. Property initialization tags must be directly enclosed between the corresponding opening and closing component tag.
</p>
-<h3>Component IDs</h3>
+<h3 id="1505">Component IDs</h3>
<p>
When specified in templates, component <tt>ID</tt> property has special meaning in addition to its normal property definition. A component tag specified with an ID value in template will register the corresponding component to the template owner control. The component can thus be directly accessed from the template control with its ID value. For example, in <tt>Home</tt> page's template, the following component tag
<com:TTextHighlighter Language="prado" CssClass="source">
@@ -53,7 +53,7 @@ makes it possible to get the textbox object in code using <tt>$page->TextBox</tt
</p>
<a name="tct"></a>
-<h2>Template Control Tags</h2>
+<h2 id="1503">Template Control Tags</h2>
A template control tag is used to configure the initial property values of the control owning the template. Its format is as follows,
<com:TTextHighlighter Language="prado" CssClass="source">
&lt;%@ PropertyName="PropertyValue" ... %&gt;
@@ -68,7 +68,7 @@ Template control tag is optional in a template. Each template can contain at mos
</p>
<a name="cot"></a>
-<h2>Comment Tags</h2>
+<h2 id="1504">Comment Tags</h2>
<p>
Comment tags are used to put comments in the template or the ultimate rendering result. There are two types of comment tags. One is like that in HTML and will be displayed to the end-users. The other only appear in a template and will be stripped out when the template is instantiated and displayed to the end-users. The format of these two comment tags is as follows,
</p>
diff --git a/demos/quickstart/protected/pages/Configurations/Templates2.page b/demos/quickstart/protected/pages/Configurations/Templates2.page
index 17502151..201c526f 100644
--- a/demos/quickstart/protected/pages/Configurations/Templates2.page
+++ b/demos/quickstart/protected/pages/Configurations/Templates2.page
@@ -1,14 +1,14 @@
<com:TContent ID="body" >
-<h1>Templates: Part II</h1>
+<h1 id="1601">Templates: Part II</h1>
<a name="dct"></a>
-<h2>Dynamic Content Tags</h2>
+<h2 id="1602">Dynamic Content Tags</h2>
<p>
Dynamic content tags are introduced as shortcuts to some commonly used <a href="?page=Configurations.Templates1#ct">component tags</a>. These tags are mainly used to render contents resulted from evaluating some PHP expressions or statements. They include <a href="#et">expression tags</a>, <a href="#st">statement tags</a>, <a href="#dt">databind tags</a>, <a href="#pt">parameter tags</a>, <a href="#at">asset tags</a> and <a href="#lot">localization tags</a>.
</p>
<a name="et"></a>
-<h3>Expression Tags</h3>
+<h3 id="1603">Expression Tags</h3>
<p>
An expression tag represents a PHP expression that is evaluated when the template control is in <tt>PreRender</tt> stage. The expression evaluation result is inserted at the place where the tag resides in the template. The context (namely <tt>$this</tt>) of the expression is the control owning the template.
</p>
@@ -26,7 +26,7 @@ For example, the following expression tag will display the current page title at
</com:TTextHighlighter>
<a name="st"></a>
-<h3>Statement Tags</h3>
+<h3 id="1604">Statement Tags</h3>
<p>
Statement tags are similar to expression tags, except that statement tags contain PHP statements rather than expressions. The output of the PHP statements (using for example <tt>echo</tt> or <tt>print</tt> in PHP) are displayed at the place where the statement tag resides in the template. The context (namely <tt>$this</tt>) of the statements is the control owning the template. The format of statement tags is as follows,
</p>
@@ -46,7 +46,7 @@ echo strftime("%A %e %B %Y",time());
</com:TTextHighlighter>
<a name="dt"></a>
-<h3>Databind Tags</h3>
+<h3 id="1605">Databind Tags</h3>
<p>
Databind tags are similar to expression tags, except that the expressions are evaluated only when a <tt>dataBind()</tt> call is invoked on the controls representing the databind tags. The context (namely <tt>$this</tt>) of a databind expression is the control owning the template. The format of databind tags is as follows,
</p>
@@ -55,7 +55,7 @@ Databind tags are similar to expression tags, except that the expressions are ev
</com:TTextHighlighter>
<a name="pt"></a>
-<h3>Parameter Tags</h3>
+<h3 id="1606">Parameter Tags</h3>
<p>
Parameter tags are used to insert application parameters at the place where they appear in the template. The format of parameter tags is as follows,
</p>
@@ -67,7 +67,7 @@ Note, application parameters are usually defined in application configurations o
</p>
<a name="at"></a>
-<h3>Asset Tags</h3>
+<h3 id="1607">Asset Tags</h3>
<p>
Asset tags are used to publish private files and display the corresponding the URLs. For example, if you have an image file that is not Web-accessible and you want to make it visible to end-users, you can use asset tags to publish this file and show the URL to end-users so that they can fetch the published image.
</p>
@@ -85,7 +85,7 @@ BE VERY CAUTIOUS when you are using asset tags as it may expose to end-users fil
</p>
<a name="lot"></a>
-<h3>Localization Tags</h3>
+<h3 id="1608">Localization Tags</h3>
<p>
Localization tags represent localized texts. They are in the following format,
</p>
diff --git a/demos/quickstart/protected/pages/Configurations/Templates3.page b/demos/quickstart/protected/pages/Configurations/Templates3.page
index 457c1a38..99d43665 100644
--- a/demos/quickstart/protected/pages/Configurations/Templates3.page
+++ b/demos/quickstart/protected/pages/Configurations/Templates3.page
@@ -1,8 +1,8 @@
<com:TContent ID="body" >
-<h1>Templates: Part III</h1>
+<h1 id="1701">Templates: Part III</h1>
<a name="dpt"></a>
-<h2>Dynamic Property Tags</h2>
+<h2 id="1702">Dynamic Property Tags</h2>
<p>
Dynamic property tags are very similar to dynamic content tags, except that they are applied to component properties. The purpose of dynamic property tags is to allow more versatile component property configuration. Note, you are not required to use dynamic property tags because what can be done using dynamic property tags can also be done in PHP code. However, using dynamic property tags bring you much more convenience at accomplishing the same tasks. The basic usage of dynamic property tags is as follows,
</p>
@@ -19,7 +19,7 @@ Like dynamic content tags, we have <a href="#et">expression tags</a>, <a href="#
</p>
<a name="et"></a>
-<h3>Expression Tags</h3>
+<h3 id="1703">Expression Tags</h3>
<p>
An expression tag represents a PHP expression that is evaluated when the control is in <tt>PreRender</tt> stage. The expression evaluation result is assigned to the corresponding component property. The format of expression tags is as follows,
</p>
@@ -34,7 +34,7 @@ In the expression, <tt>$this</tt> refers to the control owning the template. The
</com:TTextHighlighter>
<a name="dt"></a>
-<h3>Databind Tags</h3>
+<h3 id="1704">Databind Tags</h3>
<p>
Databind tags are similar to expression tags, except that they can only be used with control properties and the expressions are evaluated only when a <tt>dataBind()</tt> call is invoked on the controls represented by the component tags. In the expression, <tt>$this</tt> refers to the control owning the template. Databind tags do not apply to all components. They can only be used for controls.
</p>
@@ -46,7 +46,7 @@ The format of databind tags is as follows,
</com:TTextHighlighter>
<a name="pt"></a>
-<h3>Parameter Tags</h3>
+<h3 id="1705">Parameter Tags</h3>
<p>
Parameter tags are used to assign application parameter values to the corresponding component properties. The format of parameter tags is as follows,
</p>
@@ -58,7 +58,7 @@ Note, application parameters are usually defined in application configurations o
</p>
<a name="at"></a>
-<h3>Asset Tags</h3>
+<h3 id="1706">Asset Tags</h3>
<p>
Asset tags are used to publish private files and assign the corresponding the URLs to the component properties. For example, if you have an image file that is not Web-accessible and you want to make it visible to end-users, you can use asset tags to publish this file and show the URL to end-users so that they can fetch the published image. The asset tags are evaluated when the template is instantiated.
</p>
@@ -76,7 +76,7 @@ BE VERY CAUTIOUS when you are using asset tags as it may expose to end-users fil
</p>
<a name="lot"></a>
-<h3>Localization Tags</h3>
+<h3 id="1707">Localization Tags</h3>
<p>
Localization tags represent localized texts. They are in the following format,
</p>
diff --git a/demos/quickstart/protected/pages/Controls/Button.page b/demos/quickstart/protected/pages/Controls/Button.page
index e58571d3..5c827725 100644
--- a/demos/quickstart/protected/pages/Controls/Button.page
+++ b/demos/quickstart/protected/pages/Controls/Button.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TButton</h1>
+<h1 id="2001">TButton</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TButton" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/CheckBox.page b/demos/quickstart/protected/pages/Controls/CheckBox.page
index 77052997..7f2767c0 100644
--- a/demos/quickstart/protected/pages/Controls/CheckBox.page
+++ b/demos/quickstart/protected/pages/Controls/CheckBox.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TCheckBox</h1>
+<h1 id="2101">TCheckBox</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TCheckBox" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/ColorPicker.page b/demos/quickstart/protected/pages/Controls/ColorPicker.page
index 6e05584a..8909ad98 100644
--- a/demos/quickstart/protected/pages/Controls/ColorPicker.page
+++ b/demos/quickstart/protected/pages/Controls/ColorPicker.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TColorPicker</h1>
+<h1 id="2201">TColorPicker</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TColorPicker" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/Data.page b/demos/quickstart/protected/pages/Controls/Data.page
index 36839582..6a93ec6f 100644
--- a/demos/quickstart/protected/pages/Controls/Data.page
+++ b/demos/quickstart/protected/pages/Controls/Data.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Data Controls</h1>
+<h1 id="5001">Data Controls</h1>
<ul>
<li>
diff --git a/demos/quickstart/protected/pages/Controls/DataGrid.page b/demos/quickstart/protected/pages/Controls/DataGrid.page
index de6a94e9..ed1ad32b 100644
--- a/demos/quickstart/protected/pages/Controls/DataGrid.page
+++ b/demos/quickstart/protected/pages/Controls/DataGrid.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TDataGrid</h1>
+<h1 id="5201">TDataGrid</h1>
<p>
TDatagrid is an important control in building complex Web applications. It displays data in a tabular format with rows (also called items) and columns. A row is composed by cells, while columns govern how cells should be displayed according to their association with the columns. Data specified via <tt>DataSource</tt> or <tt>DataSourceID</tt> are bound to the rows and feed contents to cells.
@@ -12,7 +12,7 @@ TDataGrid is highly interactive. Users can sort the data along specified columns
Rows of TDataGrid can be accessed via its <tt>Items</tt> property. A row (item) can be in one of several modes: browsing, editting and selecting, which affects how cells in the row are displayed. To change an item's mode, modify <tt>EditItemIndex</tt> or <tt>SelectedItemIndex</tt>. Note, if an item is in edit mode, then selecting this item will have no effect.
</p>
-<h2>Columns</h2>
+<h2 id="5202">Columns</h2>
<p>
Columns of a data grid determine how the associated cells are displayed. For example, cells associated with a <tt>TBoundColumn</tt> are displayed differently according to their modes. A cell is displayed as a static text if the cell is in browsing mode, a text box if it is in editting mode, and so on.
</p>
@@ -28,7 +28,7 @@ PRADO provides five types of columns:
<li><tt>TTemplateColumn</tt> displays the cells according to different templates defined for it.</li>
</ul>
-<h2>Item Styles</h2>
+<h2 id="5203">Item Styles</h2>
<p>
TDataGrid defines different styles applied to its items. For example, <tt>AlternatingItemStyle</tt> is applied to alternating items (item 2, 4, 6, etc.) Through these properties, one can set CSS style fields or CSS classes for the items.
</p>
@@ -36,7 +36,7 @@ TDataGrid defines different styles applied to its items. For example, <tt>Altern
Item styles are applied in a hierarchical way. Styles in higher hierarchy will inherit from styles in lower hierarchy. Starting from the lowest hierarchy, the item styles include item's own style, <tt>ItemStyle</tt>, <tt>AlternatingItemStyle</tt>, <tt>SelectedItemStyle</tt>, and <tt>EditItemStyle</tt>. Therefore, if background color is set as red in <tt>ItemStyle</tt>, <tt>EditItemStyle</tt> will also have red background color, unless it is explicitly set to a different value.
</p>
-<h2>Events</h2>
+<h2 id="5204">Events</h2>
<p>
TDataGrid provides several events to facilitate manipulation of its items,
</p>
@@ -57,9 +57,9 @@ TDataGrid provides several events to facilitate manipulation of its items,
</li>
</ul>
-<h2>Using TDataGrid</h2>
+<h2 id="5205">Using TDataGrid</h2>
-<h3>Automatically Generated Columns</h3>
+<h3 id="5210">Automatically Generated Columns</h3>
<p>
TDataGrid by default will create a list of columns based on the structure of the bound data. TDataGrid will read the first row of the data, extract the field names of the row, and construct a column for each field. Each column is of type <tt>TBoundColumn</tt>.
</p>
@@ -77,7 +77,7 @@ public function onLoad($param) {
</com:TTextHighlighter>
<com:RunBar PagePath="Controls.Samples.TDataGrid.Sample1" />
-<h3>Manually Specified Columns</h3>
+<h3 id="5211">Manually Specified Columns</h3>
<p>
Using automatically generated columns gives a quick way of browsing tabular data. In real applications, however, automatically generated columns are often not sufficient because developers have no way customizing their appearance. Manually specified columns are thus more desirable.
</p>
@@ -109,7 +109,7 @@ The following example uses manually specified columns to show a list of book inf
<com:RunBar PagePath="Controls.Samples.TDataGrid.Sample2" />
-<h2>Interacting with TDataGrid</h2>
+<h2 id="5206">Interacting with TDataGrid</h2>
<p>
Besides the rich data presentation functionalities as demonstrated in previous section, TDataGrid is also highly user interactive. An import usage of TDataGrid is editting or deleting rows of data. The <tt>TBoundColumn</tt> can adjust the associated cell presentation according to the mode of datagrid items. When an item is in browsing mode, the cell is displayed with a static text; when the item is in editting mode, a textbox is displayed to collect user inputs. TDataGrid provides <tt>TEditCommandColumn</tt> for switching item modes. In addition, <tt>TButtonColumn</tt> offers developers the flexibility of creating arbitrary buttons for various user interactions.
</p>
@@ -118,7 +118,7 @@ The following example shows how to make the previous book information table an i
</p>
<com:RunBar PagePath="Controls.Samples.TDataGrid.Sample3" />
-<h2>Sorting</h2>
+<h2 id="5207">Sorting</h2>
<p>
TDataGrid supports sorting its items according to specific columns. To enable sorting, set <tt>AllowSorting</tt> to true. This will turn column headers into clickable buttons if their <tt>SortExpression</tt> property is not empty. When users click on the header buttons, an <tt>OnSortCommand</tt> event will be raised. Developers can write handlers to respond to the sort command and sort the data according to <tt>SortExpression</tt> which is specified in the corresponding column.
</p>
@@ -127,7 +127,7 @@ The following example turns the datagrid in <a href="?page=Controls.Samples.TDat
</p>
<com:RunBar PagePath="Controls.Samples.TDataGrid.Sample4" />
-<h2>Paging</h2>
+<h2 id="5208">Paging</h2>
<p>
When dealing with large datasets, paging is helpful in reducing the page size and complexity. TDataGrid has an embedded pager that allows users to specify which page of data they want to see. The pager can be customized via <tt>PagerStyle</tt>. For example, <tt>PagerStyle.Visible</tt> determines whether the pager is visible or not; <tt>PagerStyle.Position</tt> indicates where the pager is displayed; and <tt>PagerStyle.Mode</tt> specifies what type of pager is displayed, a numeric one or a next-prev one.
</p>
@@ -146,7 +146,7 @@ The following example enables the paging functionality of the datagrid shown in
</p>
<com:RunBar PagePath="Controls.Samples.TDataGrid.Sample5" />
-<h3>Custom Paging</h3>
+<h3 id="5212">Custom Paging</h3>
<p>
The paging functionality shown above requires loading all data into memory, even though only a portion of them is displayed in a page. For large datasets, this is inefficient and may not always be feasible. TDataGrid provides custom paging to solve this problem. Custom paging only requires the portion of the data to be displayed to end users.
</p>
@@ -155,7 +155,7 @@ To enable custom paging, set both <tt>AllowPaging</tt> and <tt>AllowCustomPaging
</p>
<com:RunBar PagePath="Controls.Samples.TDataGrid.Sample6" />
-<h2>Extending TDataGrid</h2>
+<h2 id="5209">Extending TDataGrid</h2>
<p>
Besides traditional class inheritance, extensibility of TDataGrid is mainly through developing new datagrid column components. For example, one may want to display an image column. He may use <tt>TTemplateColumn</tt> to accomplish this task. A better solution is to develop an image column component so that the work can be reused easily in other projects.
</p>
diff --git a/demos/quickstart/protected/pages/Controls/DataList.page b/demos/quickstart/protected/pages/Controls/DataList.page
index 9d904870..c1ff1147 100644
--- a/demos/quickstart/protected/pages/Controls/DataList.page
+++ b/demos/quickstart/protected/pages/Controls/DataList.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TDataList</h1>
+<h1 id="5101">TDataList</h1>
<p>
TDataList is used to display or modify a list of data items specified by its <tt>DataSource</tt> or <tt>DataSourceID</tt> property. Each data item is displayed by a data list item which is a child control of the data list. The <tt>Items</tt> property contains the list of all data list items.
</p>
diff --git a/demos/quickstart/protected/pages/Controls/DatePicker.page b/demos/quickstart/protected/pages/Controls/DatePicker.page
index 17d28382..a37ff048 100644
--- a/demos/quickstart/protected/pages/Controls/DatePicker.page
+++ b/demos/quickstart/protected/pages/Controls/DatePicker.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TDatePicker</h1>
+<h1 id="2301">TDatePicker</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TDatePicker" />
<p><tt>TDatePicker</tt> displays a text box for date input purpose.
diff --git a/demos/quickstart/protected/pages/Controls/Expression.page b/demos/quickstart/protected/pages/Controls/Expression.page
index 80a45a3a..044808c6 100644
--- a/demos/quickstart/protected/pages/Controls/Expression.page
+++ b/demos/quickstart/protected/pages/Controls/Expression.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TExpression</h1>
+<h1 id="2401">TExpression</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TExpression" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/FileUpload.page b/demos/quickstart/protected/pages/Controls/FileUpload.page
index 8aa5a0fb..404a144e 100644
--- a/demos/quickstart/protected/pages/Controls/FileUpload.page
+++ b/demos/quickstart/protected/pages/Controls/FileUpload.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TFileUpload</h1>
+<h1 id="2501">TFileUpload</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TFileUpload" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/Head.page b/demos/quickstart/protected/pages/Controls/Head.page
index 269ec404..227b5282 100644
--- a/demos/quickstart/protected/pages/Controls/Head.page
+++ b/demos/quickstart/protected/pages/Controls/Head.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>THead</h1>
+<h1 id="2601">THead</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.THead" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/HiddenField.page b/demos/quickstart/protected/pages/Controls/HiddenField.page
index c068d01b..aa2e7c87 100644
--- a/demos/quickstart/protected/pages/Controls/HiddenField.page
+++ b/demos/quickstart/protected/pages/Controls/HiddenField.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>THiddenField</h1>
+<h1 id="2701">THiddenField</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.THiddenField" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/HtmlArea.page b/demos/quickstart/protected/pages/Controls/HtmlArea.page
index 0af685d5..00a65573 100644
--- a/demos/quickstart/protected/pages/Controls/HtmlArea.page
+++ b/demos/quickstart/protected/pages/Controls/HtmlArea.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>THtmlArea</h1>
+<h1 id="2801">THtmlArea</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.THtmlArea" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/HyperLink.page b/demos/quickstart/protected/pages/Controls/HyperLink.page
index afccd041..9fa6bde3 100644
--- a/demos/quickstart/protected/pages/Controls/HyperLink.page
+++ b/demos/quickstart/protected/pages/Controls/HyperLink.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>THyperLink</h1>
+<h1 id="2901">THyperLink</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.THyperLink" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/Image.page b/demos/quickstart/protected/pages/Controls/Image.page
index a926c204..1e8df3de 100644
--- a/demos/quickstart/protected/pages/Controls/Image.page
+++ b/demos/quickstart/protected/pages/Controls/Image.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TImage</h1>
+<h1 id="3201">TImage</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TImage" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/ImageButton.page b/demos/quickstart/protected/pages/Controls/ImageButton.page
index b4a8de93..a79ecb6a 100644
--- a/demos/quickstart/protected/pages/Controls/ImageButton.page
+++ b/demos/quickstart/protected/pages/Controls/ImageButton.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TImageButton</h1>
+<h1 id="3001">TImageButton</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TImageButton" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/ImageMap.page b/demos/quickstart/protected/pages/Controls/ImageMap.page
index 997faad6..002861a3 100644
--- a/demos/quickstart/protected/pages/Controls/ImageMap.page
+++ b/demos/quickstart/protected/pages/Controls/ImageMap.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TImageMap</h1>
+<h1 id="3101">TImageMap</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TImageMap" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/InlineFrame.page b/demos/quickstart/protected/pages/Controls/InlineFrame.page
index 56e3f382..efd5144d 100644
--- a/demos/quickstart/protected/pages/Controls/InlineFrame.page
+++ b/demos/quickstart/protected/pages/Controls/InlineFrame.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TInlineFrame</h1>
+<h1 id="3301">TInlineFrame</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TInlineFrame" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/JavascriptLogger.page b/demos/quickstart/protected/pages/Controls/JavascriptLogger.page
index cff2b7aa..ccb4a27b 100644
--- a/demos/quickstart/protected/pages/Controls/JavascriptLogger.page
+++ b/demos/quickstart/protected/pages/Controls/JavascriptLogger.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TJavascriptLogger</h1>
+<h1 id="3401">TJavascriptLogger</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TJavascriptLogger" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/Label.page b/demos/quickstart/protected/pages/Controls/Label.page
index 69329c8a..b7d5b094 100644
--- a/demos/quickstart/protected/pages/Controls/Label.page
+++ b/demos/quickstart/protected/pages/Controls/Label.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TLabel</h1>
+<h1 id="3501">TLabel</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TLabel" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/LinkButton.page b/demos/quickstart/protected/pages/Controls/LinkButton.page
index eb22f9ce..e0255141 100644
--- a/demos/quickstart/protected/pages/Controls/LinkButton.page
+++ b/demos/quickstart/protected/pages/Controls/LinkButton.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TLinkButton</h1>
+<h1 id="3601">TLinkButton</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TLinkButton" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/List.page b/demos/quickstart/protected/pages/Controls/List.page
index cac07330..b5915693 100644
--- a/demos/quickstart/protected/pages/Controls/List.page
+++ b/demos/quickstart/protected/pages/Controls/List.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>List Controls</h1>
+<h1 id="4801">List Controls</h1>
<p>
List controls covered in this section all inherit directly or indirectly from <tt>TListControl</tt>. Therefore, they share the same set of commonly used properties, including,
</p>
@@ -48,19 +48,19 @@ $listbox->dataBind();
</li>
</ul>
-<h2>TListBox</h2>
+<h2 id="4802">TListBox</h2>
<p>
<tt>TListBox</tt> displays a list box that allows single or multiple selection. Set the property <tt>SelectionMode</tt> as <tt>Single</tt> to make a single selection list box, and <tt>Multiple</tt> a multiple selection list box. The number of rows displayed in the box is specified via the <tt>Rows</tt> property value.
</p>
<com:RunBar PagePath="Controls.Samples.TListBox.Home" />
-<h2>TDropDownList</h2>
+<h2 id="4803">TDropDownList</h2>
<p>
<tt>TDropDownList</tt> displays a dropdown list box that allows users to select a single option from a few prespecified ones.
</p>
<com:RunBar PagePath="Controls.Samples.TDropDownList.Home" />
-<h2>TCheckBoxList</h2>
+<h2 id="4804">TCheckBoxList</h2>
<p>
<tt>TCheckBoxList</tt> displays a list of checkboxes on a Web page. The alignment of the text besides each checkbox can be specified <tt>TextAlign</tt>. The layout of the checkboxes can be controlled by the following properties:
</p>
@@ -72,13 +72,13 @@ $listbox->dataBind();
<com:RunBar PagePath="Controls.Samples.TCheckBoxList.Home" />
-<h2>TRadioButtonList</h2>
+<h2 id="4805">TRadioButtonList</h2>
<p>
<tt>TRadioButtonList</tt> is similar to <tt>TCheckBoxList</tt> in every aspect except that each <tt>TRadioButtonList</tt> displays a group of radiobuttons. Only one of the radiobuttions can be selected (<tt>TCheckBoxList</tt> allows multiple selections.)
</p>
<com:RunBar PagePath="Controls.Samples.TRadioButtonList.Home" />
-<h2>TBulletedList</h2>
+<h2 id="4806">TBulletedList</h2>
<p>
<tt>TBulletedList</tt> displays items in a bullet format on a Web page. The style of the bullets can be specified by <tt>BulletStyle</tt>. When the style is <tt>CustomImage</tt>, the bullets are displayed as images, which is specified by <tt>BulletImageUrl</tt>.
</p>
diff --git a/demos/quickstart/protected/pages/Controls/Literal.page b/demos/quickstart/protected/pages/Controls/Literal.page
index 30a84880..d5d40a13 100644
--- a/demos/quickstart/protected/pages/Controls/Literal.page
+++ b/demos/quickstart/protected/pages/Controls/Literal.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TLiteral</h1>
+<h1 id="3701">TLiteral</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TLiteral" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/MultiView.page b/demos/quickstart/protected/pages/Controls/MultiView.page
index 7e566181..a22711b6 100644
--- a/demos/quickstart/protected/pages/Controls/MultiView.page
+++ b/demos/quickstart/protected/pages/Controls/MultiView.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TMultiView</h1>
+<h1 id="3801">TMultiView</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TMultiView" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/NewControl.page b/demos/quickstart/protected/pages/Controls/NewControl.page
index 51d0cc02..8f4b9be9 100644
--- a/demos/quickstart/protected/pages/Controls/NewControl.page
+++ b/demos/quickstart/protected/pages/Controls/NewControl.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Writing New Controls</h1>
+<h1 id="5401">Writing New Controls</h1>
<p>
Writing new controls is often desired by advanced programmers, because they want to reuse the code that they write for dealing with complex presentation and user interactions.
</p>
@@ -8,7 +8,7 @@ Writing new controls is often desired by advanced programmers, because they want
In general, there are two ways of writing new controls: composition of existing controls and extending existing controls. They all require that the new control inherit from <tt>TControl</tt> or its child classes.
</p>
-<h2>Composition of Existing Controls</h2>
+<h2 id="5402">Composition of Existing Controls</h2>
<p>
Composition is the easiest way of creating new controls. It mainly involves instantiating existing controls, configuring them and making them the constituent components. The properties of the constituent components are exposed through <a href="?page=Fundamentals.Components">subproperties</a>.
</p>
@@ -19,7 +19,7 @@ One can compose a new control in two ways. One is to extend <tt>TCompositeContro
As an example, we show how to create a labeled textbox called <tt>LabeledTextBox</tt> using the above two approaches. A labeled textbox displays a label besides a textbox. We want reuse the PRADO provided <tt>TLabel</tt> and <tt>TTextBox</tt> to accomplish this task.
</p>
-<h3>Composition by Writing Templates</h3>
+<h3 id="5404">Composition by Writing Templates</h3>
<p>
We need two files: a control class file named <tt>LabeledTextBox.php</tt> and a control template file named <tt>LabeledTextBox.tpl</tt>. Both must reside under the same directory.
</p>
@@ -46,7 +46,7 @@ In the above, the method call to <tt>ensureChildControls()</tt> ensures that bot
</p>
<com:RunBar PagePath="Controls.Samples.LabeledTextBox1.Home" />
-<h3>Composition by Overriding <tt>createChildControls()</tt></h3>
+<h3 id="5405">Composition by Overriding <tt>createChildControls()</tt></h3>
<p>
For a composite control as simple as <tt>LabeledTextBox</tt>, it is better to create it by extending <tt>TCompositeControl</tt> and overriding the <tt>createChildControls()</tt> method, because it does not use templates and thus saves template parsing time.
</p>
@@ -80,7 +80,7 @@ class LabeledTextBox extends TCompositeControl {
</com:TTextHighlighter>
<com:RunBar PagePath="Controls.Samples.LabeledTextBox2.Home" />
-<h3>Using <tt>LabeledTextBox</tt></h3>
+<h3 id="5406">Using <tt>LabeledTextBox</tt></h3>
<p>
To use <tt>LabeledTextBox</tt> control, first we need to include the corresponding class file. Then in a page template, we can write lines like the following,
</p>
@@ -91,7 +91,7 @@ To use <tt>LabeledTextBox</tt> control, first we need to include the correspondi
In the above, <tt>Label.Text</tt> is a subproperty of <tt>LabeledTextBox</tt>, which refers to the <tt>Text</tt> property of the <tt>Label</tt> property. For other details of using <tt>LabeledTextBox</tt>, see the above online examples.
</p>
-<h2>Extending Existing Controls</h2>
+<h2 id="5403">Extending Existing Controls</h2>
<p>
Extending existing controls is the same as conventional class inheritance. It allows developers to customize existing control classes by overriding their properties, methods, events, or creating new ones.
</p>
@@ -102,7 +102,7 @@ The difficulty of the task depends on how much an existing class needs to be cus
In this section, we mainly introduce the base control classes <tt>TControl</tt> and <tt>TWebControl</tt>, showing how they can be customized. We also introduce how to write controls with specific functionalities, such as loading post data, raising post data and databinding with data source.
</p>
-<h3>Extending <tt>TControl</tt></h3>
+<h3 id="5407">Extending <tt>TControl</tt></h3>
<p>
<tt>TControl</tt> is the base class of all control classes. Two methods are of the most importance for derived control classes:
</p>
@@ -125,7 +125,7 @@ Other important properties and methods include:
<li>Control lifecycles - Life page lifecycles, controls also have lifecycles. Each control undergoes the following lifecycles in order: constructor, <tt>onInit()</tt>, <tt>onLoad()</tt>, <tt>onPreRender()</tt>, <tt>render()</tt>, and <tt>onUnload</tt>. More details can be found in the <a href="?page=Fundamentals.Pages">page</a> section.</li>
</ul>
-<h3>Extending <tt>TWebControl</tt></h3>
+<h3 id="5408">Extending <tt>TWebControl</tt></h3>
<p>
<tt>TWebControl</tt> is mainly used as a base class for controls representing HTML elements. It provides a set of properties that are common among HTML elements. It breaks the <tt>TControl::render()</tt> into the following methods that are more suitable for rendering an HTML element:
</p>
@@ -139,7 +139,7 @@ Other important properties and methods include:
When rendering the openning HTML tag, <tt>TWebControl</tt> calls <tt>getTagName()</tt> to obtain the tag name. Derived classes may override this method to render different tag names.
</p>
-<h3>Creating Controls with Special Functionalities</h3>
+<h3 id="5409">Creating Controls with Special Functionalities</h3>
<p>
If a control wants to respond to client-side events and translate them into server side events (called postback events), such as <tt>TButton</tt>, it has to implement the <tt>IPostBackEventHandler</tt> interface.
</p>
diff --git a/demos/quickstart/protected/pages/Controls/Panel.page b/demos/quickstart/protected/pages/Controls/Panel.page
index b402cd8f..d507da36 100644
--- a/demos/quickstart/protected/pages/Controls/Panel.page
+++ b/demos/quickstart/protected/pages/Controls/Panel.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TPanel</h1>
+<h1 id="3901">TPanel</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TPanel" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/PlaceHolder.page b/demos/quickstart/protected/pages/Controls/PlaceHolder.page
index 323b1d52..b55d1616 100644
--- a/demos/quickstart/protected/pages/Controls/PlaceHolder.page
+++ b/demos/quickstart/protected/pages/Controls/PlaceHolder.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TPlaceHolder</h1>
+<h1 id="4001">TPlaceHolder</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TPlaceHolder" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/RadioButton.page b/demos/quickstart/protected/pages/Controls/RadioButton.page
index 5e1d9d21..b40f37bd 100644
--- a/demos/quickstart/protected/pages/Controls/RadioButton.page
+++ b/demos/quickstart/protected/pages/Controls/RadioButton.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TRadioButton</h1>
+<h1 id="4101">TRadioButton</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TRadioButton" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/Repeater.page b/demos/quickstart/protected/pages/Controls/Repeater.page
index c40517f3..795e27aa 100644
--- a/demos/quickstart/protected/pages/Controls/Repeater.page
+++ b/demos/quickstart/protected/pages/Controls/Repeater.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TRepeater</h1>
+<h1 id="5301">TRepeater</h1>
<p>
TRepeater displays its content defined in templates repeatedly based on the given data specified by the <tt>DataSource</tt> or <tt>DataSourceID</tt> property. The repeated contents can be retrieved from the <tt>Items</tt> property. Each item is created by instantiating a template and each is a child control of the repeater.
</p>
diff --git a/demos/quickstart/protected/pages/Controls/SafeHtml.page b/demos/quickstart/protected/pages/Controls/SafeHtml.page
index 565c8251..995ceb50 100644
--- a/demos/quickstart/protected/pages/Controls/SafeHtml.page
+++ b/demos/quickstart/protected/pages/Controls/SafeHtml.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TSafeHtml</h1>
+<h1 id="4201">TSafeHtml</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TSafeHtml" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/Statements.page b/demos/quickstart/protected/pages/Controls/Statements.page
index dd26ec1b..3f3bd1fc 100644
--- a/demos/quickstart/protected/pages/Controls/Statements.page
+++ b/demos/quickstart/protected/pages/Controls/Statements.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TStatements</h1>
+<h1 id="4301">TStatements</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TStatements" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/Table.page b/demos/quickstart/protected/pages/Controls/Table.page
index 75fa3fc1..e0f5586e 100644
--- a/demos/quickstart/protected/pages/Controls/Table.page
+++ b/demos/quickstart/protected/pages/Controls/Table.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TTable</h1>
+<h1 id="4401">TTable</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TTable" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/TextBox.page b/demos/quickstart/protected/pages/Controls/TextBox.page
index 64cb826a..b4d1576f 100644
--- a/demos/quickstart/protected/pages/Controls/TextBox.page
+++ b/demos/quickstart/protected/pages/Controls/TextBox.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TTextBox</h1>
+<h1 id="4501">TTextBox</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TTextBox" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/TextHighlighter.page b/demos/quickstart/protected/pages/Controls/TextHighlighter.page
index 40806b02..285a6f3e 100644
--- a/demos/quickstart/protected/pages/Controls/TextHighlighter.page
+++ b/demos/quickstart/protected/pages/Controls/TextHighlighter.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>TTextHighlighter</h1>
+<h1 id="4601">TTextHighlighter</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TTextHighlighter" />
<p>
diff --git a/demos/quickstart/protected/pages/Controls/Validation.page b/demos/quickstart/protected/pages/Controls/Validation.page
index 6d83af3d..a28c8faf 100644
--- a/demos/quickstart/protected/pages/Controls/Validation.page
+++ b/demos/quickstart/protected/pages/Controls/Validation.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Validation Controls</h1>
+<h1 id="4901">Validation Controls</h1>
<p>
Validation controls, called validators, perform validation on user-entered data values when they are post back to the server. The validation is triggered by a postback control, such as a <tt>TButton</tt>, a <tt>TLinkButton</tt> or a <tt>TTextBox</tt> (under <tt>AutoPostBack</tt> mode) whose <tt>CausesValidation</tt> property is true.
@@ -32,14 +32,14 @@ Validators share a common set of properties, which are defined in the base class
</ul>
<a name="TRequiredFieldValidator"></a>
-<h2>TRequiredFieldValidator</h2>
+<h2 id="4902">TRequiredFieldValidator</h2>
<p>
TRequiredFieldValidator ensures that the user enters some data in the specified input field. By default, TRequiredFieldValidator will check if the user input is empty or not. The validation fails if the input is empty. By setting <tt>InitialValue</tt>, the validator can check if the user input is different from <tt>InitialValue</tt>. If not, the validation fails.
</p>
<com:RunBar PagePath="Controls.Samples.TRequiredFieldValidator.Home" />
<a name="TRegularExpressionValidator"></a>
-<h2>TRegularExpressionValidator</h2>
+<h2 id="4903">TRegularExpressionValidator</h2>
<p>
TRegularExpressionValidator verifies the user input against a regular pattern. The validation fails if the input does not match the pattern. The regular expression can be specified by the <tt>RegularExpression</tt> property. Some commonly used regular expressions include:
</p>
@@ -73,7 +73,7 @@ Note, if the input being validated is empty, TEmailAddressValidator will not do
<com:RunBar PagePath="Controls.Samples.TEmailAddressValidator.Home" />
<a name="TCompareValidator"></a>
-<h2>TCompareValidator</h2>
+<h2 id="4904">TCompareValidator</h2>
<p>
TCompareValidator compares the user input with a constant value specified by <tt>ValueToCompare</tt>, or another user input specified by <tt>ControlToCompare</tt>. The <tt>Operator</tt> property specifies how to compare the values, which includes <tt>Equal</tt>, <tt>NotEqual</tt>, <tt>GreaterThan</tt>, <tt>GreaterThanEqual</tt>, <tt>LessThan</tt> and <tt>LessThanEqual</tt>. Before comparison, the values being compared will be converted to the type specified by <tt>DataType</tt> listed as follows,
</p>
@@ -92,7 +92,7 @@ Note, if the input being validated is empty, TEmailAddressValidator will not do
<com:RunBar PagePath="Controls.Samples.TCompareValidator.Home" />
<a name="TDataTypeValidator"></a>
-<h2>TDataTypeValidator</h2>
+<h2 id="4905">TDataTypeValidator</h2>
<p>
TDataTypeValidator verifies if the input data is of specific type indicated by <tt>DataType</tt>. The data types that can be checked against are the same as those in TCompareValidator.
</p>
@@ -104,7 +104,7 @@ TDataTypeValidator verifies if the input data is of specific type indicated by <
<com:RunBar PagePath="Controls.Samples.TDataTypeValidator.Home" />
<a name="TRangeValidator"></a>
-<h2>TRangeValidator</h2>
+<h2 id="4906">TRangeValidator</h2>
<p>
TRangeValidator verifies whether an input value is within a specified range. TRangeValidator uses three key properties to perform its validation. The <tt>MinValue</tt> and <tt>MaxValue</tt> properties specify the minimum and maximum values of the valid range. The <tt>DataType</tt> property specifies the data type of the value being validated. The value will be first converted into the specified type and then compare with the valid range. The data types that can be checked against are the same as those in TCompareValidator.
</p>
@@ -115,7 +115,7 @@ TRangeValidator verifies whether an input value is within a specified range. TRa
<com:RunBar PagePath="Controls.Samples.TRangeValidator.Home" />
<a name="TCustomValidator"></a>
-<h2>TCustomValidator</h2>
+<h2 id="4907">TCustomValidator</h2>
<p>
TCustomValidator performs user-defined validation (either server-side or client-side or both) on an input control.
</p>
@@ -139,7 +139,7 @@ function ValidationFunctionName(sender, parameter)
<com:RunBar PagePath="Controls.Samples.TCustomValidator.Home" />
<a name="TValidationSummary"></a>
-<h2>TValidationSummary</h2>
+<h2 id="4908">TValidationSummary</h2>
<p>
TValidationSummary displays a summary of validation errors inline on a Web page, in a message box, or both.
</p>
diff --git a/demos/quickstart/protected/pages/Controls/Wizard.page b/demos/quickstart/protected/pages/Controls/Wizard.page
index f903e183..ce8bc774 100644
--- a/demos/quickstart/protected/pages/Controls/Wizard.page
+++ b/demos/quickstart/protected/pages/Controls/Wizard.page
@@ -1,9 +1,9 @@
<com:TContent ID="body" >
-<h1>TWizard</h1>
+<h1 id="4701">TWizard</h1>
<com:DocLink ClassPath="System.Web.UI.WebControls.TWizard" />
-<h2>Overview</h2>
+<h2 id="4702">Overview</h2>
<p>
<tt>TWizard</tt> is analogous to the installation wizard commonly used to install software on Windows. It splits a large form and presents the user with a series of smaller forms, called wizard steps, to complete. The following figure shows how a wizard is composed of when presented to users, where <tt>step content</tt> is the main content of a wizard step for users to complete, <tt>header</tt> refers to header content common to all steps, <tt>navigation</tt> contains buttons that allow users to navigate step by step, and <tt>side bar</tt> contains a list of hyperlinks by which users can reach to any step with one click. The visibility of the side bar can be toggled by setting <tt>ShowSideBar</tt>.
</p>
@@ -49,15 +49,15 @@ In the above, <tt>StepType</tt> refers to the type of a wizard step, which can a
</ul>
-<h2>Using TWizard</h2>
+<h2 id="4703">Using TWizard</h2>
-<h3>A Single-Step Wizard Sample</h3>
+<h3 id="4704">A Single-Step Wizard Sample</h3>
<p>
In this sample, we use wizard to collect user's preference of color. In the first step, the user is presented with a dropdown list from which he can choose hist favorite color. In the second step, the complete step, his choice in the previous step is displayed. In real application, at this step the choice may be stored in database in the backend.
</p>
<com:RunBar PagePath="Controls.Samples.TWizard.Sample1" />
-<h3>Customizing Wizard Styles</h3>
+<h3 id="4705">Customizing Wizard Styles</h3>
<p>
<tt>TWizard</tt> defines a whole set of properties for customization of appearance of its various components as shown in the above figure. In particular, the following properties are provided for style customization:
</p>
@@ -69,7 +69,7 @@ In this sample, we use wizard to collect user's preference of color. In the firs
</ul>
<com:RunBar PagePath="Controls.Samples.TWizard.Sample2" />
-<h3>Customizing Wizard Navigation</h3>
+<h3 id="4706">Customizing Wizard Navigation</h3>
<p>
Given a set of wizard steps, <tt>TWizard</tt> supports three different ways of navigation among them:
</p>
@@ -80,7 +80,7 @@ Given a set of wizard steps, <tt>TWizard</tt> supports three different ways of n
</ul>
<com:RunBar PagePath="Controls.Samples.TWizard.Sample3" />
-<h3>Using Templates in Wizard</h3>
+<h3 id="4707">Using Templates in Wizard</h3>
<p>
<tt>TWizard</tt> supports more concrete control of its outlook through templating. In particular, it provides the following template properties that allow complete customization of the wizard's header, navigation and side bar.
</p>
@@ -91,7 +91,7 @@ Given a set of wizard steps, <tt>TWizard</tt> supports three different ways of n
</ul>
<com:RunBar PagePath="Controls.Samples.TWizard.Sample4" />
-<h3>Using Templated Wizard Steps</h3>
+<h3 id="4708">Using Templated Wizard Steps</h3>
<p>
Wizard steps can also be templated. By using <tt>TTemplatedWizardStep</tt>, one can customize step content and navigation through its <tt>ContentTemplate</tt> and <tt>NavigationTemplate</tt> properties, respectively. This is useful for control developers to build specialized wizards, such as user registration, shopping carts, etc.
</p>
diff --git a/demos/quickstart/protected/pages/Fundamentals/Applications.page b/demos/quickstart/protected/pages/Fundamentals/Applications.page
index b33b4f10..48db41e8 100644
--- a/demos/quickstart/protected/pages/Fundamentals/Applications.page
+++ b/demos/quickstart/protected/pages/Fundamentals/Applications.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Applications</h1>
+<h1 id="1201">Applications</h1>
<p>
An application is an instance of <tt>TApplication</tt> or its derived class. It manages modules that provide different functionalities and are loaded when needed. It provides services to end-users. It is the central place to store various parameters used in an application. In a PRADO application, the application instance is the only object that is globally accessible via <tt>Prado::getApplication()</tt> function call.
</p>
@@ -14,7 +14,7 @@ $application->run();
where the method <tt>run()</tt> starts the application to handle user requests.
</p>
-<h2>Directory Organization</h2>
+<h2 id="1202">Directory Organization</h2>
<p>
A minimal PRADO application contains two files: an entry file and a page template file. They must be organized as follows,
</p>
@@ -33,7 +33,7 @@ A minimal PRADO application contains two files: an entry file and a page templat
A product PRADO application usually needs more files. It may include an application configuration file named <tt>application.xml</tt> under the application base path <tt>protected</tt>. The pages may be organized in directories, some of which may contain page configuration files named <tt>config.xml</tt>. Fore more details, please see <a href="?page=Configurations.Overview">configurations</a> section.
</p>
-<h2>Application Deployment</h2>
+<h2 id="1203">Application Deployment</h2>
<p>
Deploying a PRADO application mainly involves copying directories. For example, to deploy the above minimal application to another server, follow the following steps,
</p>
@@ -43,7 +43,7 @@ Deploying a PRADO application mainly involves copying directories. For example,
<li>Remove all content under <tt>assets</tt> and <tt>runtime</tt> directories and make sure both directories are writable by the Web server process.</li>
</ol>
-<h2>Application Lifecycles</h2>
+<h2 id="1204">Application Lifecycles</h2>
<p>
Like page lifecycles, an application also has lifecycles. Application modules can register for the lifecycle events. When the application reaches a particular lifecycle and raises the corresponding event, the registered module methods are invoked automatically. Modules included in the PRADO release, such as <tt>TAuthManager</tt>, are using this way to accomplish their goals.
</p>
diff --git a/demos/quickstart/protected/pages/Fundamentals/Architecture.page b/demos/quickstart/protected/pages/Fundamentals/Architecture.page
index d53b7ee1..5811da71 100644
--- a/demos/quickstart/protected/pages/Fundamentals/Architecture.page
+++ b/demos/quickstart/protected/pages/Fundamentals/Architecture.page
@@ -1,5 +1,5 @@
<com:TContent ID="body" >
-<h1>Architecture</h1>
+<h1 id="601">Architecture</h1>
<p>
PRADO is primarily a presentational framework, although it is not limited to be so. The framework focuses on making Web programming, which deals most of the time with user interactions, to be component-based and event-driven so that developers can be more productive. The following class tree depicts some of the major classes provided by PRADO,
diff --git a/demos/quickstart/protected/pages/Fundamentals/Components.page b/demos/quickstart/protected/pages/Fundamentals/Components.page
index c419a5fa..dc4da67c 100644
--- a/demos/quickstart/protected/pages/Fundamentals/Components.page
+++ b/demos/quickstart/protected/pages/Fundamentals/Components.page
@@ -1,10 +1,10 @@
<com:TContent ID="body" >
-<h1>Components</h1>
+<h1 id="701">Components</h1>
<p>
A component is an instance of <tt>TComponent</tt> or its child class. The base class <tt>TComponent</tt> implements the mechanism of component properties and events.
</p>
-<h2>Component Properties</h2>
+<h2 id="702">Component Properties</h2>
<p>
A component property can be viewed as a public variable describing a specific aspect of the component, such as the background color, the font size, etc. A property is defined by the existence of a getter and/or a setter method in the component class. For example, in <tt>TControl</tt>, we define its <tt>ID</tt> property using the following getter and setter methods,
<com:TTextHighlighter CssClass="source">
@@ -34,7 +34,7 @@ $component->setID( $id );
A property is read-only if it has a getter method but no setter method. Since PHP method names are case-insensitive, property names are also case-insensitive. A component class inherits all its ancestor classes' properties.
</p>
-<h3>Subproperties</h3>
+<h3 id="706">Subproperties</h3>
<p>
A subproperty is a property of some object-typed property. For example, <tt>TWebControl</tt> has a <tt>Font</tt> property which is of <tt>TFont</tt> type. Then the <tt>Name</tt> property of <tt>Font</tt> is referred to as a subproperty (with respect to <tt>TWebControl</tt>).
</p>
@@ -53,7 +53,7 @@ $component-&gt;getFont()-&gt;setName( $name );
</p>
-<h2>Component Events</h2>
+<h2 id="703">Component Events</h2>
<p>
Component events are special properties that take method names as their values. Attaching (setting) a method to an event will hook up the method to the places at which the event is raised. Therefore, the behavior of a component can be modified in a way that may not be foreseen during the development of the component.
</p>
@@ -76,7 +76,7 @@ $button->attachEventHandler( 'OnClick' , $callback );
where <tt>$callback</tt> refers to a valid PHP callback (e.g. a function name, a class method <tt>array($object,'method')</tt>, etc.)
</p>
-<h2>Namespaces</h2>
+<h2 id="704">Namespaces</h2>
<p>
A namespace refers to a logical grouping of some class names so that they can be differentiated from other class names even if their names are the same. Since PHP does not support namespace intrinsically, you cannot create instances of two classes who have the same name but with different definitions. To differentiate from user defined classes, all PRADO classes are prefixed with a letter 'T' (meaning 'Type'). Users are advised not to name their classes like this. Instead, they may prefix their class names with any other letter(s).
</p>
@@ -102,12 +102,12 @@ which will include the class file if <tt>MyClass</tt> is not defined.
For more details about defining path aliases, see <a href="?page=Configurations.AppConfig">application configuration</a> section.
</p>
-<h2>Component Instantiation</h2>
+<h2 id="705">Component Instantiation</h2>
<p>
Component instantiation means creating instances of component classes. There are two types of component instantation: static instantiation and dynamic instantiation. The created components are called static components and dynamic components, respectively.
</p>
-<h3>Dynamic Component Instantiation</h3>
+<h3 id="707">Dynamic Component Instantiation</h3>
<p>
Dynamic component instantiation means creating component instances in PHP code. It is the same as the commonly referred object creation in PHP. A component can be dynamically created using one of the following two methods in PHP,
<com:TTextHighlighter CssClass="source">
@@ -117,7 +117,7 @@ $component = Prado::createComponent('ComponentType');
where <tt>ComponentType</tt> refers to a class name or a type name in namespace format (e.g. <tt>System.Web.UI.TControl</tt>). The second approach is introduced to compensate for the lack of namespace support in PHP.
</p>
-<h3>Static Component Instantiation</h3>
+<h3 id="708">Static Component Instantiation</h3>
<p>
Static component instantiation is about creating components via <a href="?page=Configurations.Overview">configurations</a>. The actual creation work is done by the PRADO framework. For example, in an <a href="?page=Configurations.AppConfig">application configuration</a>, one can configure a module to be loaded when the application runs. The module is thus a static component created by the framework. Static component instantiation is more commonly used in <a href="?page=Configurations.Templates1">templates</a>. Every component tag in a template specifies a component that will be automatically created by the framework when the template is loaded. For example, in a page template, the following tag will lead to the creation of a <tt>TButton</tt> component on the page,
<com:TTextHighlighter Language="prado" CssClass="source">
diff --git a/demos/quickstart/protected/pages/Fundamentals/Controls.page b/demos/quickstart/protected/pages/Fundamentals/Controls.page
index a90dd604..d36bff7a 100644
--- a/demos/quickstart/protected/pages/Fundamentals/Controls.page
+++ b/demos/quickstart/protected/pages/Fundamentals/Controls.page
@@ -1,10 +1,10 @@
<com:TContent ID="body" >
-<h1>Controls</h1>
+<h1 id="801">Controls</h1>
<p>
A control is an instance of class <tt>TControl</tt> or its subclass. A control is a component defined in addition with user interface. The base class <tt>TControl</tt> defines the parent-child relationship among controls which reflects the containment relationship among user interface elements.
</p>
-<h2>Control Tree</h2>
+<h2 id="802">Control Tree</h2>
<p>
Controls are related to each other via parent-child relationship. Each parent control can have one or several child controls. A parent control is in charge of the state transition of its child controls. The rendering result of the child controls are usually used to compose the parent control's presentation. The parent-child relationship brings together controls into a control tree. A page is at the root of the tree, whose presentation is returned to the end-users.
</p>
@@ -17,12 +17,12 @@ $parent->Controls[]=$child;
where the property <tt>Controls</tt> refers to the child control collection of the parent.
</p>
-<h2>Control Identification</h2>
+<h2 id="803">Control Identification</h2>
<p>
Each control has an <tt>ID</tt> property that can be uniquely identify itself among its sibling controls. In addition, each control has a <tt>UniqueID</tt> and a <tt>ClientID</tt> which can be used to globally identify the control in the tree that the control resides in. <tt>UniqueID</tt> and <tt>ClientID</tt> are very similar. The former is used by the framework to determine the location of the corresponding control in the tree, while the latter is mainly used on the client side as HTML tag IDs. In general, you should not rely on the explicit format of <tt>UniqueID</tt> or <tt>ClientID</tt>.
</p>
-<h2>Naming Containers</h2>
+<h2 id="804">Naming Containers</h2>
<p>
Each control has a naming container which is a control creating a unique namespace for differentiating between controls with the same <tt>ID</tt>. For example, a <tt>TRepeater</tt> control creates multiple items each having child controls with the same <tt>ID</tt>s. To differentiate these child controls, each item serves as a naming container. Therefore, a child control may be uniquely identified using its naming container's <tt>ID</tt> together with its own <tt>ID</tt>. As you may already have understood, <tt>UniqueID</tt> and <tt>ClientID</tt> rely on the naming containers.
</p>
@@ -30,7 +30,7 @@ Each control has a naming container which is a control creating a unique namespa
A control can serve as a naming container if it implements the <tt>INamingContainer</tt> interface.
</p>
-<h2>ViewState and ControlState</h2>
+<h2 id="805">ViewState and ControlState</h2>
<p>
HTTP is a stateless protocol, meaning it does not provide functionality to support continuing interaction between a user and a server. Each request is considered as discrete and independent of each other. A Web application, however, often needs to know what a user has done in previous requests. People thus introduce sessions to help remember such state information.
</p>
diff --git a/demos/quickstart/protected/pages/Fundamentals/Hangman.page b/demos/quickstart/protected/pages/Fundamentals/Hangman.page
index d73868a0..56180b9e 100644
--- a/demos/quickstart/protected/pages/Fundamentals/Hangman.page
+++ b/demos/quickstart/protected/pages/Fundamentals/Hangman.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Sample: Hangman Game</h1>
+<h1 id="1301">Sample: Hangman Game</h1>
<p>
Having seen the simple "Hello World" application, we now build a more complex application called "Hangman Game". In this game, the player is asked to guess a word, a letter at a time. If he guesses a letter right, the letter will be shown in the word. The player can continue to guess as long as the number of his misses is within a prespecified bound. The player wins the game if he finds out the word within the miss bound, or he loses.
</p>
diff --git a/demos/quickstart/protected/pages/Fundamentals/Modules.page b/demos/quickstart/protected/pages/Fundamentals/Modules.page
index 782ebb8c..df67f0f3 100644
--- a/demos/quickstart/protected/pages/Fundamentals/Modules.page
+++ b/demos/quickstart/protected/pages/Fundamentals/Modules.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Modules</h1>
+<h1 id="1001">Modules</h1>
<p>
A module is an instance of a class implementing the <tt>IModule</tt> interface. A module is commonly designed to provide specific functionality that may be plugged into a PRADO application and shared by all components in the application.
</p>
@@ -12,31 +12,31 @@ There are three core modules that are loaded by default whenever an application
</p>
<a name="request"></a>
-<h2>Request Module</h2>
+<h2 id="1002">Request Module</h2>
<p>
Request module represents provides storage and access scheme for user request sent via HTTP. User request data comes from several sources, including URL, post data, session data, cookie data, etc. These data can all be accessed via the request module. By default, PRADO uses <tt>THttpRequest</tt> as request module. The request module can be accessed via the <tt>Request</tt> property of application and controls.
</p>
<a name="response"></a>
-<h2>Response Module</h2>
+<h2 id="1003">Response Module</h2>
<p>
Response module implements the mechanism for sending output to client users. Response module may be configured to control how output are cached on the client side. It may also be used to send cookies back to the client side. By default, PRADO uses <tt>THttpResponse</tt> as response module. The response module can be accessed via the <tt>Response</tt> property of application and controls.
</p>
<a name="session"></a>
-<h2>Session Module</h2>
+<h2 id="1004">Session Module</h2>
<p>
Session module encapsulates the functionalities related with user session handling. Session module is automatically loaded when an application uses session. By default, PRADO uses <tt>THttpSession</tt> as session module, which is a simple wrapper of the session functions provided by PHP. The session module can be accessed via the <tt>Session</tt> property of application and controls.
</p>
<a name="error"></a>
-<h2>Error Handler Module</h2>
+<h2 id="1005">Error Handler Module</h2>
<p>
Error handler module is used to capture and process all error conditions in an application. PRADO uses <tt>TErrorHandler</tt> as error handler module. It captures all PHP warnings, notices and exceptions, and displays in an appropriate form to end-users. The error handler module can be accessed via the <tt>ErrorHandler</tt> property of the application instance.
</p>
<a name="custom"></a>
-<h2>Custom Modules</h2>
+<h2 id="1006">Custom Modules</h2>
<p>
PRADO is released with a few more modules besides the core ones. They include caching modules (<tt>TSqliteCache</tt> and <tt>TMemCache</tt>), user management module (<tt>TUserManager</tt>), authentication and authorization module (<tt>TAuthManager</tt>), etc.
</p>
diff --git a/demos/quickstart/protected/pages/Fundamentals/Pages.page b/demos/quickstart/protected/pages/Fundamentals/Pages.page
index 8dfb5caa..1e91801c 100644
--- a/demos/quickstart/protected/pages/Fundamentals/Pages.page
+++ b/demos/quickstart/protected/pages/Fundamentals/Pages.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Pages</h1>
+<h1 id="901">Pages</h1>
<p>
Pages are top-most controls that have no parent. The presentation of pages are directly displayed to end-users. Users access pages by sending page service requests.
</p>
@@ -8,13 +8,13 @@ Pages are top-most controls that have no parent. The presentation of pages are d
Each page must have a <a href="?page=Configurations.Templates1">template</a> file. The file name suffix must be <tt>.page</tt>. The file name (without suffix) is the page name. PRADO will try to locate a page class file under the directory containing the page template file. Such a page class file must have the same file name (suffixed with <tt>.php</tt>) as the template file. If the class file is not found, the page will take class <tt>TPage</tt>.
</p>
-<h2>PostBack</h2>
+<h2 id="902">PostBack</h2>
<p>
A form submission is called <i>postback</i> if the submission is made to the page containing the form. Postback can be considered an event happened on the client side, raised by the user. PRADO will try to identify which control on the server side is responsible for a postback event. If one is determined, for example, a <tt>TButton</tt>, we call it the postback event sender which will translate the postback event into some specific server-side event (e.g. <tt>Click</tt> and <tt>Command</tt> events for <tt>TButton</tt>).
</p>
-<h2>Page Lifecycles</h2>
+<h2 id="903">Page Lifecycles</h2>
<p>
Understanding the page lifecycles is crucial to grasp PRADO programming. Page lifecycles refer to the state transitions of a page when serving this page to end-users. They can be depicted in the following statechart,
<img src="<%~lifecycles.gif %>" />
diff --git a/demos/quickstart/protected/pages/Fundamentals/Services.page b/demos/quickstart/protected/pages/Fundamentals/Services.page
index 22c09c99..920c6bea 100644
--- a/demos/quickstart/protected/pages/Fundamentals/Services.page
+++ b/demos/quickstart/protected/pages/Fundamentals/Services.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Services</h1>
+<h1 id="1101">Services</h1>
<p>
A service is an instance of a class implementing the <tt>IService</tt> interface. Each kind of service processes a specific type of user requests. For example, the page service responds to users' requests for PRADO pages.
</p>
@@ -14,7 +14,7 @@ http://hostname/index.php?page=Fundamentals.Services
Developers may implement additional services for their applications. To make a service available, configure it in <a href="?page=Configurations.AppConfig">application configurations</a>.
</p>
-<h2>Page Service</h2>
+<h2 id="1102">Page Service</h2>
<p>
PRADO implements <tt>TPageService</tt> to process users' page requests. Pages are stored under a directory specified by the <tt>BasePath</tt> property of the page service. The property defaults to <tt>pages</tt> directory under the application base path. You may change this default by configuring the service in the application configuration.
</p>
diff --git a/demos/quickstart/protected/pages/GettingStarted/AboutPrado.page b/demos/quickstart/protected/pages/GettingStarted/AboutPrado.page
index 6ac58b73..dc8de7ae 100644
--- a/demos/quickstart/protected/pages/GettingStarted/AboutPrado.page
+++ b/demos/quickstart/protected/pages/GettingStarted/AboutPrado.page
@@ -1,5 +1,5 @@
<com:TContent ID="body" >
-<h1>What is PRADO?</h1>
+<h1 id="201">What is PRADO?</h1>
<p>
PRADO is a component-based and event-driven programming framework for developing Web applications in PHP 5. PRADO stands for <b>P</b>HP <b>R</b>apid <b>A</b>pplication <b>D</b>evelopment <b>O</b>bject-oriented.
</p>
@@ -16,7 +16,7 @@ To facilitate interacting with components, PRADO implements an event-driven prog
In summary, developing a PRADO Web application mainly involves instantiating prebuilt component types, configuring them by setting their properties, responding to their events by writing handler functions, and composing them into pages for the application. It is very similar to RAD toolkits, such as Borland Delphi and Microsoft Visual Basic, that are used to develop desktop GUI applications.
</p>
-<h2>Why PRADO?</h2>
+<h2 id="202">Why PRADO?</h2>
<p>
PRADO is mostly quoted as a unique framework. In fact, it is so unique that it may turn your boring PHP programming into a fun task. The following list is a short summary of the main features of PRADO,
</p>
@@ -32,7 +32,7 @@ PRADO is mostly quoted as a unique framework. In fact, it is so unique that it m
<li>Other features - Powerful error/exception handling and message logging; generic caching and selective output caching; customizable and localizable error handling; extensible authentication and authorization; security measures such as cross-site script (CSS) prevention, cookie protection, etc.</li>
</ul>
-<h2>What Is PRADO Best For?</h2>
+<h2 id="203">What Is PRADO Best For?</h2>
<p>
PRADO is best suitable for creating Web front-ends that are highly user-interactive and require small to medium traffic. It can be used to develop systems as simple as a blog system to systems as complex as a content management system (CMS) or a complete e-commerce solution. PRADO can help you cut your development time significantly.
</p>
@@ -43,7 +43,7 @@ PRADO does not exclude other back-end solutions such as most DB abstraction laye
Without caching techniques, PRADO may not be suitable for developing extremely high-traffic Web applications, such as popular portals, forums, etc. In these applications, every niche of potential performance gain must be exploited and server caching (e.g. Zend optimizer) is almost a must. PRADO implements a generic cache technique and enables selective caching of part of Web contents.
</p>
-<h2>How Is PRADO Compared with Other Frameworks?</h2>
+<h2 id="204">How Is PRADO Compared with Other Frameworks?</h2>
<p>
PRADO is described as a unique framework. Its uniqueness mainly lies in the component-based and event-driven programming paradigm that it tries to promote. Although this programming paradigm is not new in desktop application programming and not new in a few Web programming languages, PRADO is perhaps the first PHP framework enabling it.
</p>
@@ -51,7 +51,7 @@ PRADO is described as a unique framework. Its uniqueness mainly lies in the comp
Most PHP frameworks are trying to establish a loose standard of organizing PHP programming, most preferably the MVC (model-view-controller) model. It is difficult to compare PRADO with these frameworks because they have different focuses. What we can say is, PRADO is more like a high-level language built upon PHP, while the MVC frameworks stand for the best programming practices. Both aim to help developers to rapidly complete Web application development. The advantage of PRADO is its rich set of prebuilt powerful components and extreme reusability of the PRADO code, while the advantage of the MVC frameworks is the complete separation of model, view and controller, which greatly facilitates team integration.
</p>
-<h2>History of PRADO</h2>
+<h2 id="205">History of PRADO</h2>
<p>
The very original inspiration of PRADO came from Apache Tapestry. During the design and implementation, I borrowed many ideas from Borland Delphi and Microsoft ASP.NET. The first version of PRADO came out in June 2004 and was written in PHP 4. Driven by the Zend PHP 5 coding contest, I rewrote PRADO in PHP 5, which proved to be a wise move, thanks to the new object model provided by PHP 5. PRADO won the grand prize in the Zend contest, earning the highest votes from both the public and the judges' panel.
</p>
diff --git a/demos/quickstart/protected/pages/GettingStarted/HelloWorld.page b/demos/quickstart/protected/pages/GettingStarted/HelloWorld.page
index b5016f4e..780c92b9 100644
--- a/demos/quickstart/protected/pages/GettingStarted/HelloWorld.page
+++ b/demos/quickstart/protected/pages/GettingStarted/HelloWorld.page
@@ -1,5 +1,5 @@
<com:TContent ID="body" >
-<h1>My First PRADO Application</h1>
+<h1 id="401">My First PRADO Application</h1>
<p>
In this section, we guide you through creating your first PRADO application, the famous "Hello World" application.
</p>
diff --git a/demos/quickstart/protected/pages/GettingStarted/Installation.page b/demos/quickstart/protected/pages/GettingStarted/Installation.page
index ae3d18c3..07a76042 100644
--- a/demos/quickstart/protected/pages/GettingStarted/Installation.page
+++ b/demos/quickstart/protected/pages/GettingStarted/Installation.page
@@ -1,5 +1,5 @@
<com:TContent ID="body" >
-<h1>Installing PRADO</h1>
+<h1 id="301">Installing PRADO</h1>
<p>
If you are viewing this page from your own Web server, you are already done with the installation.
</p>
diff --git a/demos/quickstart/protected/pages/GettingStarted/Introduction.page b/demos/quickstart/protected/pages/GettingStarted/Introduction.page
index 3f3ea168..42ee0dad 100644
--- a/demos/quickstart/protected/pages/GettingStarted/Introduction.page
+++ b/demos/quickstart/protected/pages/GettingStarted/Introduction.page
@@ -1,5 +1,5 @@
<com:TContent ID="body" >
-<h1>Welcome to the PRADO QuickStart Tutorial</h1>
+<h1 id="101">Welcome to the PRADO QuickStart Tutorial</h1>
<p>
This QuickStart tutorial is provided to help you quickly start building your own Web applications based on PRADO version 3.0.
</p>
diff --git a/demos/quickstart/protected/pages/GettingStarted/Upgrading.page b/demos/quickstart/protected/pages/GettingStarted/Upgrading.page
index 287d3f84..cff4e72f 100644
--- a/demos/quickstart/protected/pages/GettingStarted/Upgrading.page
+++ b/demos/quickstart/protected/pages/GettingStarted/Upgrading.page
@@ -1,6 +1,6 @@
<com:TContent ID="body" >
-<h1>Upgrading from v2.x and v1.x</h1>
+<h1 id="501">Upgrading from v2.x and v1.x</h1>
<p>
PRADO v3.0 is NOT backward compatible with earlier versions of PRADO.
@@ -12,7 +12,7 @@ A good news is, properties and events of most controls remain intact, and the sy
We summarize in the following the most significant changes in v3.0 to help developers upgrade their v2.x and v1.x PRADO applications more easily, if needed.
</p>
-<h2>Component Definition</h2>
+<h2 id="502">Component Definition</h2>
<p>
Version 3.0 has completely discarded the need of component specification files. It relies more on conventions for defining component properties and events. In particular, a property is defined by the existence of a getter method and/or a setter method, while an event is defined by the existence of an <tt>on</tt>-method. Property and event names in v3.0 are both case-insensitive. As a consequence, developers are now required to take care of type conversions when a component property is being set. For example, the following code is used to define the setter method for the <tt>Enabled</tt> property of <tt>TControl</tt>, which is of <tt>boolean</tt> type,
</p>
@@ -27,22 +27,22 @@ public function setEnabled($value)
where <tt>TPropertyValue::ensureBoolean()</tt> is used to ensure that the input value be a boolean. This is because when the property is configured in template, a string value is passed to the setter. In previous versions, PRADO knows the property type based on the component specification files and does the type conversion for you.
</p>
-<h2>Application Controller</h2>
+<h2 id="503">Application Controller</h2>
<p>
Application controller now implements a modular architecture. Modules can be plugged in and configured in application specifications. Each module assumes a particular functionality, and they are coordinated together by the <a href="?page=Fundamentals.Applications">application lifecycle</a>. The concept of v2.x modules is replaced in v3.0 by <a href="?page=Configurations.PageConfig">page directories</a>. As a result, the format of v3.0 <a href="?page=Configurations.AppConfig">application specification</a> is also different from earlier versions.
</p>
-<h2>Pages</h2>
+<h2 id="504">Pages</h2>
<p>
Pages in v3.0 are organized in directories which may be compared to the module concept in v2.x. Pages are requested using the path to them. For example, a URL <tt>index.php?page=Controls.Samples.Sample1</tt> would be used to request for a page named <tt>Sample1</tt> stored under the <tt>[BasePath]/Controls/Samples</tt> directory, where <tt>[BasePath]</tt> refers to the root page path. The file name of a page template must be ended with <tt>.page</tt>, mainly to differentiate page templates from non-page control templates whose file names must be ended with <tt>.tpl</tt>.
</p>
-<h2>Control Relationship</h2>
+<h2 id="505">Control Relationship</h2>
<p>
Version 3.0 redefines the relationships between controls. In particular, the parent-child relationship now refers to the enclosure relationship between controls' presentation. And a new naming-container relationship is introduced to help better manage control IDs. For more details, see the <a href="?page=Fundamentals.Controls">controls</a> section.
</p>
-<h2>Template Syntax</h2>
+<h2 id="506">Template Syntax</h2>
<p>
The syntax of control templates in v3.0 remains similar to those in earlier versions, with many enhancements. A major change is about the databinding expression. In v3.0, this is done by the following,
</p>
@@ -53,7 +53,7 @@ The syntax of control templates in v3.0 remains similar to those in earlier vers
Expression and statement tags are also changed similarly. For more details, see the <a href="?page=Configurations.Templates1">template definition</a> section.
</p>
-<h2>Theme Syntax</h2>
+<h2 id="507">Theme Syntax</h2>
<p>
Themes in v3.0 are defined like control templates with a few restrictions.
</p>
diff --git a/demos/quickstart/protected/pages/Search.page b/demos/quickstart/protected/pages/Search.page
new file mode 100644
index 00000000..d2aee66e
--- /dev/null
+++ b/demos/quickstart/protected/pages/Search.page
@@ -0,0 +1,28 @@
+<com:TContent ID="body">
+<div class="quicksearch">
+ <div class="search">
+ <com:SearchBox ID="search" />
+ </div>
+ <com:TRepeater id="results">
+ <prop:HeaderTemplate>
+ <div class="searchHeader">
+ Found <%# $this->Parent->Data->Count %> results.
+ </div>
+ </prop:HeaderTemplate>
+ <prop:ItemTemplate>
+ <div class="searchItem">
+ <div class="searchItemLink">
+ <%# $this->ItemIndex + 1 %>.
+ <a href="<%# $this->DataItem->link %>">
+ <%# $this->DataItem->title %>
+ </a>
+ </div>
+ <p class="searchItemBody"><%# $this->Page->HighlightSearch($this->DataItem->text) %></p>
+ </div>
+ </prop:ItemTemplate>
+ <prop:EmptyTemplate>
+ Unable to find "<%= htmlentities($this->Page->search->Text) %>".
+ </prop:EmptyTemplate>
+ </com:TRepeater>
+</div>
+</com:TContent> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/Search.php b/demos/quickstart/protected/pages/Search.php
new file mode 100644
index 00000000..c7bdce89
--- /dev/null
+++ b/demos/quickstart/protected/pages/Search.php
@@ -0,0 +1,53 @@
+<?php
+/*
+ * Created on 7/05/2006
+ */
+
+class Search extends TPage
+{
+ public function onLoad($param)
+ {
+ if(!$this->IsPostBack && strlen($text = $this->search->getText()) > 0)
+ {
+ $search = $this->getApplication()->getModule("search");
+ $this->results->setDataSource($search->find($text));
+ $this->results->dataBind();
+ }
+ }
+
+ public function highlightSearch($text)
+ {
+ $words = str_word_count($text, 1);
+ $keys = str_word_count(strtolower($this->search->getText()),1);
+ $where = 0;
+ $t = count($words);
+ for($i = 0; $i<$t; $i++)
+ {
+ if($this->containsKeys(strtolower($words[$i]), $keys))
+ {
+ $words[$i] = '<span class="searchterm">'.$words[$i].'</span>';
+ $where = $i;
+ break;
+ }
+ }
+
+ $min = $where - 15 < 0 ? 0 : $where - 15;
+ $max = $where + 15 > $t ? $t : $where + 15;
+ $subtext = array_splice($words, $min, $max-$min);
+ $prefix = $min == 0 ? '' : '...';
+ $suffix = $max == $t ? '' : '...';
+ return $prefix.implode(' ', $subtext).$suffix;
+ }
+
+ protected function containsKeys($word, $keys)
+ {
+ foreach($keys as $key)
+ {
+ if(is_int(strpos($word, $key)))
+ return true;
+ }
+ return false;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/ViewSource.page b/demos/quickstart/protected/pages/ViewSource.page
index 900c45f6..8f1239b3 100644
--- a/demos/quickstart/protected/pages/ViewSource.page
+++ b/demos/quickstart/protected/pages/ViewSource.page
@@ -7,9 +7,19 @@
<body>
<div id="sourceList">
-<com:TLiteral ID="SourceList" />
+<table border="0">
+<com:TRepeater ID="SourceList">
+<prop:ItemTemplate>
+<tr>
+ <td align="right"><%# $this->DataItem['type']%>:</td>
+ <td><a href="<%# $this->DataItem['active']?'':$this->DataItem['url']%>"><%# $this->DataItem['name']%></a></td>
+</tr>
+</prop:ItemTemplate>
+</com:TRepeater>
+</table>
</div>
<div id="sourceView">
+<h3 style="text-align:center"><%= $this->FilePath %></h3>
<com:TTextHighlighter ID="Highlighter" ShowLineNumbers="true" CssClass="source">
<com:TLiteral ID="SourceView" />
</com:TTextHighlighter>
diff --git a/demos/quickstart/protected/pages/ViewSource.php b/demos/quickstart/protected/pages/ViewSource.php
index cfdc63ef..9a8d7d09 100644
--- a/demos/quickstart/protected/pages/ViewSource.php
+++ b/demos/quickstart/protected/pages/ViewSource.php
@@ -6,19 +6,6 @@ class ViewSource extends TPage
private $_fullPath=null;
private $_fileType=null;
- protected function isFileTypeAllowed($extension)
- {
- return in_array($extension,array('tpl','page','php'));
- }
-
- protected function getFileExtension($fileName)
- {
- if(($pos=strrpos($fileName,'.'))===false)
- return '';
- else
- return substr($fileName,$pos+1);
- }
-
public function onLoad($param)
{
parent::onLoad($param);
@@ -35,49 +22,88 @@ class ViewSource extends TPage
}
if($this->_fullPath===null)
throw new THttpException(500,'File Not Found: %s',$path);
+
+ $this->SourceList->DataSource=$this->SourceFiles;
+ $this->SourceList->dataBind();
+
+ $this->Highlighter->Language=$this->getFileLanguage($fileExt);
+ $this->SourceView->Text=file_get_contents($this->_fullPath);
+ }
+
+ public function getFilePath()
+ {
+ return $this->_path;
+ }
+
+ protected function getSourceFiles()
+ {
+ $list=array();
$basePath=dirname($this->_fullPath);
if($dh=opendir($basePath))
{
- $str="<h2>{$this->_path}</h2>\n";
while(($file=readdir($dh))!==false)
{
if(is_file($basePath.'/'.$file))
{
- $fileType=$this->getFileExtension($basePath.'/'.$file);
- if($this->isFileTypeAllowed($fileType))
+ $extension=$this->getFileExtension($basePath.'/'.$file);
+ if($this->isFileTypeAllowed($extension))
{
- if($fileType==='tpl' || $fileType==='page')
- $type='Template file';
- else
- $type='Class file';
- $path='/'.ltrim(strtr(dirname($this->_path),'\\','/').'/'.$file,'/');
- $str.="$type: <a href=\"?page=ViewSource&amp;path=$path\">$file</a><br/>";
+ $fileType=$this->getFileType($extension);
+ $list[]=array(
+ 'name'=>$file,
+ 'type'=>$fileType,
+ 'active'=>basename($this->_fullPath)===$file,
+ 'url'=>'?page=ViewSource&amp;path=/'.ltrim(strtr(dirname($this->_path),'\\','/').'/'.$file,'/')
+ );
}
}
}
closedir($dh);
- $this->SourceList->Text=$str;
}
+ foreach($list as $item)
+ $aux[]=$item['name'];
+ array_multisort($aux, SORT_ASC, $list);
+ return $list;
+ }
- switch($fileExt)
+ protected function isFileTypeAllowed($extension)
+ {
+ return in_array($extension,array('tpl','page','php','html'));
+ }
+
+ protected function getFileExtension($fileName)
+ {
+ if(($pos=strrpos($fileName,'.'))===false)
+ return '';
+ else
+ return substr($fileName,$pos+1);
+ }
+
+ protected function getFileType($extension)
+ {
+ if($extension==='tpl' || $extension==='page')
+ return 'Template file';
+ else
+ return 'Class file';
+ }
+
+ protected function getFileLanguage($extension)
+ {
+ switch($extension)
{
case 'page' :
case 'tpl' :
- $this->Highlighter->Language='prado';
- break;
+ return 'prado';
case 'php' :
- $this->Highlighter->Language='php';
+ return 'php';
break;
case 'xml' :
- $this->Highlighter->Language='xml';
+ return 'xml';
break;
default :
- $this->Highlighter->Language='html';
- break;
+ return 'html';
}
-
- $this->SourceView->Text=file_get_contents($this->_fullPath);
}
}
diff --git a/demos/quickstart/themes/PradoSoft/style.css b/demos/quickstart/themes/PradoSoft/style.css
index df8d1e19..38ef5375 100644
--- a/demos/quickstart/themes/PradoSoft/style.css
+++ b/demos/quickstart/themes/PradoSoft/style.css
@@ -332,3 +332,36 @@ dd
margin-bottom: 0.75em;
}
+.quicksearch .search .searchBox
+{
+ width: 25em;
+}
+
+.quicksearch .search
+{
+ text-align: center;
+ padding: 2em;
+}
+
+.searchItem
+{
+ margin-top: 20px;
+ margin-left: 20px;
+ margin-right: 20px;
+}
+
+.searchItemBody
+{
+ margin-top: 0em;
+ font-size: 0.9em;
+}
+
+.searchItemLink
+{
+ font-size: 1.05em;
+}
+
+.searchterm
+{
+ font-weight: bold;
+} \ No newline at end of file
diff --git a/framework/3rdParty/readme.html b/framework/3rdParty/readme.html
index b48f5b74..c6bc491c 100644
--- a/framework/3rdParty/readme.html
+++ b/framework/3rdParty/readme.html
@@ -116,6 +116,15 @@ projects.
<td>Has "The Software shall be used for Good, not Evil." clause.</td>
</tr>
+<tr>
+ <td><a href="../../demos/quickstart/protected/index/Zend">../../demos/quickstart/protected/index/Zend</a></td>
+ <td><a href="http://framework.zend.com">Zend Framework</a></td>
+ <td><a href="../../demos/quickstart/protected/index/Zend/LICENSE.txt">BSD</a></td>
+ <td>Quickstart tutorial indexing and search is provided the Zend Lucene search implementation.</td>
+ <td>This product includes the Zend Framework, freely available at
+ http://www.zend.com</td>
+</tr>
+
</table>
</p>
</body>
diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt
index 5edd7c75..fa12238a 100644
--- a/framework/Exceptions/messages.txt
+++ b/framework/Exceptions/messages.txt
@@ -103,7 +103,7 @@ pageserviceconf_page_invalid = <page> element must have an "id" attribute in p
template_closingtag_unexpected = Unexpected closing tag '{0}' is found.
template_closingtag_expected = Closing tag '{0}' is expected.
-template_directive_nonunique = Directive '<%@ ... %>' must appear at most once in a template.
+template_directive_nonunique = Directive '<%@ ... %>' must appear at the beginning of the template and can appear at most once.
template_comments_forbidden = Template comments are not allowed within property tags.
template_matching_unexpected = Unexpected matching.
template_property_unknown = {0} has no property called '{1}'.
diff --git a/framework/IO/TTextWriter.php b/framework/IO/TTextWriter.php
index a6bf5751..e63a7f7c 100644
--- a/framework/IO/TTextWriter.php
+++ b/framework/IO/TTextWriter.php
@@ -19,7 +19,7 @@
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Revision: $ $Date: $
- * @package System
+ * @package System.IO
* @since 3.0
*/
class TTextWriter extends TComponent implements ITextWriter
diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php
index d2a92c04..60ce11ca 100644
--- a/framework/Web/UI/TTemplateManager.php
+++ b/framework/Web/UI/TTemplateManager.php
@@ -546,6 +546,7 @@ class TTemplate extends TApplicationComponent implements ITemplate
$container=-1;
$matchEnd=0;
$c=0;
+ $this->_directive=null;
try
{
for($i=0;$i<$n;++$i)
@@ -598,7 +599,7 @@ class TTemplate extends TApplicationComponent implements ITemplate
if($matchStart>$textStart)
$tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart));
$textStart=$matchEnd+1;
- if(isset($tpl[0]))
+ if(isset($tpl[0]) || $this->_directive!==null)
throw new TConfigurationException('template_directive_nonunique');
$this->_directive=$this->parseAttributes($match[4][0],$match[4][1]);
}
@@ -675,18 +676,23 @@ class TTemplate extends TApplicationComponent implements ITemplate
}
if(($last=count($stack))<1 || $stack[$last-1][0]!=='@')
{
- if($matchStart>$textStart && $container>=0)
+ if($matchStart>$textStart)
{
$value=substr($input,$textStart,$matchStart-$textStart);
if(strrpos($prop,'template')===strlen($prop)-8)
$value=$this->parseTemplateProperty($value,$textStart);
else
$value=$this->parseAttribute($value);
- $type=$tpl[$container][1];
- $this->validateAttributes($type,array($prop=>$value));
- if(isset($tpl[$container][2][$prop]))
- throw new TConfigurationException('template_property_duplicated',$prop);
- $tpl[$container][2][$prop]=$value;
+ if($container>=0)
+ {
+ $type=$tpl[$container][1];
+ $this->validateAttributes($type,array($prop=>$value));
+ if(isset($tpl[$container][2][$prop]))
+ throw new TConfigurationException('template_property_duplicated',$prop);
+ $tpl[$container][2][$prop]=$value;
+ }
+ else // a property for the template control
+ $this->_directive[$prop]=$value;
$textStart=$matchEnd+1;
}
$expectPropEnd=false;
@@ -730,6 +736,9 @@ class TTemplate extends TApplicationComponent implements ITemplate
throw new TConfigurationException('template_format_invalid',$this->_tplFile,$line,$e->getMessage());
}
+ if($this->_directive===null)
+ $this->_directive=array();
+
// optimization by merging consecutive strings, expressions, statements and bindings
$objects=array();
$parent=null;
diff --git a/framework/Web/UI/WebControls/TButton.php b/framework/Web/UI/WebControls/TButton.php
index ba523168..75d60695 100644
--- a/framework/Web/UI/WebControls/TButton.php
+++ b/framework/Web/UI/WebControls/TButton.php
@@ -19,7 +19,7 @@
* A <b>command</b> button has a command name (specified by
* the {@link setCommandName CommandName} property) and and a command parameter
* (specified by {@link setCommandParameter CommandParameter} property)
- * associated with the button. This allows you to create multiple TLinkButton
+ * associated with the button. This allows you to create multiple TButton
* components on a Web page and programmatically determine which one is clicked
* with what parameter. You can provide an event handler for
* {@link onCommand OnCommand} event to programmatically control the actions performed