First commit
This commit is contained in:
commit
c6e2478c40
13918 changed files with 2303184 additions and 0 deletions
37
modules/search/search-block-form.tpl.php
Normal file
37
modules/search/search-block-form.tpl.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Displays the search form block.
|
||||
*
|
||||
* Available variables:
|
||||
* - $search_form: The complete search form ready for print.
|
||||
* - $search: Associative array of search elements. Can be used to print each
|
||||
* form element separately.
|
||||
*
|
||||
* Default elements within $search:
|
||||
* - $search['search_block_form']: Text input area wrapped in a div.
|
||||
* - $search['actions']: Rendered form buttons.
|
||||
* - $search['hidden']: Hidden form elements. Used to validate forms when
|
||||
* submitted.
|
||||
*
|
||||
* Modules can add to the search form, so it is recommended to check for their
|
||||
* existence before printing. The default keys will always exist. To check for
|
||||
* a module-provided field, use code like this:
|
||||
* @code
|
||||
* <?php if (isset($search['extra_field'])): ?>
|
||||
* <div class="extra-field">
|
||||
* <?php print $search['extra_field']; ?>
|
||||
* </div>
|
||||
* <?php endif; ?>
|
||||
* @endcode
|
||||
*
|
||||
* @see template_preprocess_search_block_form()
|
||||
*/
|
||||
?>
|
||||
<div class="container-inline">
|
||||
<?php if (empty($variables['form']['#block']->subject)): ?>
|
||||
<h2 class="element-invisible"><?php print t('Search form'); ?></h2>
|
||||
<?php endif; ?>
|
||||
<?php print $search_form; ?>
|
||||
</div>
|
81
modules/search/search-result.tpl.php
Normal file
81
modules/search/search-result.tpl.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for displaying a single search result.
|
||||
*
|
||||
* This template renders a single search result and is collected into
|
||||
* search-results.tpl.php. This and the parent template are
|
||||
* dependent to one another sharing the markup for definition lists.
|
||||
*
|
||||
* Available variables:
|
||||
* - $url: URL of the result.
|
||||
* - $title: Title of the result.
|
||||
* - $snippet: A small preview of the result. Does not apply to user searches.
|
||||
* - $info: String of all the meta information ready for print. Does not apply
|
||||
* to user searches.
|
||||
* - $info_split: Contains same data as $info, split into a keyed array.
|
||||
* - $module: The machine-readable name of the module (tab) being searched, such
|
||||
* as "node" or "user".
|
||||
* - $title_prefix (array): An array containing additional output populated by
|
||||
* modules, intended to be displayed in front of the main title tag that
|
||||
* appears in the template.
|
||||
* - $title_suffix (array): An array containing additional output populated by
|
||||
* modules, intended to be displayed after the main title tag that appears in
|
||||
* the template.
|
||||
*
|
||||
* Default keys within $info_split:
|
||||
* - $info_split['module']: The module that implemented the search query.
|
||||
* - $info_split['user']: Author of the node linked to users profile. Depends
|
||||
* on permission.
|
||||
* - $info_split['date']: Last update of the node. Short formatted.
|
||||
* - $info_split['comment']: Number of comments output as "% comments", %
|
||||
* being the count. Depends on comment.module.
|
||||
*
|
||||
* Other variables:
|
||||
* - $classes_array: Array of HTML class attribute values. It is flattened
|
||||
* into a string within the variable $classes.
|
||||
* - $title_attributes_array: Array of HTML attributes for the title. It is
|
||||
* flattened into a string within the variable $title_attributes.
|
||||
* - $content_attributes_array: Array of HTML attributes for the content. It is
|
||||
* flattened into a string within the variable $content_attributes.
|
||||
*
|
||||
* Since $info_split is keyed, a direct print of the item is possible.
|
||||
* This array does not apply to user searches so it is recommended to check
|
||||
* for its existence before printing. The default keys of 'type', 'user' and
|
||||
* 'date' always exist for node searches. Modules may provide other data.
|
||||
* @code
|
||||
* <?php if (isset($info_split['comment'])): ?>
|
||||
* <span class="info-comment">
|
||||
* <?php print $info_split['comment']; ?>
|
||||
* </span>
|
||||
* <?php endif; ?>
|
||||
* @endcode
|
||||
*
|
||||
* To check for all available data within $info_split, use the code below.
|
||||
* @code
|
||||
* <?php print '<pre>'. check_plain(print_r($info_split, 1)) .'</pre>'; ?>
|
||||
* @endcode
|
||||
*
|
||||
* @see template_preprocess()
|
||||
* @see template_preprocess_search_result()
|
||||
* @see template_process()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
?>
|
||||
<li class="<?php print $classes; ?>"<?php print $attributes; ?>>
|
||||
<?php print render($title_prefix); ?>
|
||||
<h3 class="title"<?php print $title_attributes; ?>>
|
||||
<a href="<?php print $url; ?>"><?php print $title; ?></a>
|
||||
</h3>
|
||||
<?php print render($title_suffix); ?>
|
||||
<div class="search-snippet-info">
|
||||
<?php if ($snippet): ?>
|
||||
<p class="search-snippet"<?php print $content_attributes; ?>><?php print $snippet; ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if ($info): ?>
|
||||
<p class="search-info"><?php print $info; ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</li>
|
35
modules/search/search-results.tpl.php
Normal file
35
modules/search/search-results.tpl.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for displaying search results.
|
||||
*
|
||||
* This template collects each invocation of theme_search_result(). This and
|
||||
* the child template are dependent to one another sharing the markup for
|
||||
* definition lists.
|
||||
*
|
||||
* Note that modules may implement their own search type and theme function
|
||||
* completely bypassing this template.
|
||||
*
|
||||
* Available variables:
|
||||
* - $search_results: All results as it is rendered through
|
||||
* search-result.tpl.php
|
||||
* - $module: The machine-readable name of the module (tab) being searched, such
|
||||
* as "node" or "user".
|
||||
*
|
||||
*
|
||||
* @see template_preprocess_search_results()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
?>
|
||||
<?php if ($search_results): ?>
|
||||
<h2><?php print t('Search results');?></h2>
|
||||
<ol class="search-results <?php print $module; ?>-results">
|
||||
<?php print $search_results; ?>
|
||||
</ol>
|
||||
<?php print $pager; ?>
|
||||
<?php else : ?>
|
||||
<h2><?php print t('Your search yielded no results');?></h2>
|
||||
<?php print search_help('search#noresults', drupal_help_arg()); ?>
|
||||
<?php endif; ?>
|
13
modules/search/search-rtl.css
Normal file
13
modules/search/search-rtl.css
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
.search-advanced .criterion {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 2em;
|
||||
}
|
||||
.search-advanced .action {
|
||||
float: right;
|
||||
clear: right;
|
||||
}
|
||||
.search-results .search-snippet-info {
|
||||
padding-right: 1em; /* LTR */
|
||||
}
|
196
modules/search/search.admin.inc
Normal file
196
modules/search/search.admin.inc
Normal file
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Admin page callbacks for the search module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Menu callback: confirm wiping of the index.
|
||||
*/
|
||||
function search_reindex_confirm() {
|
||||
return confirm_form(array(), t('Are you sure you want to re-index the site?'),
|
||||
'admin/config/search/settings', t('The search index is not cleared but systematically updated to reflect the new settings. Searching will continue to work but new content won\'t be indexed until all existing content has been re-indexed. This action cannot be undone.'), t('Re-index site'), t('Cancel'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for wipe confirmation
|
||||
*/
|
||||
function search_reindex_confirm_submit(&$form, &$form_state) {
|
||||
if ($form['confirm']) {
|
||||
search_reindex();
|
||||
drupal_set_message(t('The index will be rebuilt.'));
|
||||
$form_state['redirect'] = 'admin/config/search/settings';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get real module names.
|
||||
*/
|
||||
function _search_get_module_names() {
|
||||
|
||||
$search_info = search_get_info(TRUE);
|
||||
$system_info = system_get_info('module');
|
||||
$names = array();
|
||||
foreach ($search_info as $module => $info) {
|
||||
$names[$module] = $system_info[$module]['name'];
|
||||
}
|
||||
asort($names, SORT_STRING);
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback: displays the search module settings page.
|
||||
*
|
||||
* @ingroup forms
|
||||
*
|
||||
* @see search_admin_settings_validate()
|
||||
* @see search_admin_settings_submit()
|
||||
* @see search_admin_reindex_submit()
|
||||
*/
|
||||
function search_admin_settings($form) {
|
||||
// Collect some stats
|
||||
$remaining = 0;
|
||||
$total = 0;
|
||||
foreach (variable_get('search_active_modules', array('node', 'user')) as $module) {
|
||||
if ($status = module_invoke($module, 'search_status')) {
|
||||
$remaining += $status['remaining'];
|
||||
$total += $status['total'];
|
||||
}
|
||||
}
|
||||
|
||||
$count = format_plural($remaining, 'There is 1 item left to index.', 'There are @count items left to index.');
|
||||
$percentage = ((int)min(100, 100 * ($total - $remaining) / max(1, $total))) . '%';
|
||||
$status = '<p><strong>' . t('%percentage of the site has been indexed.', array('%percentage' => $percentage)) . ' ' . $count . '</strong></p>';
|
||||
$form['status'] = array('#type' => 'fieldset', '#title' => t('Indexing status'));
|
||||
$form['status']['status'] = array('#markup' => $status);
|
||||
$form['status']['wipe'] = array('#type' => 'submit', '#value' => t('Re-index site'), '#submit' => array('search_admin_reindex_submit'));
|
||||
|
||||
$items = drupal_map_assoc(array(10, 20, 50, 100, 200, 500));
|
||||
|
||||
// Indexing throttle:
|
||||
$form['indexing_throttle'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Indexing throttle')
|
||||
);
|
||||
$form['indexing_throttle']['search_cron_limit'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Number of items to index per cron run'),
|
||||
'#default_value' => variable_get('search_cron_limit', 100),
|
||||
'#options' => $items,
|
||||
'#description' => t('The maximum number of items indexed in each pass of a <a href="@cron">cron maintenance task</a>. If necessary, reduce the number of items to prevent timeouts and memory errors while indexing.', array('@cron' => url('admin/reports/status')))
|
||||
);
|
||||
// Indexing settings:
|
||||
$form['indexing_settings'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Indexing settings')
|
||||
);
|
||||
$form['indexing_settings']['info'] = array(
|
||||
'#markup' => t('<p><em>Changing the settings below will cause the site index to be rebuilt. The search index is not cleared but systematically updated to reflect the new settings. Searching will continue to work but new content won\'t be indexed until all existing content has been re-indexed.</em></p><p><em>The default settings should be appropriate for the majority of sites.</em></p>')
|
||||
);
|
||||
$form['indexing_settings']['minimum_word_size'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Minimum word length to index'),
|
||||
'#default_value' => variable_get('minimum_word_size', 3),
|
||||
'#size' => 5,
|
||||
'#maxlength' => 3,
|
||||
'#description' => t('The number of characters a word has to be to be indexed. A lower setting means better search result ranking, but also a larger database. Each search query must contain at least one keyword that is this size (or longer).'),
|
||||
'#element_validate' => array('element_validate_integer_positive'),
|
||||
);
|
||||
$form['indexing_settings']['overlap_cjk'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Simple CJK handling'),
|
||||
'#default_value' => variable_get('overlap_cjk', TRUE),
|
||||
'#description' => t('Whether to apply a simple Chinese/Japanese/Korean tokenizer based on overlapping sequences. Turn this off if you want to use an external preprocessor for this instead. Does not affect other languages.')
|
||||
);
|
||||
|
||||
$form['active'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Active search modules')
|
||||
);
|
||||
$module_options = _search_get_module_names();
|
||||
$form['active']['search_active_modules'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Active modules'),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => variable_get('search_active_modules', array('node', 'user')),
|
||||
'#options' => $module_options,
|
||||
'#description' => t('Choose which search modules are active from the available modules.')
|
||||
);
|
||||
$form['active']['search_default_module'] = array(
|
||||
'#title' => t('Default search module'),
|
||||
'#type' => 'radios',
|
||||
'#default_value' => variable_get('search_default_module', 'node'),
|
||||
'#options' => $module_options,
|
||||
'#description' => t('Choose which search module is the default.')
|
||||
);
|
||||
$form['logging'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Logging')
|
||||
);
|
||||
$form['logging']['search_logging'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Log searches'),
|
||||
'#default_value' => variable_get('search_logging', 1),
|
||||
'#description' => t('If checked, all searches will be logged. Uncheck to skip logging. Logging may affect performance.'),
|
||||
);
|
||||
$form['#validate'][] = 'search_admin_settings_validate';
|
||||
$form['#submit'][] = 'search_admin_settings_submit';
|
||||
|
||||
// Per module settings
|
||||
foreach (variable_get('search_active_modules', array('node', 'user')) as $module) {
|
||||
$added_form = module_invoke($module, 'search_admin');
|
||||
if (is_array($added_form)) {
|
||||
$form = array_merge($form, $added_form);
|
||||
}
|
||||
}
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validation handler for search_admin_settings().
|
||||
*/
|
||||
function search_admin_settings_validate($form, &$form_state) {
|
||||
// Check whether we selected a valid default.
|
||||
if ($form_state['triggering_element']['#value'] != t('Reset to defaults')) {
|
||||
$new_modules = array_filter($form_state['values']['search_active_modules']);
|
||||
$default = $form_state['values']['search_default_module'];
|
||||
if (!in_array($default, $new_modules, TRUE)) {
|
||||
form_set_error('search_default_module', t('Your default search module is not selected as an active module.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for search_admin_settings().
|
||||
*/
|
||||
function search_admin_settings_submit($form, &$form_state) {
|
||||
// If these settings change, the index needs to be rebuilt.
|
||||
if ((variable_get('minimum_word_size', 3) != $form_state['values']['minimum_word_size']) ||
|
||||
(variable_get('overlap_cjk', TRUE) != $form_state['values']['overlap_cjk'])) {
|
||||
drupal_set_message(t('The index will be rebuilt.'));
|
||||
search_reindex();
|
||||
}
|
||||
$current_modules = variable_get('search_active_modules', array('node', 'user'));
|
||||
// Check whether we are resetting the values.
|
||||
if ($form_state['triggering_element']['#value'] == t('Reset to defaults')) {
|
||||
$new_modules = array('node', 'user');
|
||||
}
|
||||
else {
|
||||
$new_modules = array_filter($form_state['values']['search_active_modules']);
|
||||
}
|
||||
if (array_diff($current_modules, $new_modules)) {
|
||||
drupal_set_message(t('The active search modules have been changed.'));
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for reindex button on search_admin_settings_form().
|
||||
*/
|
||||
function search_admin_reindex_submit($form, &$form_state) {
|
||||
// send the user to the confirmation page
|
||||
$form_state['redirect'] = 'admin/config/search/settings/reindex';
|
||||
}
|
380
modules/search/search.api.php
Normal file
380
modules/search/search.api.php
Normal file
|
@ -0,0 +1,380 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Search module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Define a custom search type.
|
||||
*
|
||||
* This hook allows a module to tell search.module that it wishes to perform
|
||||
* searches on content it defines (custom node types, users, or comments for
|
||||
* example) when a site search is performed.
|
||||
*
|
||||
* In order for the search to do anything, your module must also implement
|
||||
* hook_search_execute(), which is called when someone requests a search
|
||||
* on your module's type of content. If you want to have your content
|
||||
* indexed in the standard search index, your module should also implement
|
||||
* hook_update_index(). If your search type has settings, you can implement
|
||||
* hook_search_admin() to add them to the search settings page. You can use
|
||||
* hook_form_FORM_ID_alter(), with FORM_ID set to 'search_form', to add fields
|
||||
* to the search form (see node_form_search_form_alter() for an example).
|
||||
* You can use hook_search_access() to limit access to searching,
|
||||
* and hook_search_page() to override how search results are displayed.
|
||||
*
|
||||
* @return
|
||||
* Array with optional keys:
|
||||
* - title: Title for the tab on the search page for this module. Title must
|
||||
* be untranslated. Outside of this return array, pass the title through the
|
||||
* t() function to register it as a translatable string.
|
||||
* - path: Path component after 'search/' for searching with this module.
|
||||
* Defaults to the module name if not given.
|
||||
* - conditions_callback: An implementation of callback_search_conditions().
|
||||
*
|
||||
* @ingroup search
|
||||
*/
|
||||
function hook_search_info() {
|
||||
// Make the title translatable.
|
||||
t('Content');
|
||||
|
||||
return array(
|
||||
'title' => 'Content',
|
||||
'path' => 'node',
|
||||
'conditions_callback' => 'callback_search_conditions',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define access to a custom search routine.
|
||||
*
|
||||
* This hook allows a module to define permissions for a search tab.
|
||||
*
|
||||
* @ingroup search
|
||||
*/
|
||||
function hook_search_access() {
|
||||
return user_access('access content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Take action when the search index is going to be rebuilt.
|
||||
*
|
||||
* Modules that use hook_update_index() should update their indexing
|
||||
* bookkeeping so that it starts from scratch the next time
|
||||
* hook_update_index() is called.
|
||||
*
|
||||
* @ingroup search
|
||||
*/
|
||||
function hook_search_reset() {
|
||||
db_update('search_dataset')
|
||||
->fields(array('reindex' => REQUEST_TIME))
|
||||
->condition('type', 'node')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the status of indexing.
|
||||
*
|
||||
* The core search module only invokes this hook on active modules.
|
||||
* Implementing modules do not need to check whether they are active when
|
||||
* calculating their return values.
|
||||
*
|
||||
* @return
|
||||
* An associative array with the key-value pairs:
|
||||
* - 'remaining': The number of items left to index.
|
||||
* - 'total': The total number of items to index.
|
||||
*
|
||||
* @ingroup search
|
||||
*/
|
||||
function hook_search_status() {
|
||||
$total = db_query('SELECT COUNT(*) FROM {node} WHERE status = 1')->fetchField();
|
||||
$remaining = db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = 1 AND d.sid IS NULL OR d.reindex <> 0")->fetchField();
|
||||
return array('remaining' => $remaining, 'total' => $total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add elements to the search settings form.
|
||||
*
|
||||
* @return
|
||||
* Form array for the Search settings page at admin/config/search/settings.
|
||||
*
|
||||
* @ingroup search
|
||||
*/
|
||||
function hook_search_admin() {
|
||||
// Output form for defining rank factor weights.
|
||||
$form['content_ranking'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Content ranking'),
|
||||
);
|
||||
$form['content_ranking']['#theme'] = 'node_search_admin';
|
||||
$form['content_ranking']['info'] = array(
|
||||
'#value' => '<em>' . t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '</em>'
|
||||
);
|
||||
|
||||
// Note: reversed to reflect that higher number = higher ranking.
|
||||
$options = drupal_map_assoc(range(0, 10));
|
||||
foreach (module_invoke_all('ranking') as $var => $values) {
|
||||
$form['content_ranking']['factors']['node_rank_' . $var] = array(
|
||||
'#title' => $values['title'],
|
||||
'#type' => 'select',
|
||||
'#options' => $options,
|
||||
'#default_value' => variable_get('node_rank_' . $var, 0),
|
||||
);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a search for a set of key words.
|
||||
*
|
||||
* Use database API with the 'PagerDefault' query extension to perform your
|
||||
* search.
|
||||
*
|
||||
* If your module uses hook_update_index() and search_index() to index its
|
||||
* items, use table 'search_index' aliased to 'i' as the main table in your
|
||||
* query, with the 'SearchQuery' extension. You can join to your module's table
|
||||
* using the 'i.sid' field, which will contain the $sid values you provided to
|
||||
* search_index(). Add the main keywords to the query by using method
|
||||
* searchExpression(). The functions search_expression_extract() and
|
||||
* search_expression_insert() may also be helpful for adding custom search
|
||||
* parameters to the search expression.
|
||||
*
|
||||
* See node_search_execute() for an example of a module that uses the search
|
||||
* index, and user_search_execute() for an example that doesn't use the search
|
||||
* index.
|
||||
*
|
||||
* @param $keys
|
||||
* The search keywords as entered by the user.
|
||||
* @param $conditions
|
||||
* An optional array of additional conditions, such as filters.
|
||||
*
|
||||
* @return
|
||||
* An array of search results. To use the default search result
|
||||
* display, each item should have the following keys':
|
||||
* - 'link': Required. The URL of the found item.
|
||||
* - 'type': The type of item (such as the content type).
|
||||
* - 'title': Required. The name of the item.
|
||||
* - 'user': The author of the item.
|
||||
* - 'date': A timestamp when the item was last modified.
|
||||
* - 'extra': An array of optional extra information items.
|
||||
* - 'snippet': An excerpt or preview to show with the result (can be
|
||||
* generated with search_excerpt()).
|
||||
* - 'language': Language code for the item (usually two characters).
|
||||
*
|
||||
* @ingroup search
|
||||
*/
|
||||
function hook_search_execute($keys = NULL, $conditions = NULL) {
|
||||
// Build matching conditions
|
||||
$query = db_select('search_index', 'i', array('target' => 'slave'))->extend('SearchQuery')->extend('PagerDefault');
|
||||
$query->join('node', 'n', 'n.nid = i.sid');
|
||||
$query
|
||||
->condition('n.status', 1)
|
||||
->addTag('node_access')
|
||||
->searchExpression($keys, 'node');
|
||||
|
||||
// Insert special keywords.
|
||||
$query->setOption('type', 'n.type');
|
||||
$query->setOption('language', 'n.language');
|
||||
if ($query->setOption('term', 'ti.tid')) {
|
||||
$query->join('taxonomy_index', 'ti', 'n.nid = ti.nid');
|
||||
}
|
||||
// Only continue if the first pass query matches.
|
||||
if (!$query->executeFirstPass()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Add the ranking expressions.
|
||||
_node_rankings($query);
|
||||
|
||||
// Load results.
|
||||
$find = $query
|
||||
->limit(10)
|
||||
->execute();
|
||||
$results = array();
|
||||
foreach ($find as $item) {
|
||||
// Build the node body.
|
||||
$node = node_load($item->sid);
|
||||
node_build_content($node, 'search_result');
|
||||
$node->body = drupal_render($node->content);
|
||||
|
||||
// Fetch comments for snippet.
|
||||
$node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
|
||||
// Fetch terms for snippet.
|
||||
$node->rendered .= ' ' . module_invoke('taxonomy', 'node_update_index', $node);
|
||||
|
||||
$extra = module_invoke_all('node_search_result', $node);
|
||||
|
||||
$results[] = array(
|
||||
'link' => url('node/' . $item->sid, array('absolute' => TRUE)),
|
||||
'type' => check_plain(node_type_get_name($node)),
|
||||
'title' => $node->title,
|
||||
'user' => theme('username', array('account' => $node)),
|
||||
'date' => $node->changed,
|
||||
'node' => $node,
|
||||
'extra' => $extra,
|
||||
'score' => $item->calculated_score,
|
||||
'snippet' => search_excerpt($keys, $node->body),
|
||||
);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the rendering of search results.
|
||||
*
|
||||
* A module that implements hook_search_info() to define a type of search may
|
||||
* implement this hook in order to override the default theming of its search
|
||||
* results, which is otherwise themed using theme('search_results').
|
||||
*
|
||||
* Note that by default, theme('search_results') and theme('search_result')
|
||||
* work together to create an ordered list (OL). So your hook_search_page()
|
||||
* implementation should probably do this as well.
|
||||
*
|
||||
* @param $results
|
||||
* An array of search results.
|
||||
*
|
||||
* @return
|
||||
* A renderable array, which will render the formatted search results with a
|
||||
* pager included.
|
||||
*
|
||||
* @see search-result.tpl.php
|
||||
* @see search-results.tpl.php
|
||||
*/
|
||||
function hook_search_page($results) {
|
||||
$output['prefix']['#markup'] = '<ol class="search-results">';
|
||||
|
||||
foreach ($results as $entry) {
|
||||
$output[] = array(
|
||||
'#theme' => 'search_result',
|
||||
'#result' => $entry,
|
||||
'#module' => 'my_module_name',
|
||||
);
|
||||
}
|
||||
$output['suffix']['#markup'] = '</ol>' . theme('pager');
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess text for search.
|
||||
*
|
||||
* This hook is called to preprocess both the text added to the search index and
|
||||
* the keywords users have submitted for searching.
|
||||
*
|
||||
* Possible uses:
|
||||
* - Adding spaces between words of Chinese or Japanese text.
|
||||
* - Stemming words down to their root words to allow matches between, for
|
||||
* instance, walk, walked, walking, and walks in searching.
|
||||
* - Expanding abbreviations and acronymns that occur in text.
|
||||
*
|
||||
* @param $text
|
||||
* The text to preprocess. This is a single piece of plain text extracted
|
||||
* from between two HTML tags or from the search query. It will not contain
|
||||
* any HTML entities or HTML tags.
|
||||
*
|
||||
* @return
|
||||
* The text after preprocessing. Note that if your module decides not to alter
|
||||
* the text, it should return the original text. Also, after preprocessing,
|
||||
* words in the text should be separated by a space.
|
||||
*
|
||||
* @ingroup search
|
||||
*/
|
||||
function hook_search_preprocess($text) {
|
||||
// Do processing on $text
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the search index for this module.
|
||||
*
|
||||
* This hook is called every cron run if search.module is enabled, your
|
||||
* module has implemented hook_search_info(), and your module has been set as
|
||||
* an active search module on the Search settings page
|
||||
* (admin/config/search/settings). It allows your module to add items to the
|
||||
* built-in search index using search_index(), or to add them to your module's
|
||||
* own indexing mechanism.
|
||||
*
|
||||
* When implementing this hook, your module should index content items that
|
||||
* were modified or added since the last run. PHP has a time limit
|
||||
* for cron, though, so it is advisable to limit how many items you index
|
||||
* per run using variable_get('search_cron_limit') (see example below). Also,
|
||||
* since the cron run could time out and abort in the middle of your run, you
|
||||
* should update your module's internal bookkeeping on when items have last
|
||||
* been indexed as you go rather than waiting to the end of indexing.
|
||||
*
|
||||
* @ingroup search
|
||||
*/
|
||||
function hook_update_index() {
|
||||
$limit = (int)variable_get('search_cron_limit', 100);
|
||||
|
||||
$result = db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE d.sid IS NULL OR d.reindex <> 0 ORDER BY d.reindex ASC, n.nid ASC", 0, $limit);
|
||||
|
||||
foreach ($result as $node) {
|
||||
$node = node_load($node->nid);
|
||||
|
||||
// Save the changed time of the most recent indexed node, for the search
|
||||
// results half-life calculation.
|
||||
variable_set('node_cron_last', $node->changed);
|
||||
|
||||
// Render the node.
|
||||
node_build_content($node, 'search_index');
|
||||
$node->rendered = drupal_render($node->content);
|
||||
|
||||
$text = '<h1>' . check_plain($node->title) . '</h1>' . $node->rendered;
|
||||
|
||||
// Fetch extra data normally not visible
|
||||
$extra = module_invoke_all('node_update_index', $node);
|
||||
foreach ($extra as $t) {
|
||||
$text .= $t;
|
||||
}
|
||||
|
||||
// Update index
|
||||
search_index($node->nid, 'node', $text);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide search query conditions.
|
||||
*
|
||||
* Callback for hook_search_info().
|
||||
*
|
||||
* This callback is invoked by search_view() to get an array of additional
|
||||
* search conditions to pass to search_data(). For example, a search module
|
||||
* may get additional keywords, filters, or modifiers for the search from
|
||||
* the query string.
|
||||
*
|
||||
* This example pulls additional search keywords out of the $_REQUEST variable,
|
||||
* (i.e. from the query string of the request). The conditions may also be
|
||||
* generated internally - for example based on a module's settings.
|
||||
*
|
||||
* @param $keys
|
||||
* The search keywords string.
|
||||
*
|
||||
* @return
|
||||
* An array of additional conditions, such as filters.
|
||||
*
|
||||
* @ingroup callbacks
|
||||
* @ingroup search
|
||||
*/
|
||||
function callback_search_conditions($keys) {
|
||||
$conditions = array();
|
||||
|
||||
if (!empty($_REQUEST['keys'])) {
|
||||
$conditions['keys'] = $_REQUEST['keys'];
|
||||
}
|
||||
if (!empty($_REQUEST['sample_search_keys'])) {
|
||||
$conditions['sample_search_keys'] = $_REQUEST['sample_search_keys'];
|
||||
}
|
||||
if ($force_keys = config('sample_search.settings')->get('force_keywords')) {
|
||||
$conditions['sample_search_force_keywords'] = $force_keys;
|
||||
}
|
||||
return $conditions;
|
||||
}
|
34
modules/search/search.css
Normal file
34
modules/search/search.css
Normal file
|
@ -0,0 +1,34 @@
|
|||
|
||||
.search-form {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.search-form input {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.search-results {
|
||||
list-style: none;
|
||||
}
|
||||
.search-results p {
|
||||
margin-top: 0;
|
||||
}
|
||||
.search-results .title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.search-results li {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.search-results .search-snippet-info {
|
||||
padding-left: 1em; /* LTR */
|
||||
}
|
||||
.search-results .search-info {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.search-advanced .criterion {
|
||||
float: left; /* LTR */
|
||||
margin-right: 2em; /* LTR */
|
||||
}
|
||||
.search-advanced .action {
|
||||
float: left; /* LTR */
|
||||
clear: left; /* LTR */
|
||||
}
|
546
modules/search/search.extender.inc
Normal file
546
modules/search/search.extender.inc
Normal file
|
@ -0,0 +1,546 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Search query extender and helper functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Do a query on the full-text search index for a word or words.
|
||||
*
|
||||
* This function is normally only called by each module that supports the
|
||||
* indexed search (and thus, implements hook_update_index()).
|
||||
*
|
||||
* Results are retrieved in two logical passes. However, the two passes are
|
||||
* joined together into a single query. And in the case of most simple
|
||||
* queries the second pass is not even used.
|
||||
*
|
||||
* The first pass selects a set of all possible matches, which has the benefit
|
||||
* of also providing the exact result set for simple "AND" or "OR" searches.
|
||||
*
|
||||
* The second portion of the query further refines this set by verifying
|
||||
* advanced text conditions (such as negative or phrase matches).
|
||||
*
|
||||
* The used query object has the tag 'search_$module' and can be further
|
||||
* extended with hook_query_alter().
|
||||
*/
|
||||
class SearchQuery extends SelectQueryExtender {
|
||||
/**
|
||||
* The search query that is used for searching.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $searchExpression;
|
||||
|
||||
/**
|
||||
* Type of search (search module).
|
||||
*
|
||||
* This maps to the value of the type column in search_index, and is equal
|
||||
* to the machine-readable name of the module that implements
|
||||
* hook_search_info().
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Positive and negative search keys.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $keys = array('positive' => array(), 'negative' => array());
|
||||
|
||||
/**
|
||||
* Indicates whether the first pass query requires complex conditions (LIKE).
|
||||
*
|
||||
* @var boolean.
|
||||
*/
|
||||
protected $simple = TRUE;
|
||||
|
||||
/**
|
||||
* Conditions that are used for exact searches.
|
||||
*
|
||||
* This is always used for the second pass query but not for the first pass,
|
||||
* unless $this->simple is FALSE.
|
||||
*
|
||||
* @var DatabaseCondition
|
||||
*/
|
||||
protected $conditions;
|
||||
|
||||
/**
|
||||
* Indicates how many matches for a search query are necessary.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $matches = 0;
|
||||
|
||||
/**
|
||||
* Array of search words.
|
||||
*
|
||||
* These words have to match against {search_index}.word.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $words = array();
|
||||
|
||||
/**
|
||||
* Multiplier for the normalized search score.
|
||||
*
|
||||
* This value is calculated by the first pass query and multiplied with the
|
||||
* actual score of a specific word to make sure that the resulting calculated
|
||||
* score is between 0 and 1.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $normalize;
|
||||
|
||||
/**
|
||||
* Indicates whether the first pass query has been executed.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $executedFirstPass = FALSE;
|
||||
|
||||
/**
|
||||
* Stores score expressions.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @see addScore()
|
||||
*/
|
||||
protected $scores = array();
|
||||
|
||||
/**
|
||||
* Stores arguments for score expressions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scoresArguments = array();
|
||||
|
||||
/**
|
||||
* Stores multipliers for score expressions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $multiply = array();
|
||||
|
||||
/**
|
||||
* Whether or not search expressions were ignored.
|
||||
*
|
||||
* The maximum number of AND/OR combinations exceeded can be configured to
|
||||
* avoid Denial-of-Service attacks. Expressions beyond the limit are ignored.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $expressionsIgnored = FALSE;
|
||||
|
||||
/**
|
||||
* Sets up the search query expression.
|
||||
*
|
||||
* @param $query
|
||||
* A search query string, which can contain options.
|
||||
* @param $module
|
||||
* The search module. This maps to {search_index}.type in the database.
|
||||
*
|
||||
* @return
|
||||
* The SearchQuery object.
|
||||
*/
|
||||
public function searchExpression($expression, $module) {
|
||||
$this->searchExpression = $expression;
|
||||
$this->type = $module;
|
||||
|
||||
// Add a search_* tag. This needs to be added before any preExecute methods
|
||||
// for decorated queries are called, as $this->prepared will be set to TRUE
|
||||
// and tags added in the execute method will never get used. For example,
|
||||
// if $query is extended by 'SearchQuery' then 'PagerDefault', the
|
||||
// search-specific tag will be added too late (when preExecute() has
|
||||
// already been called from the PagerDefault extender), and as a
|
||||
// consequence will not be available to hook_query_alter() implementations,
|
||||
// nor will the correct hook_query_TAG_alter() implementations get invoked.
|
||||
// See node_search_execute().
|
||||
$this->addTag('search_' . $module);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a search option and removes it from the search query string.
|
||||
*
|
||||
* These options are in the form option:value,value2,value3.
|
||||
*
|
||||
* @param $option
|
||||
* Name of the option.
|
||||
* @param $column
|
||||
* Name of the database column to which the value should be applied.
|
||||
*
|
||||
* @return
|
||||
* TRUE if a value for that option was found, FALSE if not.
|
||||
*/
|
||||
public function setOption($option, $column) {
|
||||
if ($values = search_expression_extract($this->searchExpression, $option)) {
|
||||
$or = db_or();
|
||||
foreach (explode(',', $values) as $value) {
|
||||
$or->condition($column, $value);
|
||||
}
|
||||
$this->condition($or);
|
||||
$this->searchExpression = search_expression_insert($this->searchExpression, $option);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the search query into SQL conditions.
|
||||
*
|
||||
* We build two queries that match the dataset bodies.
|
||||
*/
|
||||
protected function parseSearchExpression() {
|
||||
// Matchs words optionally prefixed by a dash. A word in this case is
|
||||
// something between two spaces, optionally quoted.
|
||||
preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->searchExpression , $keywords, PREG_SET_ORDER);
|
||||
|
||||
if (count($keywords) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Classify tokens.
|
||||
$or = FALSE;
|
||||
$warning = '';
|
||||
$limit_combinations = variable_get('search_and_or_limit', 7);
|
||||
// The first search expression does not count as AND.
|
||||
$and_count = -1;
|
||||
$or_count = 0;
|
||||
foreach ($keywords as $match) {
|
||||
if ($or_count && $and_count + $or_count >= $limit_combinations) {
|
||||
// Ignore all further search expressions to prevent Denial-of-Service
|
||||
// attacks using a high number of AND/OR combinations.
|
||||
$this->expressionsIgnored = TRUE;
|
||||
break;
|
||||
}
|
||||
$phrase = FALSE;
|
||||
// Strip off phrase quotes.
|
||||
if ($match[2]{0} == '"') {
|
||||
$match[2] = substr($match[2], 1, -1);
|
||||
$phrase = TRUE;
|
||||
$this->simple = FALSE;
|
||||
}
|
||||
// Simplify keyword according to indexing rules and external
|
||||
// preprocessors. Use same process as during search indexing, so it
|
||||
// will match search index.
|
||||
$words = search_simplify($match[2]);
|
||||
// Re-explode in case simplification added more words, except when
|
||||
// matching a phrase.
|
||||
$words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY);
|
||||
// Negative matches.
|
||||
if ($match[1] == '-') {
|
||||
$this->keys['negative'] = array_merge($this->keys['negative'], $words);
|
||||
}
|
||||
// OR operator: instead of a single keyword, we store an array of all
|
||||
// OR'd keywords.
|
||||
elseif ($match[2] == 'OR' && count($this->keys['positive'])) {
|
||||
$last = array_pop($this->keys['positive']);
|
||||
// Starting a new OR?
|
||||
if (!is_array($last)) {
|
||||
$last = array($last);
|
||||
}
|
||||
$this->keys['positive'][] = $last;
|
||||
$or = TRUE;
|
||||
$or_count++;
|
||||
continue;
|
||||
}
|
||||
// AND operator: implied, so just ignore it.
|
||||
elseif ($match[2] == 'AND' || $match[2] == 'and') {
|
||||
$warning = $match[2];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Plain keyword.
|
||||
else {
|
||||
if ($match[2] == 'or') {
|
||||
$warning = $match[2];
|
||||
}
|
||||
if ($or) {
|
||||
// Add to last element (which is an array).
|
||||
$this->keys['positive'][count($this->keys['positive']) - 1] = array_merge($this->keys['positive'][count($this->keys['positive']) - 1], $words);
|
||||
}
|
||||
else {
|
||||
$this->keys['positive'] = array_merge($this->keys['positive'], $words);
|
||||
$and_count++;
|
||||
}
|
||||
}
|
||||
$or = FALSE;
|
||||
}
|
||||
|
||||
// Convert keywords into SQL statements.
|
||||
$this->conditions = db_and();
|
||||
$simple_and = FALSE;
|
||||
$simple_or = FALSE;
|
||||
// Positive matches.
|
||||
foreach ($this->keys['positive'] as $key) {
|
||||
// Group of ORed terms.
|
||||
if (is_array($key) && count($key)) {
|
||||
$simple_or = TRUE;
|
||||
$any = FALSE;
|
||||
$queryor = db_or();
|
||||
foreach ($key as $or) {
|
||||
list($num_new_scores) = $this->parseWord($or);
|
||||
$any |= $num_new_scores;
|
||||
$queryor->condition('d.data', "% $or %", 'LIKE');
|
||||
}
|
||||
if (count($queryor)) {
|
||||
$this->conditions->condition($queryor);
|
||||
// A group of OR keywords only needs to match once.
|
||||
$this->matches += ($any > 0);
|
||||
}
|
||||
}
|
||||
// Single ANDed term.
|
||||
else {
|
||||
$simple_and = TRUE;
|
||||
list($num_new_scores, $num_valid_words) = $this->parseWord($key);
|
||||
$this->conditions->condition('d.data', "% $key %", 'LIKE');
|
||||
if (!$num_valid_words) {
|
||||
$this->simple = FALSE;
|
||||
}
|
||||
// Each AND keyword needs to match at least once.
|
||||
$this->matches += $num_new_scores;
|
||||
}
|
||||
}
|
||||
if ($simple_and && $simple_or) {
|
||||
$this->simple = FALSE;
|
||||
}
|
||||
// Negative matches.
|
||||
foreach ($this->keys['negative'] as $key) {
|
||||
$this->conditions->condition('d.data', "% $key %", 'NOT LIKE');
|
||||
$this->simple = FALSE;
|
||||
}
|
||||
|
||||
if ($warning == 'or') {
|
||||
drupal_set_message(t('Search for either of the two terms with uppercase <strong>OR</strong>. For example, <strong>cats OR dogs</strong>.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for parseQuery().
|
||||
*/
|
||||
protected function parseWord($word) {
|
||||
$num_new_scores = 0;
|
||||
$num_valid_words = 0;
|
||||
// Determine the scorewords of this word/phrase.
|
||||
$split = explode(' ', $word);
|
||||
foreach ($split as $s) {
|
||||
$num = is_numeric($s);
|
||||
if ($num || drupal_strlen($s) >= variable_get('minimum_word_size', 3)) {
|
||||
if (!isset($this->words[$s])) {
|
||||
$this->words[$s] = $s;
|
||||
$num_new_scores++;
|
||||
}
|
||||
$num_valid_words++;
|
||||
}
|
||||
}
|
||||
// Return matching snippet and number of added words.
|
||||
return array($num_new_scores, $num_valid_words);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the first pass query.
|
||||
*
|
||||
* This can either be done explicitly, so that additional scores and
|
||||
* conditions can be applied to the second pass query, or implicitly by
|
||||
* addScore() or execute().
|
||||
*
|
||||
* @return
|
||||
* TRUE if search items exist, FALSE if not.
|
||||
*/
|
||||
public function executeFirstPass() {
|
||||
$this->parseSearchExpression();
|
||||
|
||||
if (count($this->words) == 0) {
|
||||
form_set_error('keys', format_plural(variable_get('minimum_word_size', 3), 'You must include at least one positive keyword with 1 character or more.', 'You must include at least one positive keyword with @count characters or more.'));
|
||||
return FALSE;
|
||||
}
|
||||
if ($this->expressionsIgnored) {
|
||||
drupal_set_message(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => variable_get('search_and_or_limit', 7))), 'warning');
|
||||
}
|
||||
$this->executedFirstPass = TRUE;
|
||||
|
||||
if (!empty($this->words)) {
|
||||
$or = db_or();
|
||||
foreach ($this->words as $word) {
|
||||
$or->condition('i.word', $word);
|
||||
}
|
||||
$this->condition($or);
|
||||
}
|
||||
// Build query for keyword normalization.
|
||||
$this->join('search_total', 't', 'i.word = t.word');
|
||||
$this
|
||||
->condition('i.type', $this->type)
|
||||
->groupBy('i.type')
|
||||
->groupBy('i.sid')
|
||||
->having('COUNT(*) >= :matches', array(':matches' => $this->matches));
|
||||
|
||||
// Clone the query object to do the firstPass query;
|
||||
$first = clone $this->query;
|
||||
|
||||
// For complex search queries, add the LIKE conditions to the first pass query.
|
||||
if (!$this->simple) {
|
||||
$first->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type');
|
||||
$first->condition($this->conditions);
|
||||
}
|
||||
|
||||
// Calculate maximum keyword relevance, to normalize it.
|
||||
$first->addExpression('SUM(i.score * t.count)', 'calculated_score');
|
||||
$this->normalize = $first
|
||||
->range(0, 1)
|
||||
->orderBy('calculated_score', 'DESC')
|
||||
->execute()
|
||||
->fetchField();
|
||||
|
||||
if ($this->normalize) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom score expression to the search query.
|
||||
*
|
||||
* Score expressions are used to order search results. If no calls to
|
||||
* addScore() have taken place, a default keyword relevance score will be
|
||||
* used. However, if at least one call to addScore() has taken place, the
|
||||
* keyword relevance score is not automatically added.
|
||||
*
|
||||
* Note that you must use this method to add ordering to your searches, and
|
||||
* not call orderBy() directly, when using the SearchQuery extender. This is
|
||||
* because of the two-pass system the SearchQuery class uses to normalize
|
||||
* scores.
|
||||
*
|
||||
* @param $score
|
||||
* The score expression, which should evaluate to a number between 0 and 1.
|
||||
* The string 'i.relevance' in a score expression will be replaced by a
|
||||
* measure of keyword relevance between 0 and 1.
|
||||
* @param $arguments
|
||||
* Query arguments needed to provide values to the score expression.
|
||||
* @param $multiply
|
||||
* If set, the score is multiplied with this value. However, all scores
|
||||
* with multipliers are then divided by the total of all multipliers, so
|
||||
* that overall, the normalization is maintained.
|
||||
*
|
||||
* @return object
|
||||
* The updated query object.
|
||||
*/
|
||||
public function addScore($score, $arguments = array(), $multiply = FALSE) {
|
||||
if ($multiply) {
|
||||
$i = count($this->multiply);
|
||||
// Modify the score expression so it is multiplied by the multiplier,
|
||||
// with a divisor to renormalize.
|
||||
$score = "CAST(:multiply_$i AS DECIMAL) * COALESCE(( " . $score . "), 0) / CAST(:total_$i AS DECIMAL)";
|
||||
// Add an argument for the multiplier. The :total_$i argument is taken
|
||||
// care of in the execute() method, which is when the total divisor is
|
||||
// calculated.
|
||||
$arguments[':multiply_' . $i] = $multiply;
|
||||
$this->multiply[] = $multiply;
|
||||
}
|
||||
|
||||
$this->scores[] = $score;
|
||||
$this->scoresArguments += $arguments;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the search.
|
||||
*
|
||||
* If not already done, this executes the first pass query. Then the complex
|
||||
* conditions are applied to the query including score expressions and
|
||||
* ordering.
|
||||
*
|
||||
* @return
|
||||
* FALSE if the first pass query returned no results, and a database result
|
||||
* set if there were results.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if (!$this->executedFirstPass) {
|
||||
$this->executeFirstPass();
|
||||
}
|
||||
if (!$this->normalize) {
|
||||
return new DatabaseStatementEmpty();
|
||||
}
|
||||
|
||||
// Add conditions to query.
|
||||
$this->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type');
|
||||
$this->condition($this->conditions);
|
||||
|
||||
if (empty($this->scores)) {
|
||||
// Add default score.
|
||||
$this->addScore('i.relevance');
|
||||
}
|
||||
|
||||
if (count($this->multiply)) {
|
||||
// Re-normalize scores with multipliers by dividing by the total of all
|
||||
// multipliers. The expressions were altered in addScore(), so here just
|
||||
// add the arguments for the total.
|
||||
$i = 0;
|
||||
$sum = array_sum($this->multiply);
|
||||
foreach ($this->multiply as $total) {
|
||||
$this->scoresArguments[':total_' . $i] = $sum;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the pseudo-expression 'i.relevance' with a measure of keyword
|
||||
// relevance in all score expressions, using string replacement. Careful
|
||||
// though! If you just print out a float, some locales use ',' as the
|
||||
// decimal separator in PHP, while SQL always uses '.'. So, make sure to
|
||||
// set the number format correctly.
|
||||
$relevance = number_format((1.0 / $this->normalize), 10, '.', '');
|
||||
$this->scores = str_replace('i.relevance', '(' . $relevance . ' * i.score * t.count)', $this->scores);
|
||||
|
||||
// Add all scores together to form a query field.
|
||||
$this->addExpression('SUM(' . implode(' + ', $this->scores) . ')', 'calculated_score', $this->scoresArguments);
|
||||
|
||||
// If an order has not yet been set for this query, add a default order
|
||||
// that sorts by the calculated sum of scores.
|
||||
if (count($this->getOrderBy()) == 0) {
|
||||
$this->orderBy('calculated_score', 'DESC');
|
||||
}
|
||||
|
||||
// Add useful metadata.
|
||||
$this
|
||||
->addMetaData('normalize', $this->normalize)
|
||||
->fields('i', array('type', 'sid'));
|
||||
|
||||
return $this->query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the default count query for SearchQuery.
|
||||
*
|
||||
* Since SearchQuery always uses GROUP BY, we can default to a subquery. We
|
||||
* also add the same conditions as execute() because countQuery() is called
|
||||
* first.
|
||||
*/
|
||||
public function countQuery() {
|
||||
// Clone the inner query.
|
||||
$inner = clone $this->query;
|
||||
|
||||
// Add conditions to query.
|
||||
$inner->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type');
|
||||
$inner->condition($this->conditions);
|
||||
|
||||
// Remove existing fields and expressions, they are not needed for a count
|
||||
// query.
|
||||
$fields =& $inner->getFields();
|
||||
$fields = array();
|
||||
$expressions =& $inner->getExpressions();
|
||||
$expressions = array();
|
||||
|
||||
// Add the sid as the only field and count them as a subquery.
|
||||
$count = db_select($inner->fields('i', array('sid')), NULL, array('target' => 'slave'));
|
||||
|
||||
// Add the COUNT() expression.
|
||||
$count->addExpression('COUNT(*)');
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
15
modules/search/search.info
Normal file
15
modules/search/search.info
Normal file
|
@ -0,0 +1,15 @@
|
|||
name = Search
|
||||
description = Enables site-wide keyword searching.
|
||||
package = Core
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
files[] = search.extender.inc
|
||||
files[] = search.test
|
||||
configure = admin/config/search/settings
|
||||
stylesheets[all][] = search.css
|
||||
|
||||
; Information added by Drupal.org packaging script on 2017-06-21
|
||||
version = "7.56"
|
||||
project = "drupal"
|
||||
datestamp = "1498069849"
|
||||
|
183
modules/search/search.install
Normal file
183
modules/search/search.install
Normal file
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the search module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function search_uninstall() {
|
||||
variable_del('minimum_word_size');
|
||||
variable_del('overlap_cjk');
|
||||
variable_del('search_cron_limit');
|
||||
variable_del('search_logging');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function search_schema() {
|
||||
$schema['search_dataset'] = array(
|
||||
'description' => 'Stores items that will be searched.',
|
||||
'fields' => array(
|
||||
'sid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Search item ID, e.g. node ID for nodes.',
|
||||
),
|
||||
'type' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 16,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Type of item, e.g. node.',
|
||||
),
|
||||
'data' => array(
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'description' => 'List of space-separated words from the item.',
|
||||
),
|
||||
'reindex' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Set to force node reindexing.',
|
||||
),
|
||||
),
|
||||
'primary key' => array('sid', 'type'),
|
||||
);
|
||||
|
||||
$schema['search_index'] = array(
|
||||
'description' => 'Stores the search index, associating words, items and scores.',
|
||||
'fields' => array(
|
||||
'word' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 50,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The {search_total}.word that is associated with the search item.',
|
||||
),
|
||||
'sid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'The {search_dataset}.sid of the searchable item to which the word belongs.',
|
||||
),
|
||||
'type' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 16,
|
||||
'not null' => TRUE,
|
||||
'description' => 'The {search_dataset}.type of the searchable item to which the word belongs.',
|
||||
),
|
||||
'score' => array(
|
||||
'type' => 'float',
|
||||
'not null' => FALSE,
|
||||
'description' => 'The numeric score of the word, higher being more important.',
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'sid_type' => array('sid', 'type'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'search_dataset' => array(
|
||||
'table' => 'search_dataset',
|
||||
'columns' => array(
|
||||
'sid' => 'sid',
|
||||
'type' => 'type',
|
||||
),
|
||||
),
|
||||
),
|
||||
'primary key' => array('word', 'sid', 'type'),
|
||||
);
|
||||
|
||||
$schema['search_total'] = array(
|
||||
'description' => 'Stores search totals for words.',
|
||||
'fields' => array(
|
||||
'word' => array(
|
||||
'description' => 'Primary Key: Unique word in the search index.',
|
||||
'type' => 'varchar',
|
||||
'length' => 50,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'count' => array(
|
||||
'description' => "The count of the word in the index using Zipf's law to equalize the probability distribution.",
|
||||
'type' => 'float',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('word'),
|
||||
);
|
||||
|
||||
$schema['search_node_links'] = array(
|
||||
'description' => 'Stores items (like nodes) that link to other nodes, used to improve search scores for nodes that are frequently linked to.',
|
||||
'fields' => array(
|
||||
'sid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'The {search_dataset}.sid of the searchable item containing the link to the node.',
|
||||
),
|
||||
'type' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 16,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The {search_dataset}.type of the searchable item containing the link to the node.',
|
||||
),
|
||||
'nid' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'The {node}.nid that this item links to.',
|
||||
),
|
||||
'caption' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
'description' => 'The text used to link to the {node}.nid.',
|
||||
),
|
||||
),
|
||||
'primary key' => array('sid', 'type', 'nid'),
|
||||
'indexes' => array(
|
||||
'nid' => array('nid'),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace unique keys in 'search_dataset' and 'search_index' by primary keys.
|
||||
*/
|
||||
function search_update_7000() {
|
||||
db_drop_unique_key('search_dataset', 'sid_type');
|
||||
$dataset_type_spec = array(
|
||||
'type' => 'varchar',
|
||||
'length' => 16,
|
||||
'not null' => TRUE,
|
||||
'description' => 'Type of item, e.g. node.',
|
||||
);
|
||||
db_change_field('search_dataset', 'type', 'type', $dataset_type_spec);
|
||||
db_add_primary_key('search_dataset', array('sid', 'type'));
|
||||
|
||||
db_drop_index('search_index', 'word');
|
||||
db_drop_unique_key('search_index', 'word_sid_type');
|
||||
$index_type_spec = array(
|
||||
'type' => 'varchar',
|
||||
'length' => 16,
|
||||
'not null' => TRUE,
|
||||
'description' => 'The {search_dataset}.type of the searchable item to which the word belongs.',
|
||||
);
|
||||
db_change_field('search_index', 'type', 'type', $index_type_spec);
|
||||
db_add_primary_key('search_index', array('word', 'sid', 'type'));
|
||||
}
|
||||
|
1356
modules/search/search.module
Normal file
1356
modules/search/search.module
Normal file
File diff suppressed because it is too large
Load diff
159
modules/search/search.pages.inc
Normal file
159
modules/search/search.pages.inc
Normal file
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* User page callbacks for the search module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Menu callback; presents the search form and/or search results.
|
||||
*
|
||||
* @param $module
|
||||
* Search module to use for the search.
|
||||
* @param $keys
|
||||
* Keywords to use for the search.
|
||||
*/
|
||||
function search_view($module = NULL, $keys = '') {
|
||||
$info = FALSE;
|
||||
$keys = trim($keys);
|
||||
// Also try to pull search keywords out of the $_REQUEST variable to
|
||||
// support old GET format of searches for existing links.
|
||||
if (!$keys && !empty($_REQUEST['keys'])) {
|
||||
$keys = trim($_REQUEST['keys']);
|
||||
}
|
||||
|
||||
if (!empty($module)) {
|
||||
$active_module_info = search_get_info();
|
||||
if (isset($active_module_info[$module])) {
|
||||
$info = $active_module_info[$module];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($info)) {
|
||||
// No path or invalid path: find the default module. Note that if there
|
||||
// are no enabled search modules, this function should never be called,
|
||||
// since hook_menu() would not have defined any search paths.
|
||||
$info = search_get_default_module_info();
|
||||
// Redirect from bare /search or an invalid path to the default search path.
|
||||
$path = 'search/' . $info['path'];
|
||||
if ($keys) {
|
||||
$path .= '/' . $keys;
|
||||
}
|
||||
drupal_goto($path);
|
||||
}
|
||||
|
||||
// Default results output is an empty string.
|
||||
$results = array('#markup' => '');
|
||||
// Process the search form. Note that if there is $_POST data,
|
||||
// search_form_submit() will cause a redirect to search/[module path]/[keys],
|
||||
// which will get us back to this page callback. In other words, the search
|
||||
// form submits with POST but redirects to GET. This way we can keep
|
||||
// the search query URL clean as a whistle.
|
||||
if (empty($_POST['form_id']) || ($_POST['form_id'] != 'search_form' && $_POST['form_id'] != 'search_block_form')) {
|
||||
$conditions = NULL;
|
||||
if (isset($info['conditions_callback']) && function_exists($info['conditions_callback'])) {
|
||||
// Build an optional array of more search conditions.
|
||||
$conditions = call_user_func($info['conditions_callback'], $keys);
|
||||
}
|
||||
// Only search if there are keywords or non-empty conditions.
|
||||
if ($keys || !empty($conditions)) {
|
||||
if (variable_get('search_logging', TRUE)) {
|
||||
// Log the search keys.
|
||||
watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $info['path'] . '/' . $keys));
|
||||
}
|
||||
// Collect the search results.
|
||||
$results = search_data($keys, $info['module'], $conditions);
|
||||
}
|
||||
}
|
||||
// The form may be altered based on whether the search was run.
|
||||
$build['search_form'] = drupal_get_form('search_form', NULL, $keys, $info['module']);
|
||||
$build['search_results'] = $results;
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process variables for search-results.tpl.php.
|
||||
*
|
||||
* The $variables array contains the following arguments:
|
||||
* - $results: Search results array.
|
||||
* - $module: Module the search results came from (module implementing
|
||||
* hook_search_info()).
|
||||
*
|
||||
* @see search-results.tpl.php
|
||||
*/
|
||||
function template_preprocess_search_results(&$variables) {
|
||||
$variables['search_results'] = '';
|
||||
if (!empty($variables['module'])) {
|
||||
$variables['module'] = check_plain($variables['module']);
|
||||
}
|
||||
foreach ($variables['results'] as $result) {
|
||||
$variables['search_results'] .= theme('search_result', array('result' => $result, 'module' => $variables['module']));
|
||||
}
|
||||
$variables['pager'] = theme('pager', array('tags' => NULL));
|
||||
$variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process variables for search-result.tpl.php.
|
||||
*
|
||||
* The $variables array contains the following arguments:
|
||||
* - $result
|
||||
* - $module
|
||||
*
|
||||
* @see search-result.tpl.php
|
||||
*/
|
||||
function template_preprocess_search_result(&$variables) {
|
||||
global $language;
|
||||
|
||||
$result = $variables['result'];
|
||||
$variables['url'] = check_url($result['link']);
|
||||
$variables['title'] = check_plain($result['title']);
|
||||
if (isset($result['language']) && $result['language'] != $language->language && $result['language'] != LANGUAGE_NONE) {
|
||||
$variables['title_attributes_array']['xml:lang'] = $result['language'];
|
||||
$variables['content_attributes_array']['xml:lang'] = $result['language'];
|
||||
}
|
||||
|
||||
$info = array();
|
||||
if (!empty($result['module'])) {
|
||||
$info['module'] = check_plain($result['module']);
|
||||
}
|
||||
if (!empty($result['user'])) {
|
||||
$info['user'] = $result['user'];
|
||||
}
|
||||
if (!empty($result['date'])) {
|
||||
$info['date'] = format_date($result['date'], 'short');
|
||||
}
|
||||
if (isset($result['extra']) && is_array($result['extra'])) {
|
||||
$info = array_merge($info, $result['extra']);
|
||||
}
|
||||
// Check for existence. User search does not include snippets.
|
||||
$variables['snippet'] = isset($result['snippet']) ? $result['snippet'] : '';
|
||||
// Provide separated and grouped meta information..
|
||||
$variables['info_split'] = $info;
|
||||
$variables['info'] = implode(' - ', $info);
|
||||
$variables['theme_hook_suggestions'][] = 'search_result__' . $variables['module'];
|
||||
}
|
||||
|
||||
/**
|
||||
* As the search form collates keys from other modules hooked in via
|
||||
* hook_form_alter, the validation takes place in _submit.
|
||||
* search_form_validate() is used solely to set the 'processed_keys' form
|
||||
* value for the basic search form.
|
||||
*/
|
||||
function search_form_validate($form, &$form_state) {
|
||||
form_set_value($form['basic']['processed_keys'], trim($form_state['values']['keys']), $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a search form submission.
|
||||
*/
|
||||
function search_form_submit($form, &$form_state) {
|
||||
$keys = $form_state['values']['processed_keys'];
|
||||
if ($keys == '') {
|
||||
form_set_error('keys', t('Please enter some keywords.'));
|
||||
// Fall through to the form redirect.
|
||||
}
|
||||
|
||||
$form_state['redirect'] = $form_state['action'] . '/' . $keys;
|
||||
}
|
2175
modules/search/search.test
Normal file
2175
modules/search/search.test
Normal file
File diff suppressed because it is too large
Load diff
333
modules/search/tests/UnicodeTest.txt
Normal file
333
modules/search/tests/UnicodeTest.txt
Normal file
|
@ -0,0 +1,333 @@
|
|||
|
||||
!"#$%&'()*+,-./
|
||||
0123456789
|
||||
:;<=>?@
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
[\]^_`
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
{|}~
¡¢£¤¥¦§¨©
|
||||
ª
|
||||
«¬®¯°±
|
||||
²³
|
||||
´
|
||||
µ
|
||||
¶·¸
|
||||
¹º
|
||||
»
|
||||
¼½¾
|
||||
¿
|
||||
ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ
|
||||
×
|
||||
ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö
|
||||
÷
|
||||
øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿˀˁ
|
||||
˂˃˄˅
|
||||
ˆˇˈˉˊˋˌˍˎˏːˑ
|
||||
˒˓˔˕˖˗˘˙˚˛˜˝˞˟
|
||||
ˠˡˢˣˤ
|
||||
˥˦˧˨˩˪˫
|
||||
ˬ
|
||||
˭
|
||||
ˮ
|
||||
˯˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿
|
||||
̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡ͰͱͲͳʹ
|
||||
͵
|
||||
Ͷͷͺͻͼͽ
|
||||
;΄΅
|
||||
Ά
|
||||
·
|
||||
ΈΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵ
|
||||
϶
|
||||
ϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ
|
||||
҂
|
||||
҃҄҅҆҇҈҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖՙ
|
||||
՚՛՜՝՞՟
|
||||
աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև
|
||||
։֊
|
||||
ְֱֲֳִֵֶַָֹֺֻּֽ֑֖֛֢֣֤֥֦֧֪֚֭֮֒֓֔֕֗֘֙֜֝֞֟֠֡֨֩֫֬֯
|
||||
־
|
||||
ֿ
|
||||
׀
|
||||
ׁׂ
|
||||
׃
|
||||
ׅׄ
|
||||
׆
|
||||
ׇאבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ
|
||||
׳״؆؇؈؉؊؋،؍؎؏
|
||||
ؘؙؚؐؑؒؓؔؕؖؗ
|
||||
؛؞؟
|
||||
ءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّْٕٖٜٓٔٗ٘ٙٚٛٝٞ٠١٢٣٤٥٦٧٨٩
|
||||
٪٫٬٭
|
||||
ٮٯٰٱٲٳٴٵٶٷٸٹٺٻټٽپٿڀځڂڃڄڅچڇڈډڊڋڌڍڎڏڐڑڒړڔڕږڗژڙښڛڜڝڞڟڠڡڢڣڤڥڦڧڨکڪګڬڭڮگڰڱڲڳڴڵڶڷڸڹںڻڼڽھڿۀہۂۃۄۅۆۇۈۉۊۋیۍێۏېۑےۓ
|
||||
۔
|
||||
ەۖۗۘۙۚۛۜ
|
||||
|
||||
۞ۣ۟۠ۡۢۤۥۦۧۨ
|
||||
۩
|
||||
۪ۭ۫۬ۮۯ۰۱۲۳۴۵۶۷۸۹ۺۻۼ
|
||||
۽۾
|
||||
ۿ
|
||||
܀܁܂܃܄܅܆܇܈܉܊܋܌܍
|
||||
ܐܑܒܓܔܕܖܗܘܙܚܛܜܝܞܟܠܡܢܣܤܥܦܧܨܩܪܫܬܭܮܯܱܴܷܸܹܻܼܾ݂݄݆݈ܰܲܳܵܶܺܽܿ݀݁݃݅݇݉݊ݍݎݏݐݑݒݓݔݕݖݗݘݙݚݛݜݝݞݟݠݡݢݣݤݥݦݧݨݩݪݫݬݭݮݯݰݱݲݳݴݵݶݷݸݹݺݻݼݽݾݿހށނރބޅކއވމފދތލގޏސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥަާިީުޫެޭޮޯްޱ߀߁߂߃߄߅߆߇߈߉ߊߋߌߍߎߏߐߑߒߓߔߕߖߗߘߙߚߛߜߝߞߟߠߡߢߣߤߥߦߧߨߩߪ߲߫߬߭߮߯߰߱߳ߴߵ
|
||||
߶߷߸߹
|
||||
ߺࠀࠁࠂࠃࠄࠅࠆࠇࠈࠉࠊࠋࠌࠍࠎࠏࠐࠑࠒࠓࠔࠕࠖࠗ࠘࠙ࠚࠛࠜࠝࠞࠟࠠࠡࠢࠣࠤࠥࠦࠧࠨࠩࠪࠫࠬ࠭
|
||||
࠰࠱࠲࠳࠴࠵࠶࠷࠸࠹࠺࠻࠼࠽࠾
|
||||
ऀँंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयरऱलळऴवशषसह़ऽािीुूृॄॅॆेैॉॊोौ्ॎॐ॒॑॓॔ॕक़ख़ग़ज़ड़ढ़फ़य़ॠॡॢॣ
|
||||
।॥
|
||||
०१२३४५६७८९
|
||||
॰
|
||||
ॱॲॹॺॻॼॽॾॿঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ০১২৩৪৫৬৭৮৯ৰৱ
|
||||
৲৳
|
||||
৴৵৶৷৸৹
|
||||
৺৻
|
||||
ਁਂਃਅਆਇਈਉਊਏਐਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨਪਫਬਭਮਯਰਲਲ਼ਵਸ਼ਸਹ਼ਾਿੀੁੂੇੈੋੌ੍ੑਖ਼ਗ਼ਜ਼ੜਫ਼੦੧੨੩੪੫੬੭੮੯ੰੱੲੳੴੵઁંઃઅઆઇઈઉઊઋઌઍએઐઑઓઔકખગઘઙચછજઝઞટઠડઢણતથદધનપફબભમયરલળવશષસહ઼ઽાિીુૂૃૄૅેૈૉોૌ્ૐૠૡૢૣ૦૧૨૩૪૫૬૭૮૯
|
||||
૱
|
||||
ଁଂଃଅଆଇଈଉଊଋଌଏଐଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନପଫବଭମଯରଲଳଵଶଷସହ଼ଽାିୀୁୂୃୄେୈୋୌ୍ୖୗଡ଼ଢ଼ୟୠୡୢୣ୦୧୨୩୪୫୬୭୮୯
|
||||
୰
|
||||
ୱஂஃஅஆஇஈஉஊஎஏஐஒஓஔகஙசஜஞடணதநனபமயரறலளழவஶஷஸஹாிீுூெேைொோௌ்ௐௗ௦௧௨௩௪௫௬௭௮௯௰௱௲
|
||||
௳௴௵௶௷௸௹௺
|
||||
ఁంఃఅఆఇఈఉఊఋఌఎఏఐఒఓఔకఖగఘఙచఛజఝఞటఠడఢణతథదధనపఫబభమయరఱలళవశషసహఽాిీుూృౄెేైొోౌ్ౕౖౘౙౠౡౢౣ౦౧౨౩౪౫౬౭౮౯౸౹౺౻౼౽౾
|
||||
౿
|
||||
ಂಃಅಆಇಈಉಊಋಌಎಏಐಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನಪಫಬಭಮಯರಱಲಳವಶಷಸಹ಼ಽಾಿೀುೂೃೄೆೇೈೊೋೌ್ೕೖೞೠೡೢೣ೦೧೨೩೪೫೬೭೮೯
|
||||
ೱೲ
|
||||
ംഃഅആഇഈഉഊഋഌഎഏഐഒഓഔകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനപഫബഭമയരറലളഴവശഷസഹഽാിീുൂൃൄെേൈൊോൌ്ൗൠൡൢൣ൦൧൨൩൪൫൬൭൮൯൰൱൲൳൴൵
|
||||
൹
|
||||
ൺൻർൽൾൿංඃඅආඇඈඉඊඋඌඍඎඏඐඑඒඓඔඕඖකඛගඝඞඟචඡජඣඤඥඦටඨඩඪණඬතථදධනඳපඵබභමඹයරලවශෂසහළෆ්ාැෑිීුූෘෙේෛොෝෞෟෲෳ
|
||||
෴
|
||||
กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู
|
||||
฿
|
||||
เแโใไๅๆ็่้๊๋์ํ๎
|
||||
๏
|
||||
๐๑๒๓๔๕๖๗๘๙
|
||||
๚๛
|
||||
ກຂຄງຈຊຍດຕຖທນບປຜຝພຟມຢຣລວສຫອຮຯະັາຳິີຶືຸູົຼຽເແໂໃໄໆ່້໊໋໌ໍ໐໑໒໓໔໕໖໗໘໙ໜໝༀ
|
||||
༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗
|
||||
༘༙
|
||||
༚༛༜༝༞༟
|
||||
༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳
|
||||
༴
|
||||
༵
|
||||
༶
|
||||
༷
|
||||
༸
|
||||
༹
|
||||
༺༻༼༽
|
||||
༾༿ཀཁགགྷངཅཆཇཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ
|
||||
྅
|
||||
྆྇ྈྉྊྋྐྑྒྒྷྔྕྖྗྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ
|
||||
྾྿࿀࿁࿂࿃࿄࿅
|
||||
࿆
|
||||
࿇࿈࿉࿊࿋࿌࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘
|
||||
ကခဂဃငစဆဇဈဉညဋဌဍဎဏတထဒဓနပဖဗဘမယရလဝသဟဠအဢဣဤဥဦဧဨဩဪါာိီုူေဲဳဴဵံ့း္်ျြွှဿ၀၁၂၃၄၅၆၇၈၉
|
||||
၊။၌၍၎၏
|
||||
ၐၑၒၓၔၕၖၗၘၙၚၛၜၝၞၟၠၡၢၣၤၥၦၧၨၩၪၫၬၭၮၯၰၱၲၳၴၵၶၷၸၹၺၻၼၽၾၿႀႁႂႃႄႅႆႇႈႉႊႋႌႍႎႏ႐႑႒႓႔႕႖႗႘႙ႚႛႜႝ
|
||||
႞႟
|
||||
ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅაბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰჱჲჳჴჵჶჷჸჹჺ
|
||||
჻
|
||||
ჼᄀᄁᄂᄃᄄᄅᄆᄇᄈᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒᄓᄔᄕᄖᄗᄘᄙᄚᄛᄜᄝᄞᄟᄠᄡᄢᄣᄤᄥᄦᄧᄨᄩᄪᄫᄬᄭᄮᄯᄰᄱᄲᄳᄴᄵᄶᄷᄸᄹᄺᄻᄼᄽᄾᄿᅀᅁᅂᅃᅄᅅᅆᅇᅈᅉᅊᅋᅌᅍᅎᅏᅐᅑᅒᅓᅔᅕᅖᅗᅘᅙᅚᅛᅜᅝᅞᅟᅠᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵᅶᅷᅸᅹᅺᅻᅼᅽᅾᅿᆀᆁᆂᆃᆄᆅᆆᆇᆈᆉᆊᆋᆌᆍᆎᆏᆐᆑᆒᆓᆔᆕᆖᆗᆘᆙᆚᆛᆜᆝᆞᆟᆠᆡᆢᆣᆤᆥᆦᆧᆨᆩᆪᆫᆬᆭᆮᆯᆰᆱᆲᆳᆴᆵᆶᆷᆸᆹᆺᆻᆼᆽᆾᆿᇀᇁᇂᇃᇄᇅᇆᇇᇈᇉᇊᇋᇌᇍᇎᇏᇐᇑᇒᇓᇔᇕᇖᇗᇘᇙᇚᇛᇜᇝᇞᇟᇠᇡᇢᇣᇤᇥᇦᇧᇨᇩᇪᇫᇬᇭᇮᇯᇰᇱᇲᇳᇴᇵᇶᇷᇸᇹᇺᇻᇼᇽᇾᇿሀሁሂሃሄህሆሇለሉሊላሌልሎሏሐሑሒሓሔሕሖሗመሙሚማሜምሞሟሠሡሢሣሤሥሦሧረሩሪራሬርሮሯሰሱሲሳሴስሶሷሸሹሺሻሼሽሾሿቀቁቂቃቄቅቆቇቈቊቋቌቍቐቑቒቓቔቕቖቘቚቛቜቝበቡቢባቤብቦቧቨቩቪቫቬቭቮቯተቱቲታቴትቶቷቸቹቺቻቼችቾቿኀኁኂኃኄኅኆኇኈኊኋኌኍነኑኒናኔንኖኗኘኙኚኛኜኝኞኟአኡኢኣኤእኦኧከኩኪካኬክኮኯኰኲኳኴኵኸኹኺኻኼኽኾዀዂዃዄዅወዉዊዋዌውዎዏዐዑዒዓዔዕዖዘዙዚዛዜዝዞዟዠዡዢዣዤዥዦዧየዩዪያዬይዮዯደዱዲዳዴድዶዷዸዹዺዻዼዽዾዿጀጁጂጃጄጅጆጇገጉጊጋጌግጎጏጐጒጓጔጕጘጙጚጛጜጝጞጟጠጡጢጣጤጥጦጧጨጩጪጫጬጭጮጯጰጱጲጳጴጵጶጷጸጹጺጻጼጽጾጿፀፁፂፃፄፅፆፇፈፉፊፋፌፍፎፏፐፑፒፓፔፕፖፗፘፙፚ፟
|
||||
፠፡።፣፤፥፦፧፨
|
||||
፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼ᎀᎁᎂᎃᎄᎅᎆᎇᎈᎉᎊᎋᎌᎍᎎᎏ
|
||||
᎐᎑᎒᎓᎔᎕᎖᎗᎘᎙
|
||||
ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩᎪᎫᎬᎭᎮᎯᎰᎱᎲᎳᎴᎵᎶᎷᎸᎹᎺᎻᎼᎽᎾᎿᏀᏁᏂᏃᏄᏅᏆᏇᏈᏉᏊᏋᏌᏍᏎᏏᏐᏑᏒᏓᏔᏕᏖᏗᏘᏙᏚᏛᏜᏝᏞᏟᏠᏡᏢᏣᏤᏥᏦᏧᏨᏩᏪᏫᏬᏭᏮᏯᏰᏱᏲᏳᏴ
|
||||
᐀
|
||||
ᐁᐂᐃᐄᐅᐆᐇᐈᐉᐊᐋᐌᐍᐎᐏᐐᐑᐒᐓᐔᐕᐖᐗᐘᐙᐚᐛᐜᐝᐞᐟᐠᐡᐢᐣᐤᐥᐦᐧᐨᐩᐪᐫᐬᐭᐮᐯᐰᐱᐲᐳᐴᐵᐶᐷᐸᐹᐺᐻᐼᐽᐾᐿᑀᑁᑂᑃᑄᑅᑆᑇᑈᑉᑊᑋᑌᑍᑎᑏᑐᑑᑒᑓᑔᑕᑖᑗᑘᑙᑚᑛᑜᑝᑞᑟᑠᑡᑢᑣᑤᑥᑦᑧᑨᑩᑪᑫᑬᑭᑮᑯᑰᑱᑲᑳᑴᑵᑶᑷᑸᑹᑺᑻᑼᑽᑾᑿᒀᒁᒂᒃᒄᒅᒆᒇᒈᒉᒊᒋᒌᒍᒎᒏᒐᒑᒒᒓᒔᒕᒖᒗᒘᒙᒚᒛᒜᒝᒞᒟᒠᒡᒢᒣᒤᒥᒦᒧᒨᒩᒪᒫᒬᒭᒮᒯᒰᒱᒲᒳᒴᒵᒶᒷᒸᒹᒺᒻᒼᒽᒾᒿᓀᓁᓂᓃᓄᓅᓆᓇᓈᓉᓊᓋᓌᓍᓎᓏᓐᓑᓒᓓᓔᓕᓖᓗᓘᓙᓚᓛᓜᓝᓞᓟᓠᓡᓢᓣᓤᓥᓦᓧᓨᓩᓪᓫᓬᓭᓮᓯᓰᓱᓲᓳᓴᓵᓶᓷᓸᓹᓺᓻᓼᓽᓾᓿᔀᔁᔂᔃᔄᔅᔆᔇᔈᔉᔊᔋᔌᔍᔎᔏᔐᔑᔒᔓᔔᔕᔖᔗᔘᔙᔚᔛᔜᔝᔞᔟᔠᔡᔢᔣᔤᔥᔦᔧᔨᔩᔪᔫᔬᔭᔮᔯᔰᔱᔲᔳᔴᔵᔶᔷᔸᔹᔺᔻᔼᔽᔾᔿᕀᕁᕂᕃᕄᕅᕆᕇᕈᕉᕊᕋᕌᕍᕎᕏᕐᕑᕒᕓᕔᕕᕖᕗᕘᕙᕚᕛᕜᕝᕞᕟᕠᕡᕢᕣᕤᕥᕦᕧᕨᕩᕪᕫᕬᕭᕮᕯᕰᕱᕲᕳᕴᕵᕶᕷᕸᕹᕺᕻᕼᕽᕾᕿᖀᖁᖂᖃᖄᖅᖆᖇᖈᖉᖊᖋᖌᖍᖎᖏᖐᖑᖒᖓᖔᖕᖖᖗᖘᖙᖚᖛᖜᖝᖞᖟᖠᖡᖢᖣᖤᖥᖦᖧᖨᖩᖪᖫᖬᖭᖮᖯᖰᖱᖲᖳᖴᖵᖶᖷᖸᖹᖺᖻᖼᖽᖾᖿᗀᗁᗂᗃᗄᗅᗆᗇᗈᗉᗊᗋᗌᗍᗎᗏᗐᗑᗒᗓᗔᗕᗖᗗᗘᗙᗚᗛᗜᗝᗞᗟᗠᗡᗢᗣᗤᗥᗦᗧᗨᗩᗪᗫᗬᗭᗮᗯᗰᗱᗲᗳᗴᗵᗶᗷᗸᗹᗺᗻᗼᗽᗾᗿᘀᘁᘂᘃᘄᘅᘆᘇᘈᘉᘊᘋᘌᘍᘎᘏᘐᘑᘒᘓᘔᘕᘖᘗᘘᘙᘚᘛᘜᘝᘞᘟᘠᘡᘢᘣᘤᘥᘦᘧᘨᘩᘪᘫᘬᘭᘮᘯᘰᘱᘲᘳᘴᘵᘶᘷᘸᘹᘺᘻᘼᘽᘾᘿᙀᙁᙂᙃᙄᙅᙆᙇᙈᙉᙊᙋᙌᙍᙎᙏᙐᙑᙒᙓᙔᙕᙖᙗᙘᙙᙚᙛᙜᙝᙞᙟᙠᙡᙢᙣᙤᙥᙦᙧᙨᙩᙪᙫᙬ
|
||||
᙭᙮
|
||||
ᙯᙰᙱᙲᙳᙴᙵᙶᙷᙸᙹᙺᙻᙼᙽᙾᙿ
|
||||
|
||||
ᚁᚂᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌᚍᚎᚏᚐᚑᚒᚓᚔᚕᚖᚗᚘᚙᚚ
|
||||
᚛᚜
|
||||
ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ
|
||||
᛫᛬᛭
|
||||
ᛮᛯᛰᜀᜁᜂᜃᜄᜅᜆᜇᜈᜉᜊᜋᜌᜎᜏᜐᜑᜒᜓ᜔ᜠᜡᜢᜣᜤᜥᜦᜧᜨᜩᜪᜫᜬᜭᜮᜯᜰᜱᜲᜳ᜴
|
||||
᜵᜶
|
||||
ᝀᝁᝂᝃᝄᝅᝆᝇᝈᝉᝊᝋᝌᝍᝎᝏᝐᝑᝒᝓᝠᝡᝢᝣᝤᝥᝦᝧᝨᝩᝪᝫᝬᝮᝯᝰᝲᝳកខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវឝឞសហឡអឣឤឥឦឧឨឩឪឫឬឭឮឯឰឱឲឳ
|
||||
឴឵
|
||||
ាិីឹឺុូួើឿៀេែៃោៅំះៈ៉៊់៌៍៎៏័៑្៓
|
||||
។៕៖
|
||||
ៗ
|
||||
៘៙៚៛
|
||||
ៜ៝០១២៣៤៥៦៧៨៩៰៱៲៳៴៵៶៷៸៹
|
||||
᠀᠁᠂᠃᠄᠅᠆᠇᠈᠉᠊
|
||||
᠋᠌᠍
|
||||
|
||||
᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙ᠠᠡᠢᠣᠤᠥᠦᠧᠨᠩᠪᠫᠬᠭᠮᠯᠰᠱᠲᠳᠴᠵᠶᠷᠸᠹᠺᠻᠼᠽᠾᠿᡀᡁᡂᡃᡄᡅᡆᡇᡈᡉᡊᡋᡌᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡘᡙᡚᡛᡜᡝᡞᡟᡠᡡᡢᡣᡤᡥᡦᡧᡨᡩᡪᡫᡬᡭᡮᡯᡰᡱᡲᡳᡴᡵᡶᡷᢀᢁᢂᢃᢄᢅᢆᢇᢈᢉᢊᢋᢌᢍᢎᢏᢐᢑᢒᢓᢔᢕᢖᢗᢘᢙᢚᢛᢜᢝᢞᢟᢠᢡᢢᢣᢤᢥᢦᢧᢨᢩᢪᢰᢱᢲᢳᢴᢵᢶᢷᢸᢹᢺᢻᢼᢽᢾᢿᣀᣁᣂᣃᣄᣅᣆᣇᣈᣉᣊᣋᣌᣍᣎᣏᣐᣑᣒᣓᣔᣕᣖᣗᣘᣙᣚᣛᣜᣝᣞᣟᣠᣡᣢᣣᣤᣥᣦᣧᣨᣩᣪᣫᣬᣭᣮᣯᣰᣱᣲᣳᣴᣵᤀᤁᤂᤃᤄᤅᤆᤇᤈᤉᤊᤋᤌᤍᤎᤏᤐᤑᤒᤓᤔᤕᤖᤗᤘᤙᤚᤛᤜᤠᤡᤢᤣᤤᤥᤦᤧᤨᤩᤪᤫᤰᤱᤲᤳᤴᤵᤶᤷᤸ᤻᤹᤺
|
||||
᥀᥄᥅
|
||||
᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏ᥐᥑᥒᥓᥔᥕᥖᥗᥘᥙᥚᥛᥜᥝᥞᥟᥠᥡᥢᥣᥤᥥᥦᥧᥨᥩᥪᥫᥬᥭᥰᥱᥲᥳᥴᦀᦁᦂᦃᦄᦅᦆᦇᦈᦉᦊᦋᦌᦍᦎᦏᦐᦑᦒᦓᦔᦕᦖᦗᦘᦙᦚᦛᦜᦝᦞᦟᦠᦡᦢᦣᦤᦥᦦᦧᦨᦩᦪᦫᦰᦱᦲᦳᦴᦵᦶᦷᦸᦹᦺᦻᦼᦽᦾᦿᧀᧁᧂᧃᧄᧅᧆᧇᧈᧉ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᧚
|
||||
᧞᧟᧠᧡᧢᧣᧤᧥᧦᧧᧨᧩᧪᧫᧬᧭᧮᧯᧰᧱᧲᧳᧴᧵᧶᧷᧸᧹᧺᧻᧼᧽᧾᧿
|
||||
ᨀᨁᨂᨃᨄᨅᨆᨇᨈᨉᨊᨋᨌᨍᨎᨏᨐᨑᨒᨓᨔᨕᨖᨘᨗᨙᨚᨛ
|
||||
᨞᨟
|
||||
ᨠᨡᨢᨣᨤᨥᨦᨧᨨᨩᨪᨫᨬᨭᨮᨯᨰᨱᨲᨳᨴᨵᨶᨷᨸᨹᨺᨻᨼᨽᨾᨿᩀᩁᩂᩃᩄᩅᩆᩇᩈᩉᩊᩋᩌᩍᩎᩏᩐᩑᩒᩓᩔᩕᩖᩗᩘᩙᩚᩛᩜᩝᩞ᩠ᩡᩢᩣᩤᩥᩦᩧᩨᩩᩪᩫᩬᩭᩮᩯᩰᩱᩲᩳᩴ᩿᩵᩶᩷᩸᩹᩺᩻᩼᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙
|
||||
᪠᪡᪢᪣᪤᪥᪦
|
||||
ᪧ
|
||||
᪨᪩᪪᪫᪬᪭
|
||||
ᬀᬁᬂᬃᬄᬅᬆᬇᬈᬉᬊᬋᬌᬍᬎᬏᬐᬑᬒᬓᬔᬕᬖᬗᬘᬙᬚᬛᬜᬝᬞᬟᬠᬡᬢᬣᬤᬥᬦᬧᬨᬩᬪᬫᬬᬭᬮᬯᬰᬱᬲᬳ᬴ᬵᬶᬷᬸᬹᬺᬻᬼᬽᬾᬿᭀᭁᭂᭃ᭄ᭅᭆᭇᭈᭉᭊᭋ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙
|
||||
᭚᭛᭜᭝᭞᭟᭠᭡᭢᭣᭤᭥᭦᭧᭨᭩᭪
|
||||
᭬᭫᭭᭮᭯᭰᭱᭲᭳
|
||||
᭴᭵᭶᭷᭸᭹᭺᭻᭼
|
||||
ᮀᮁᮂᮃᮄᮅᮆᮇᮈᮉᮊᮋᮌᮍᮎᮏᮐᮑᮒᮓᮔᮕᮖᮗᮘᮙᮚᮛᮜᮝᮞᮟᮠᮡᮢᮣᮤᮥᮦᮧᮨᮩ᮪ᮮᮯ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹ᰀᰁᰂᰃᰄᰅᰆᰇᰈᰉᰊᰋᰌᰍᰎᰏᰐᰑᰒᰓᰔᰕᰖᰗᰘᰙᰚᰛᰜᰝᰞᰟᰠᰡᰢᰣᰤᰥᰦᰧᰨᰩᰪᰫᰬᰭᰮᰯᰰᰱᰲᰳᰴᰵᰶ᰷
|
||||
᰻᰼᰽᰾᰿
|
||||
᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉ᱍᱎᱏ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙ᱚᱛᱜᱝᱞᱟᱠᱡᱢᱣᱤᱥᱦᱧᱨᱩᱪᱫᱬᱭᱮᱯᱰᱱᱲᱳᱴᱵᱶᱷᱸᱹᱺᱻᱼᱽ
|
||||
᱾᱿
|
||||
᳐᳑᳒
|
||||
᳓
|
||||
᳔᳕᳖᳗᳘᳙᳜᳝᳞᳟᳚᳛᳠᳡᳢᳣᳤᳥᳦᳧᳨ᳩᳪᳫᳬ᳭ᳮᳯᳰᳱᳲᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩᴪᴫᴬᴭᴮᴯᴰᴱᴲᴳᴴᴵᴶᴷᴸᴹᴺᴻᴼᴽᴾᴿᵀᵁᵂᵃᵄᵅᵆᵇᵈᵉᵊᵋᵌᵍᵎᵏᵐᵑᵒᵓᵔᵕᵖᵗᵘᵙᵚᵛᵜᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵸᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚᶛᶜᶝᶞᶟᶠᶡᶢᶣᶤᶥᶦᶧᶨᶩᶪᶫᶬᶭᶮᶯᶰᶱᶲᶳᶴᶵᶶᶷᶸᶹᶺᶻᶼᶽᶾᶿ᷐᷎᷂᷊᷏᷽᷿᷀᷁᷃᷄᷅᷆᷇᷈᷉᷋᷌᷑᷒ᷓᷔᷕᷖᷗᷘᷙᷚᷛᷜᷝᷞᷟᷠᷡᷢᷣᷤᷥᷦ᷾᷍ḀḁḂḃḄḅḆḇḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẜẝẞẟẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹỺỻỼỽỾỿἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌᾍᾎᾏᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾞᾟᾠᾡᾢᾣᾤᾥᾦᾧᾨᾩᾪᾫᾬᾭᾮᾯᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆᾼ
|
||||
᾽
|
||||
ι
|
||||
᾿῀῁
|
||||
ῂῃῄῆῇῈΈῊΉῌ
|
||||
῍῎῏
|
||||
ῐῑῒΐῖῗῘῙῚΊ
|
||||
῝῞῟
|
||||
ῠῡῢΰῤῥῦῧῨῩῪΎῬ
|
||||
῭΅`
|
||||
ῲῳῴῶῷῸΌῺΏῼ
|
||||
´῾ ‐‑‒–—―‖‗‘’‚‛“”„‟†‡•‣․‥…‧
‰‱′″‴‵‶‷‸‹›※‼‽‾‿⁀⁁⁂⁃⁄⁅⁆⁇⁈⁉⁊⁋⁌⁍⁎⁏⁐⁑⁒⁓⁔⁕⁖⁗⁘⁙⁚⁛⁜⁝⁞
|
||||
⁰ⁱ⁴⁵⁶⁷⁸⁹
|
||||
⁺⁻⁼⁽⁾
|
||||
ⁿ₀₁₂₃₄₅₆₇₈₉
|
||||
₊₋₌₍₎
|
||||
ₐₑₒₓₔ
|
||||
₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸
|
||||
⃒⃓⃘⃙⃚⃐⃑⃔⃕⃖⃗⃛⃜⃝⃞⃟⃠⃡⃢⃣⃤⃥⃦⃪⃫⃨⃬⃭⃮⃯⃧⃩⃰
|
||||
℀℁
|
||||
ℂ
|
||||
℃℄℅℆
|
||||
ℇ
|
||||
℈℉
|
||||
ℊℋℌℍℎℏℐℑℒℓ
|
||||
℔
|
||||
ℕ
|
||||
№℗℘
|
||||
ℙℚℛℜℝ
|
||||
℞℟℠℡™℣
|
||||
ℤ
|
||||
℥
|
||||
Ω
|
||||
℧
|
||||
ℨ
|
||||
℩
|
||||
KÅℬℭ
|
||||
℮
|
||||
ℯℰℱℲℳℴℵℶℷℸℹ
|
||||
℺℻
|
||||
ℼℽℾℿ
|
||||
⅀⅁⅂⅃⅄
|
||||
ⅅⅆⅇⅈⅉ
|
||||
⅊⅋⅌⅍
|
||||
ⅎ
|
||||
⅏
|
||||
⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↃↄↅↆↇↈ↉
|
||||
←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟∠∡∢∣∤∥∦∧∨∩∪∫∬∭∮∯∰∱∲∳∴∵∶∷∸∹∺∻∼∽∾∿≀≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼⊽⊾⊿⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮⋯⋰⋱⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿⌀⌁⌂⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌⌍⌎⌏⌐⌑⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜⌝⌞⌟⌠⌡⌢⌣⌤⌥⌦⌧⌨〈〉⌫⌬⌭⌮⌯⌰⌱⌲⌳⌴⌵⌶⌷⌸⌹⌺⌻⌼⌽⌾⌿⍀⍁⍂⍃⍄⍅⍆⍇⍈⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⍻⍼⍽⍾⍿⎀⎁⎂⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌⎍⎎⎏⎐⎑⎒⎓⎔⎕⎖⎗⎘⎙⎚⎛⎜⎝⎞⎟⎠⎡⎢⎣⎤⎥⎦⎧⎨⎩⎪⎫⎬⎭⎮⎯⎰⎱⎲⎳⎴⎵⎶⎷⎸⎹⎺⎻⎼⎽⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇⏈⏉⏊⏋⏌⏍⏎⏏⏐⏑⏒⏓⏔⏕⏖⏗⏘⏙⏚⏛⏜⏝⏞⏟⏠⏡⏢⏣⏤⏥⏦⏧⏨␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␠␡␢␣␥␦⑀⑁⑂⑃⑄⑅⑆⑇⑈⑉⑊
|
||||
①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛
|
||||
⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ
|
||||
⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿
|
||||
─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⚽⚾⚿⛀⛁⛂⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌⛍⛏⛐⛑⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜⛝⛞⛟⛠⛡⛣⛨⛩⛪⛫⛬⛭⛮⛯⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿✁✂✃✄✆✇✈✉✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❍❏❐❑❒❖❗❘❙❚❛❜❝❞❡❢❣❤❥❦❧❨❩❪❫❬❭❮❯❰❱❲❳❴❵
|
||||
❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓
|
||||
➔➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯➱➲➳➴➵➶➷➸➹➺➻➼➽➾⟀⟁⟂⟃⟄⟅⟆⟇⟈⟉⟊⟌⟐⟑⟒⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜⟝⟞⟟⟠⟡⟢⟣⟤⟥⟦⟧⟨⟩⟪⟫⟬⟭⟮⟯⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿⤀⤁⤂⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞⤟⤠⤡⤢⤣⤤⤥⤦⤧⤨⤩⤪⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶⤷⤸⤹⤺⤻⤼⤽⤾⤿⥀⥁⥂⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎⥏⥐⥑⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜⥝⥞⥟⥠⥡⥢⥣⥤⥥⥦⥧⥨⥩⥪⥫⥬⥭⥮⥯⥰⥱⥲⥳⥴⥵⥶⥷⥸⥹⥺⥻⥼⥽⥾⥿⦀⦁⦂⦃⦄⦅⦆⦇⦈⦉⦊⦋⦌⦍⦎⦏⦐⦑⦒⦓⦔⦕⦖⦗⦘⦙⦚⦛⦜⦝⦞⦟⦠⦡⦢⦣⦤⦥⦦⦧⦨⦩⦪⦫⦬⦭⦮⦯⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧀⧁⧂⧃⧄⧅⧆⧇⧈⧉⧊⧋⧌⧍⧎⧏⧐⧑⧒⧓⧔⧕⧖⧗⧘⧙⧚⧛⧜⧝⧞⧟⧠⧡⧢⧣⧤⧥⧦⧧⧨⧩⧪⧫⧬⧭⧮⧯⧰⧱⧲⧳⧴⧵⧶⧷⧸⧹⧺⧻⧼⧽⧾⧿⨀⨁⨂⨃⨄⨅⨆⨇⨈⨉⨊⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜⨝⨞⨟⨠⨡⨢⨣⨤⨥⨦⨧⨨⨩⨪⨫⨬⨭⨮⨯⨰⨱⨲⨳⨴⨵⨶⨷⨸⨹⨺⨻⨼⨽⨾⨿⩀⩁⩂⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌⩍⩎⩏⩐⩑⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜⩝⩞⩟⩠⩡⩢⩣⩤⩥⩦⩧⩨⩩⩪⩫⩬⩭⩮⩯⩰⩱⩲⩳⩴⩵⩶⩷⩸⩹⩺⩻⩼⩽⩾⩿⪀⪁⪂⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌⪍⪎⪏⪐⪑⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜⪝⪞⪟⪠⪡⪢⪣⪤⪥⪦⪧⪨⪩⪪⪫⪬⪭⪮⪯⪰⪱⪲⪳⪴⪵⪶⪷⪸⪹⪺⪻⪼⪽⪾⪿⫀⫁⫂⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌⫍⫎⫏⫐⫑⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝̸⫝⫞⫟⫠⫡⫢⫣⫤⫥⫦⫧⫨⫩⫪⫫⫬⫭⫮⫯⫰⫱⫲⫳⫴⫵⫶⫷⫸⫹⫺⫻⫼⫽⫾⫿⬀⬁⬂⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌⬍⬎⬏⬐⬑⬒⬓⬔⬕⬖⬗⬘⬙⬚⬛⬜⬝⬞⬟⬠⬡⬢⬣⬤⬥⬦⬧⬨⬩⬪⬫⬬⬭⬮⬯⬰⬱⬲⬳⬴⬵⬶⬷⬸⬹⬺⬻⬼⬽⬾⬿⭀⭁⭂⭃⭄⭅⭆⭇⭈⭉⭊⭋⭌⭐⭑⭒⭓⭔⭕⭖⭗⭘⭙
|
||||
ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞⱠⱡⱢⱣⱤⱥⱦⱧⱨⱩⱪⱫⱬⱭⱮⱯⱰⱱⱲⱳⱴⱵⱶⱷⱸⱹⱺⱻⱼⱽⱾⱿⲀⲁⲂⲃⲄⲅⲆⲇⲈⲉⲊⲋⲌⲍⲎⲏⲐⲑⲒⲓⲔⲕⲖⲗⲘⲙⲚⲛⲜⲝⲞⲟⲠⲡⲢⲣⲤⲥⲦⲧⲨⲩⲪⲫⲬⲭⲮⲯⲰⲱⲲⲳⲴⲵⲶⲷⲸⲹⲺⲻⲼⲽⲾⲿⳀⳁⳂⳃⳄⳅⳆⳇⳈⳉⳊⳋⳌⳍⳎⳏⳐⳑⳒⳓⳔⳕⳖⳗⳘⳙⳚⳛⳜⳝⳞⳟⳠⳡⳢⳣⳤ
|
||||
⳥⳦⳧⳨⳩⳪
|
||||
ⳫⳬⳭⳮ⳯⳰⳱
|
||||
⳹⳺⳻⳼
|
||||
⳽
|
||||
⳾⳿
|
||||
ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥⴰⴱⴲⴳⴴⴵⴶⴷⴸⴹⴺⴻⴼⴽⴾⴿⵀⵁⵂⵃⵄⵅⵆⵇⵈⵉⵊⵋⵌⵍⵎⵏⵐⵑⵒⵓⵔⵕⵖⵗⵘⵙⵚⵛⵜⵝⵞⵟⵠⵡⵢⵣⵤⵥⵯⶀⶁⶂⶃⶄⶅⶆⶇⶈⶉⶊⶋⶌⶍⶎⶏⶐⶑⶒⶓⶔⶕⶖⶠⶡⶢⶣⶤⶥⶦⶨⶩⶪⶫⶬⶭⶮⶰⶱⶲⶳⶴⶵⶶⶸⶹⶺⶻⶼⶽⶾⷀⷁⷂⷃⷄⷅⷆⷈⷉⷊⷋⷌⷍⷎⷐⷑⷒⷓⷔⷕⷖⷘⷙⷚⷛⷜⷝⷞⷠⷡⷢⷣⷤⷥⷦⷧⷨⷩⷪⷫⷬⷭⷮⷯⷰⷱⷲⷳⷴⷵⷶⷷⷸⷹⷺⷻⷼⷽⷾⷿ
|
||||
⸀⸁⸂⸃⸄⸅⸆⸇⸈⸉⸊⸋⸌⸍⸎⸏⸐⸑⸒⸓⸔⸕⸖⸗⸘⸙⸚⸛⸜⸝⸞⸟⸠⸡⸢⸣⸤⸥⸦⸧⸨⸩⸪⸫⸬⸭⸮
|
||||
ⸯ
|
||||
⸰⸱⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠⻡⻢⻣⻤⻥⻦⻧⻨⻩⻪⻫⻬⻭⻮⻯⻰⻱⻲⻳⼀⼁⼂⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌⼍⼎⼏⼐⼑⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜⼝⼞⼟⼠⼡⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿⽀⽁⽂⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿⾀⾁⾂⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌⾍⾎⾏⾐⾑⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜⾝⾞⾟⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿⿀⿁⿂⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌⿍⿎⿏⿐⿑⿒⿓⿔⿕⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻ 、。〃〄
|
||||
々〆〇
|
||||
〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟〠
|
||||
〡〢〣〤〥〦〧〨〩〪〭〮〯〫〬
|
||||
〰
|
||||
〱〲〳〴〵
|
||||
〶〷
|
||||
〸〹〺〻〼
|
||||
〽〾〿
|
||||
ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ゙゚
|
||||
゛゜
|
||||
ゝゞゟ
|
||||
゠
|
||||
ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ
|
||||
・
|
||||
ーヽヾヿㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩㄪㄫㄬㄭㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣㅤㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀㆁㆂㆃㆄㆅㆆㆇㆈㆉㆊㆋㆌㆍㆎ
|
||||
㆐㆑
|
||||
㆒㆓㆔㆕
|
||||
㆖㆗㆘㆙㆚㆛㆜㆝㆞㆟
|
||||
ㆠㆡㆢㆣㆤㆥㆦㆧㆨㆩㆪㆫㆬㆭㆮㆯㆰㆱㆲㆳㆴㆵㆶㆷ
|
||||
㇀㇁㇂㇃㇄㇅㇆㇇㇈㇉㇊㇋㇌㇍㇎㇏㇐㇑㇒㇓㇔㇕㇖㇗㇘㇙㇚㇛㇜㇝㇞㇟㇠㇡㇢㇣
|
||||
ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ
|
||||
㈀㈁㈂㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌㈍㈎㈏㈐㈑㈒㈓㈔㈕㈖㈗㈘㈙㈚㈛㈜㈝㈞
|
||||
㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩
|
||||
㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀㉁㉂㉃㉄㉅㉆㉇㉈㉉㉊㉋㉌㉍㉎㉏㉐
|
||||
㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟
|
||||
㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉼㉽㉾㉿
|
||||
㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉
|
||||
㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜㊝㊞㊟㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰
|
||||
㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿
|
||||
㋀㋁㋂㋃㋄㋅㋆㋇㋈㋉㋊㋋㋌㋍㋎㋏㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋽㋾㌀㌁㌂㌃㌄㌅㌆㌇㌈㌉㌊㌋㌌㌍㌎㌏㌐㌑㌒㌓㌔㌕㌖㌗㌘㌙㌚㌛㌜㌝㌞㌟㌠㌡㌢㌣㌤㌥㌦㌧㌨㌩㌪㌫㌬㌭㌮㌯㌰㌱㌲㌳㌴㌵㌶㌷㌸㌹㌺㌻㌼㌽㌾㌿㍀㍁㍂㍃㍄㍅㍆㍇㍈㍉㍊㍋㍌㍍㍎㍏㍐㍑㍒㍓㍔㍕㍖㍗㍘㍙㍚㍛㍜㍝㍞㍟㍠㍡㍢㍣㍤㍥㍦㍧㍨㍩㍪㍫㍬㍭㍮㍯㍰㍱㍲㍳㍴㍵㍶㍷㍸㍹㍺㍻㍼㍽㍾㍿㎀㎁㎂㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜㎝㎞㎟㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿㏀㏁㏂㏃㏄㏅㏆㏇㏈㏉㏊㏋㏌㏍㏎㏏㏐㏑㏒㏓㏔㏕㏖㏗㏘㏙㏚㏛㏜㏝㏞㏟㏠㏡㏢㏣㏤㏥㏦㏧㏨㏩㏪㏫㏬㏭㏮㏯㏰㏱㏲㏳㏴㏵㏶㏷㏸㏹㏺㏻㏼㏽㏾㏿
|
||||
㐀䶵
|
||||
䷀䷁䷂䷃䷄䷅䷆䷇䷈䷉䷊䷋䷌䷍䷎䷏䷐䷑䷒䷓䷔䷕䷖䷗䷘䷙䷚䷛䷜䷝䷞䷟䷠䷡䷢䷣䷤䷥䷦䷧䷨䷩䷪䷫䷬䷭䷮䷯䷰䷱䷲䷳䷴䷵䷶䷷䷸䷹䷺䷻䷼䷽䷾䷿
|
||||
一鿋ꀀꀁꀂꀃꀄꀅꀆꀇꀈꀉꀊꀋꀌꀍꀎꀏꀐꀑꀒꀓꀔꀕꀖꀗꀘꀙꀚꀛꀜꀝꀞꀟꀠꀡꀢꀣꀤꀥꀦꀧꀨꀩꀪꀫꀬꀭꀮꀯꀰꀱꀲꀳꀴꀵꀶꀷꀸꀹꀺꀻꀼꀽꀾꀿꁀꁁꁂꁃꁄꁅꁆꁇꁈꁉꁊꁋꁌꁍꁎꁏꁐꁑꁒꁓꁔꁕꁖꁗꁘꁙꁚꁛꁜꁝꁞꁟꁠꁡꁢꁣꁤꁥꁦꁧꁨꁩꁪꁫꁬꁭꁮꁯꁰꁱꁲꁳꁴꁵꁶꁷꁸꁹꁺꁻꁼꁽꁾꁿꂀꂁꂂꂃꂄꂅꂆꂇꂈꂉꂊꂋꂌꂍꂎꂏꂐꂑꂒꂓꂔꂕꂖꂗꂘꂙꂚꂛꂜꂝꂞꂟꂠꂡꂢꂣꂤꂥꂦꂧꂨꂩꂪꂫꂬꂭꂮꂯꂰꂱꂲꂳꂴꂵꂶꂷꂸꂹꂺꂻꂼꂽꂾꂿꃀꃁꃂꃃꃄꃅꃆꃇꃈꃉꃊꃋꃌꃍꃎꃏꃐꃑꃒꃓꃔꃕꃖꃗꃘꃙꃚꃛꃜꃝꃞꃟꃠꃡꃢꃣꃤꃥꃦꃧꃨꃩꃪꃫꃬꃭꃮꃯꃰꃱꃲꃳꃴꃵꃶꃷꃸꃹꃺꃻꃼꃽꃾꃿꄀꄁꄂꄃꄄꄅꄆꄇꄈꄉꄊꄋꄌꄍꄎꄏꄐꄑꄒꄓꄔꄕꄖꄗꄘꄙꄚꄛꄜꄝꄞꄟꄠꄡꄢꄣꄤꄥꄦꄧꄨꄩꄪꄫꄬꄭꄮꄯꄰꄱꄲꄳꄴꄵꄶꄷꄸꄹꄺꄻꄼꄽꄾꄿꅀꅁꅂꅃꅄꅅꅆꅇꅈꅉꅊꅋꅌꅍꅎꅏꅐꅑꅒꅓꅔꅕꅖꅗꅘꅙꅚꅛꅜꅝꅞꅟꅠꅡꅢꅣꅤꅥꅦꅧꅨꅩꅪꅫꅬꅭꅮꅯꅰꅱꅲꅳꅴꅵꅶꅷꅸꅹꅺꅻꅼꅽꅾꅿꆀꆁꆂꆃꆄꆅꆆꆇꆈꆉꆊꆋꆌꆍꆎꆏꆐꆑꆒꆓꆔꆕꆖꆗꆘꆙꆚꆛꆜꆝꆞꆟꆠꆡꆢꆣꆤꆥꆦꆧꆨꆩꆪꆫꆬꆭꆮꆯꆰꆱꆲꆳꆴꆵꆶꆷꆸꆹꆺꆻꆼꆽꆾꆿꇀꇁꇂꇃꇄꇅꇆꇇꇈꇉꇊꇋꇌꇍꇎꇏꇐꇑꇒꇓꇔꇕꇖꇗꇘꇙꇚꇛꇜꇝꇞꇟꇠꇡꇢꇣꇤꇥꇦꇧꇨꇩꇪꇫꇬꇭꇮꇯꇰꇱꇲꇳꇴꇵꇶꇷꇸꇹꇺꇻꇼꇽꇾꇿꈀꈁꈂꈃꈄꈅꈆꈇꈈꈉꈊꈋꈌꈍꈎꈏꈐꈑꈒꈓꈔꈕꈖꈗꈘꈙꈚꈛꈜꈝꈞꈟꈠꈡꈢꈣꈤꈥꈦꈧꈨꈩꈪꈫꈬꈭꈮꈯꈰꈱꈲꈳꈴꈵꈶꈷꈸꈹꈺꈻꈼꈽꈾꈿꉀꉁꉂꉃꉄꉅꉆꉇꉈꉉꉊꉋꉌꉍꉎꉏꉐꉑꉒꉓꉔꉕꉖꉗꉘꉙꉚꉛꉜꉝꉞꉟꉠꉡꉢꉣꉤꉥꉦꉧꉨꉩꉪꉫꉬꉭꉮꉯꉰꉱꉲꉳꉴꉵꉶꉷꉸꉹꉺꉻꉼꉽꉾꉿꊀꊁꊂꊃꊄꊅꊆꊇꊈꊉꊊꊋꊌꊍꊎꊏꊐꊑꊒꊓꊔꊕꊖꊗꊘꊙꊚꊛꊜꊝꊞꊟꊠꊡꊢꊣꊤꊥꊦꊧꊨꊩꊪꊫꊬꊭꊮꊯꊰꊱꊲꊳꊴꊵꊶꊷꊸꊹꊺꊻꊼꊽꊾꊿꋀꋁꋂꋃꋄꋅꋆꋇꋈꋉꋊꋋꋌꋍꋎꋏꋐꋑꋒꋓꋔꋕꋖꋗꋘꋙꋚꋛꋜꋝꋞꋟꋠꋡꋢꋣꋤꋥꋦꋧꋨꋩꋪꋫꋬꋭꋮꋯꋰꋱꋲꋳꋴꋵꋶꋷꋸꋹꋺꋻꋼꋽꋾꋿꌀꌁꌂꌃꌄꌅꌆꌇꌈꌉꌊꌋꌌꌍꌎꌏꌐꌑꌒꌓꌔꌕꌖꌗꌘꌙꌚꌛꌜꌝꌞꌟꌠꌡꌢꌣꌤꌥꌦꌧꌨꌩꌪꌫꌬꌭꌮꌯꌰꌱꌲꌳꌴꌵꌶꌷꌸꌹꌺꌻꌼꌽꌾꌿꍀꍁꍂꍃꍄꍅꍆꍇꍈꍉꍊꍋꍌꍍꍎꍏꍐꍑꍒꍓꍔꍕꍖꍗꍘꍙꍚꍛꍜꍝꍞꍟꍠꍡꍢꍣꍤꍥꍦꍧꍨꍩꍪꍫꍬꍭꍮꍯꍰꍱꍲꍳꍴꍵꍶꍷꍸꍹꍺꍻꍼꍽꍾꍿꎀꎁꎂꎃꎄꎅꎆꎇꎈꎉꎊꎋꎌꎍꎎꎏꎐꎑꎒꎓꎔꎕꎖꎗꎘꎙꎚꎛꎜꎝꎞꎟꎠꎡꎢꎣꎤꎥꎦꎧꎨꎩꎪꎫꎬꎭꎮꎯꎰꎱꎲꎳꎴꎵꎶꎷꎸꎹꎺꎻꎼꎽꎾꎿꏀꏁꏂꏃꏄꏅꏆꏇꏈꏉꏊꏋꏌꏍꏎꏏꏐꏑꏒꏓꏔꏕꏖꏗꏘꏙꏚꏛꏜꏝꏞꏟꏠꏡꏢꏣꏤꏥꏦꏧꏨꏩꏪꏫꏬꏭꏮꏯꏰꏱꏲꏳꏴꏵꏶꏷꏸꏹꏺꏻꏼꏽꏾꏿꐀꐁꐂꐃꐄꐅꐆꐇꐈꐉꐊꐋꐌꐍꐎꐏꐐꐑꐒꐓꐔꐕꐖꐗꐘꐙꐚꐛꐜꐝꐞꐟꐠꐡꐢꐣꐤꐥꐦꐧꐨꐩꐪꐫꐬꐭꐮꐯꐰꐱꐲꐳꐴꐵꐶꐷꐸꐹꐺꐻꐼꐽꐾꐿꑀꑁꑂꑃꑄꑅꑆꑇꑈꑉꑊꑋꑌꑍꑎꑏꑐꑑꑒꑓꑔꑕꑖꑗꑘꑙꑚꑛꑜꑝꑞꑟꑠꑡꑢꑣꑤꑥꑦꑧꑨꑩꑪꑫꑬꑭꑮꑯꑰꑱꑲꑳꑴꑵꑶꑷꑸꑹꑺꑻꑼꑽꑾꑿꒀꒁꒂꒃꒄꒅꒆꒇꒈꒉꒊꒋꒌ
|
||||
꒐꒑꒒꒓꒔꒕꒖꒗꒘꒙꒚꒛꒜꒝꒞꒟꒠꒡꒢꒣꒤꒥꒦꒧꒨꒩꒪꒫꒬꒭꒮꒯꒰꒱꒲꒳꒴꒵꒶꒷꒸꒹꒺꒻꒼꒽꒾꒿꓀꓁꓂꓃꓄꓅꓆
|
||||
ꓐꓑꓒꓓꓔꓕꓖꓗꓘꓙꓚꓛꓜꓝꓞꓟꓠꓡꓢꓣꓤꓥꓦꓧꓨꓩꓪꓫꓬꓭꓮꓯꓰꓱꓲꓳꓴꓵꓶꓷꓸꓹꓺꓻꓼꓽ
|
||||
꓾꓿
|
||||
ꔀꔁꔂꔃꔄꔅꔆꔇꔈꔉꔊꔋꔌꔍꔎꔏꔐꔑꔒꔓꔔꔕꔖꔗꔘꔙꔚꔛꔜꔝꔞꔟꔠꔡꔢꔣꔤꔥꔦꔧꔨꔩꔪꔫꔬꔭꔮꔯꔰꔱꔲꔳꔴꔵꔶꔷꔸꔹꔺꔻꔼꔽꔾꔿꕀꕁꕂꕃꕄꕅꕆꕇꕈꕉꕊꕋꕌꕍꕎꕏꕐꕑꕒꕓꕔꕕꕖꕗꕘꕙꕚꕛꕜꕝꕞꕟꕠꕡꕢꕣꕤꕥꕦꕧꕨꕩꕪꕫꕬꕭꕮꕯꕰꕱꕲꕳꕴꕵꕶꕷꕸꕹꕺꕻꕼꕽꕾꕿꖀꖁꖂꖃꖄꖅꖆꖇꖈꖉꖊꖋꖌꖍꖎꖏꖐꖑꖒꖓꖔꖕꖖꖗꖘꖙꖚꖛꖜꖝꖞꖟꖠꖡꖢꖣꖤꖥꖦꖧꖨꖩꖪꖫꖬꖭꖮꖯꖰꖱꖲꖳꖴꖵꖶꖷꖸꖹꖺꖻꖼꖽꖾꖿꗀꗁꗂꗃꗄꗅꗆꗇꗈꗉꗊꗋꗌꗍꗎꗏꗐꗑꗒꗓꗔꗕꗖꗗꗘꗙꗚꗛꗜꗝꗞꗟꗠꗡꗢꗣꗤꗥꗦꗧꗨꗩꗪꗫꗬꗭꗮꗯꗰꗱꗲꗳꗴꗵꗶꗷꗸꗹꗺꗻꗼꗽꗾꗿꘀꘁꘂꘃꘄꘅꘆꘇꘈꘉꘊꘋꘌ
|
||||
꘍꘎꘏
|
||||
ꘐꘑꘒꘓꘔꘕꘖꘗꘘꘙꘚꘛꘜꘝꘞꘟ꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩ꘪꘫꙀꙁꙂꙃꙄꙅꙆꙇꙈꙉꙊꙋꙌꙍꙎꙏꙐꙑꙒꙓꙔꙕꙖꙗꙘꙙꙚꙛꙜꙝꙞꙟꙢꙣꙤꙥꙦꙧꙨꙩꙪꙫꙬꙭꙮ꙯꙰꙱꙲
|
||||
꙳
|
||||
꙼꙽
|
||||
꙾
|
||||
ꙿꚀꚁꚂꚃꚄꚅꚆꚇꚈꚉꚊꚋꚌꚍꚎꚏꚐꚑꚒꚓꚔꚕꚖꚗꚠꚡꚢꚣꚤꚥꚦꚧꚨꚩꚪꚫꚬꚭꚮꚯꚰꚱꚲꚳꚴꚵꚶꚷꚸꚹꚺꚻꚼꚽꚾꚿꛀꛁꛂꛃꛄꛅꛆꛇꛈꛉꛊꛋꛌꛍꛎꛏꛐꛑꛒꛓꛔꛕꛖꛗꛘꛙꛚꛛꛜꛝꛞꛟꛠꛡꛢꛣꛤꛥꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ꛰꛱
|
||||
꛲꛳꛴꛵꛶꛷꜀꜁꜂꜃꜄꜅꜆꜇꜈꜉꜊꜋꜌꜍꜎꜏꜐꜑꜒꜓꜔꜕꜖
|
||||
ꜗꜘꜙꜚꜛꜜꜝꜞꜟ
|
||||
꜠꜡
|
||||
ꜢꜣꜤꜥꜦꜧꜨꜩꜪꜫꜬꜭꜮꜯꜰꜱꜲꜳꜴꜵꜶꜷꜸꜹꜺꜻꜼꜽꜾꜿꝀꝁꝂꝃꝄꝅꝆꝇꝈꝉꝊꝋꝌꝍꝎꝏꝐꝑꝒꝓꝔꝕꝖꝗꝘꝙꝚꝛꝜꝝꝞꝟꝠꝡꝢꝣꝤꝥꝦꝧꝨꝩꝪꝫꝬꝭꝮꝯꝰꝱꝲꝳꝴꝵꝶꝷꝸꝹꝺꝻꝼꝽꝾꝿꞀꞁꞂꞃꞄꞅꞆꞇꞈ
|
||||
꞉꞊
|
||||
Ꞌꞌꟻꟼꟽꟾꟿꠀꠁꠂꠃꠄꠅ꠆ꠇꠈꠉꠊꠋꠌꠍꠎꠏꠐꠑꠒꠓꠔꠕꠖꠗꠘꠙꠚꠛꠜꠝꠞꠟꠠꠡꠢꠣꠤꠥꠦꠧ
|
||||
꠨꠩꠪꠫
|
||||
꠰꠱꠲꠳꠴꠵
|
||||
꠶꠷꠸꠹
|
||||
ꡀꡁꡂꡃꡄꡅꡆꡇꡈꡉꡊꡋꡌꡍꡎꡏꡐꡑꡒꡓꡔꡕꡖꡗꡘꡙꡚꡛꡜꡝꡞꡟꡠꡡꡢꡣꡤꡥꡦꡧꡨꡩꡪꡫꡬꡭꡮꡯꡰꡱꡲꡳ
|
||||
꡴꡵꡶꡷
|
||||
ꢀꢁꢂꢃꢄꢅꢆꢇꢈꢉꢊꢋꢌꢍꢎꢏꢐꢑꢒꢓꢔꢕꢖꢗꢘꢙꢚꢛꢜꢝꢞꢟꢠꢡꢢꢣꢤꢥꢦꢧꢨꢩꢪꢫꢬꢭꢮꢯꢰꢱꢲꢳꢴꢵꢶꢷꢸꢹꢺꢻꢼꢽꢾꢿꣀꣁꣂꣃ꣄
|
||||
꣎꣏
|
||||
꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꣠꣡꣢꣣꣤꣥꣦꣧꣨꣩꣪꣫꣬꣭꣮꣯꣰꣱ꣲꣳꣴꣵꣶꣷ
|
||||
꣸꣹꣺
|
||||
ꣻ꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉ꤊꤋꤌꤍꤎꤏꤐꤑꤒꤓꤔꤕꤖꤗꤘꤙꤚꤛꤜꤝꤞꤟꤠꤡꤢꤣꤤꤥꤦꤧꤨꤩꤪ꤫꤬꤭
|
||||
꤮꤯
|
||||
ꤰꤱꤲꤳꤴꤵꤶꤷꤸꤹꤺꤻꤼꤽꤾꤿꥀꥁꥂꥃꥄꥅꥆꥇꥈꥉꥊꥋꥌꥍꥎꥏꥐꥑꥒ꥓
|
||||
꥟
|
||||
ꥠꥡꥢꥣꥤꥥꥦꥧꥨꥩꥪꥫꥬꥭꥮꥯꥰꥱꥲꥳꥴꥵꥶꥷꥸꥹꥺꥻꥼꦀꦁꦂꦃꦄꦅꦆꦇꦈꦉꦊꦋꦌꦍꦎꦏꦐꦑꦒꦓꦔꦕꦖꦗꦘꦙꦚꦛꦜꦝꦞꦟꦠꦡꦢꦣꦤꦥꦦꦧꦨꦩꦪꦫꦬꦭꦮꦯꦰꦱꦲ꦳ꦴꦵꦶꦷꦸꦹꦺꦻꦼꦽꦾꦿ꧀
|
||||
꧁꧂꧃꧄꧅꧆꧇꧈꧉꧊꧋꧌꧍
|
||||
ꧏ꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙
|
||||
꧞꧟
|
||||
ꨀꨁꨂꨃꨄꨅꨆꨇꨈꨉꨊꨋꨌꨍꨎꨏꨐꨑꨒꨓꨔꨕꨖꨗꨘꨙꨚꨛꨜꨝꨞꨟꨠꨡꨢꨣꨤꨥꨦꨧꨨꨩꨪꨫꨬꨭꨮꨯꨰꨱꨲꨳꨴꨵꨶꩀꩁꩂꩃꩄꩅꩆꩇꩈꩉꩊꩋꩌꩍ꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙
|
||||
꩜꩝꩞꩟
|
||||
ꩠꩡꩢꩣꩤꩥꩦꩧꩨꩩꩪꩫꩬꩭꩮꩯꩰꩱꩲꩳꩴꩵꩶ
|
||||
꩷꩸꩹
|
||||
ꩺꩻꪀꪁꪂꪃꪄꪅꪆꪇꪈꪉꪊꪋꪌꪍꪎꪏꪐꪑꪒꪓꪔꪕꪖꪗꪘꪙꪚꪛꪜꪝꪞꪟꪠꪡꪢꪣꪤꪥꪦꪧꪨꪩꪪꪫꪬꪭꪮꪯꪰꪱꪴꪲꪳꪵꪶꪷꪸꪹꪺꪻꪼꪽꪾ꪿ꫀ꫁ꫂꫛꫜꫝ
|
||||
꫞꫟
|
||||
ꯀꯁꯂꯃꯄꯅꯆꯇꯈꯉꯊꯋꯌꯍꯎꯏꯐꯑꯒꯓꯔꯕꯖꯗꯘꯙꯚꯛꯜꯝꯞꯟꯠꯡꯢꯣꯤꯥꯦꯧꯨꯩꯪ
|
||||
꯫
|
||||
꯬꯭꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹가힣ힰힱힲힳힴힵힶힷힸힹힺힻힼힽힾힿퟀퟁퟂퟃퟄퟅퟆퟋퟌퟍퟎퟏퟐퟑퟒퟓퟔퟕퟖퟗퟘퟙퟚퟛퟜퟝퟞퟟퟠퟡퟢퟣퟤퟥퟦퟧퟨퟩퟪퟫퟬퟭퟮퟯퟰퟱퟲퟳퟴퟵퟶퟷퟸퟹퟺퟻ
|
||||
|
||||
豈更車賈滑串句龜龜契金喇奈懶癩羅蘿螺裸邏樂洛烙珞落酪駱亂卵欄爛蘭鸞嵐濫藍襤拉臘蠟廊朗浪狼郎來冷勞擄櫓爐盧老蘆虜路露魯鷺碌祿綠菉錄鹿論壟弄籠聾牢磊賂雷壘屢樓淚漏累縷陋勒肋凜凌稜綾菱陵讀拏樂諾丹寧怒率異北磻便復不泌數索參塞省葉說殺辰沈拾若掠略亮兩凉梁糧良諒量勵呂女廬旅濾礪閭驪麗黎力曆歷轢年憐戀撚漣煉璉秊練聯輦蓮連鍊列劣咽烈裂說廉念捻殮簾獵令囹寧嶺怜玲瑩羚聆鈴零靈領例禮醴隸惡了僚寮尿料樂燎療蓼遼龍暈阮劉杻柳流溜琉留硫紐類六戮陸倫崙淪輪律慄栗率隆利吏履易李梨泥理痢罹裏裡里離匿溺吝燐璘藺隣鱗麟林淋臨立笠粒狀炙識什茶刺切度拓糖宅洞暴輻行降見廓兀嗀﨎﨏塚﨑晴﨓﨔凞猪益礼神祥福靖精羽﨟蘒﨡諸﨣﨤逸都﨧﨨﨩飯飼館鶴侮僧免勉勤卑喝嘆器塀墨層屮悔慨憎懲敏既暑梅海渚漢煮爫琢碑社祉祈祐祖祝禍禎穀突節練縉繁署者臭艹艹著褐視謁謹賓贈辶逸難響頻恵𤋮舘並况全侀充冀勇勺喝啕喙嗢塚墳奄奔婢嬨廒廙彩徭惘慎愈憎慠懲戴揄搜摒敖晴朗望杖歹殺流滛滋漢瀞煮瞧爵犯猪瑱甆画瘝瘟益盛直睊着磌窱節类絛練缾者荒華蝹襁覆視調諸請謁諾諭謹變贈輸遲醙鉶陼難靖韛響頋頻鬒龜𢡊𢡄𣏕㮝䀘䀹𥉉𥳐𧻓齃龎fffiflffifflſtstﬓﬔﬕﬖﬗיִﬞײַﬠﬡﬢﬣﬤﬥﬦﬧﬨ
|
||||
﬩
|
||||
שׁשׂשּׁשּׂאַאָאּבּגּדּהּוּזּטּיּךּכּלּמּנּסּףּפּצּקּרּשּתּוֹבֿכֿפֿﭏﭐﭑﭒﭓﭔﭕﭖﭗﭘﭙﭚﭛﭜﭝﭞﭟﭠﭡﭢﭣﭤﭥﭦﭧﭨﭩﭪﭫﭬﭭﭮﭯﭰﭱﭲﭳﭴﭵﭶﭷﭸﭹﭺﭻﭼﭽﭾﭿﮀﮁﮂﮃﮄﮅﮆﮇﮈﮉﮊﮋﮌﮍﮎﮏﮐﮑﮒﮓﮔﮕﮖﮗﮘﮙﮚﮛﮜﮝﮞﮟﮠﮡﮢﮣﮤﮥﮦﮧﮨﮩﮪﮫﮬﮭﮮﮯﮰﮱﯓﯔﯕﯖﯗﯘﯙﯚﯛﯜﯝﯞﯟﯠﯡﯢﯣﯤﯥﯦﯧﯨﯩﯪﯫﯬﯭﯮﯯﯰﯱﯲﯳﯴﯵﯶﯷﯸﯹﯺﯻﯼﯽﯾﯿﰀﰁﰂﰃﰄﰅﰆﰇﰈﰉﰊﰋﰌﰍﰎﰏﰐﰑﰒﰓﰔﰕﰖﰗﰘﰙﰚﰛﰜﰝﰞﰟﰠﰡﰢﰣﰤﰥﰦﰧﰨﰩﰪﰫﰬﰭﰮﰯﰰﰱﰲﰳﰴﰵﰶﰷﰸﰹﰺﰻﰼﰽﰾﰿﱀﱁﱂﱃﱄﱅﱆﱇﱈﱉﱊﱋﱌﱍﱎﱏﱐﱑﱒﱓﱔﱕﱖﱗﱘﱙﱚﱛﱜﱝﱞﱟﱠﱡﱢﱣﱤﱥﱦﱧﱨﱩﱪﱫﱬﱭﱮﱯﱰﱱﱲﱳﱴﱵﱶﱷﱸﱹﱺﱻﱼﱽﱾﱿﲀﲁﲂﲃﲄﲅﲆﲇﲈﲉﲊﲋﲌﲍﲎﲏﲐﲑﲒﲓﲔﲕﲖﲗﲘﲙﲚﲛﲜﲝﲞﲟﲠﲡﲢﲣﲤﲥﲦﲧﲨﲩﲪﲫﲬﲭﲮﲯﲰﲱﲲﲳﲴﲵﲶﲷﲸﲹﲺﲻﲼﲽﲾﲿﳀﳁﳂﳃﳄﳅﳆﳇﳈﳉﳊﳋﳌﳍﳎﳏﳐﳑﳒﳓﳔﳕﳖﳗﳘﳙﳚﳛﳜﳝﳞﳟﳠﳡﳢﳣﳤﳥﳦﳧﳨﳩﳪﳫﳬﳭﳮﳯﳰﳱﳲﳳﳴﳵﳶﳷﳸﳹﳺﳻﳼﳽﳾﳿﴀﴁﴂﴃﴄﴅﴆﴇﴈﴉﴊﴋﴌﴍﴎﴏﴐﴑﴒﴓﴔﴕﴖﴗﴘﴙﴚﴛﴜﴝﴞﴟﴠﴡﴢﴣﴤﴥﴦﴧﴨﴩﴪﴫﴬﴭﴮﴯﴰﴱﴲﴳﴴﴵﴶﴷﴸﴹﴺﴻﴼﴽ
|
||||
﴾﴿
|
||||
ﵐﵑﵒﵓﵔﵕﵖﵗﵘﵙﵚﵛﵜﵝﵞﵟﵠﵡﵢﵣﵤﵥﵦﵧﵨﵩﵪﵫﵬﵭﵮﵯﵰﵱﵲﵳﵴﵵﵶﵷﵸﵹﵺﵻﵼﵽﵾﵿﶀﶁﶂﶃﶄﶅﶆﶇﶈﶉﶊﶋﶌﶍﶎﶏﶒﶓﶔﶕﶖﶗﶘﶙﶚﶛﶜﶝﶞﶟﶠﶡﶢﶣﶤﶥﶦﶧﶨﶩﶪﶫﶬﶭﶮﶯﶰﶱﶲﶳﶴﶵﶶﶷﶸﶹﶺﶻﶼﶽﶾﶿﷀﷁﷂﷃﷄﷅﷆﷇﷰﷱﷲﷳﷴﷵﷶﷷﷸﷹﷺﷻ
|
||||
﷼﷽
|
||||
︀︁︂︃︄︅︆︇︈︉︊︋︌︍︎️
|
||||
︐︑︒︓︔︕︖︗︘︙
|
||||
︠︡︢︣︤︥︦
|
||||
︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀﹁﹂﹃﹄﹅﹆﹇﹈﹉﹊﹋﹌﹍﹎﹏﹐﹑﹒﹔﹕﹖﹗﹘﹙﹚﹛﹜﹝﹞﹟﹠﹡﹢﹣﹤﹥﹦﹨﹩﹪﹫
|
||||
ﹰﹱﹲﹳﹴﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ
|
||||
!"#$%&'()*+,-./
|
||||
0123456789
|
||||
:;<=>?@
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
[\]^_`
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
{|}~⦅⦆。「」、・
|
||||
ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚ᅠᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑ하ᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵ
|
||||
¢£¬ ̄¦¥₩│←↑→↓■○<EFBFBD>
|
||||
𐀀
|
12
modules/search/tests/search_embedded_form.info
Normal file
12
modules/search/tests/search_embedded_form.info
Normal file
|
@ -0,0 +1,12 @@
|
|||
name = "Search embedded form"
|
||||
description = "Support module for search module testing of embedded forms."
|
||||
package = Testing
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2017-06-21
|
||||
version = "7.56"
|
||||
project = "drupal"
|
||||
datestamp = "1498069849"
|
||||
|
70
modules/search/tests/search_embedded_form.module
Normal file
70
modules/search/tests/search_embedded_form.module
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test module implementing a form that can be embedded in search results.
|
||||
*
|
||||
* Embedded form are important, for example, for ecommerce sites where each
|
||||
* search result may included an embedded form with buttons like "Add to cart"
|
||||
* for each individual product (node) listed in the search results.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function search_embedded_form_menu() {
|
||||
$items['search_embedded_form'] = array(
|
||||
'title' => 'Search_Embed_Form',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('search_embedded_form_form'),
|
||||
'access arguments' => array('search content'),
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a form for embedding in search results for testing.
|
||||
*
|
||||
* @see search_embedded_form_form_submit().
|
||||
*/
|
||||
function search_embedded_form_form($form, &$form_state) {
|
||||
$count = variable_get('search_embedded_form_submitted', 0);
|
||||
|
||||
$form['name'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Your name'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => '',
|
||||
'#required' => TRUE,
|
||||
'#description' => t('Times form has been submitted: %count', array('%count' => $count)),
|
||||
);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Send away'),
|
||||
);
|
||||
|
||||
$form['#submit'][] = 'search_embedded_form_form_submit';
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for search_embedded_form_form().
|
||||
*/
|
||||
function search_embedded_form_form_submit($form, &$form_state) {
|
||||
$count = variable_get('search_embedded_form_submitted', 0) + 1;
|
||||
variable_set('search_embedded_form_submitted', $count);
|
||||
drupal_set_message(t('Test form was submitted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the test form to search results.
|
||||
*/
|
||||
function search_embedded_form_preprocess_search_result(&$variables) {
|
||||
$form = drupal_get_form('search_embedded_form_form');
|
||||
$variables['snippet'] .= drupal_render($form);
|
||||
}
|
12
modules/search/tests/search_extra_type.info
Normal file
12
modules/search/tests/search_extra_type.info
Normal file
|
@ -0,0 +1,12 @@
|
|||
name = "Test search type"
|
||||
description = "Support module for search module testing."
|
||||
package = Testing
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2017-06-21
|
||||
version = "7.56"
|
||||
project = "drupal"
|
||||
datestamp = "1498069849"
|
||||
|
69
modules/search/tests/search_extra_type.module
Normal file
69
modules/search/tests/search_extra_type.module
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Dummy module implementing a search type for search module testing.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_search_info().
|
||||
*/
|
||||
function search_extra_type_search_info() {
|
||||
return array(
|
||||
'title' => 'Dummy search type',
|
||||
'path' => 'dummy_path',
|
||||
'conditions_callback' => 'search_extra_type_conditions',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test conditions callback for hook_search_info().
|
||||
*/
|
||||
function search_extra_type_conditions() {
|
||||
$conditions = array();
|
||||
|
||||
if (!empty($_REQUEST['search_conditions'])) {
|
||||
$conditions['search_conditions'] = $_REQUEST['search_conditions'];
|
||||
}
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_search_execute().
|
||||
*
|
||||
* This is a dummy search, so when search "executes", we just return a dummy
|
||||
* result containing the keywords and a list of conditions.
|
||||
*/
|
||||
function search_extra_type_search_execute($keys = NULL, $conditions = NULL) {
|
||||
if (!$keys) {
|
||||
$keys = '';
|
||||
}
|
||||
return array(
|
||||
array(
|
||||
'link' => url('node'),
|
||||
'type' => 'Dummy result type',
|
||||
'title' => 'Dummy title',
|
||||
'snippet' => "Dummy search snippet to display. Keywords: {$keys}\n\nConditions: " . print_r($conditions, TRUE),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_search_page().
|
||||
*
|
||||
* Adds some text to the search page so we can verify that it runs.
|
||||
*/
|
||||
function search_extra_type_search_page($results) {
|
||||
$output['prefix']['#markup'] = '<h2>Test page text is here</h2> <ol class="search-results">';
|
||||
|
||||
foreach ($results as $entry) {
|
||||
$output[] = array(
|
||||
'#theme' => 'search_result',
|
||||
'#result' => $entry,
|
||||
'#module' => 'search_extra_type',
|
||||
);
|
||||
}
|
||||
$output['suffix']['#markup'] = '</ol>' . theme('pager');
|
||||
|
||||
return $output;
|
||||
}
|
12
modules/search/tests/search_node_tags.info
Normal file
12
modules/search/tests/search_node_tags.info
Normal file
|
@ -0,0 +1,12 @@
|
|||
name = "Test search node tags"
|
||||
description = "Support module for Node search tags testing."
|
||||
package = Testing
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2017-06-21
|
||||
version = "7.56"
|
||||
project = "drupal"
|
||||
datestamp = "1498069849"
|
||||
|
23
modules/search/tests/search_node_tags.module
Normal file
23
modules/search/tests/search_node_tags.module
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Dummy module implementing a node search hooks for search module testing.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_query_alter().
|
||||
*/
|
||||
function search_node_tags_query_alter(QueryAlterableInterface $query) {
|
||||
if ($query->hasTag('search_node')) {
|
||||
variable_set('search_node_tags_test_query_tag', TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_query_TAG_alter().
|
||||
*/
|
||||
function search_node_tags_query_search_node_alter(QueryAlterableInterface $query) {
|
||||
variable_set('search_node_tags_test_query_tag_hook', TRUE);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue