eurotext_translationMANAGER - Version 1.1.2.2

Version Notes

Version 1.1.2.2
====================================================================================
- Eigene Kategorieattribute übersetzen: Exportieren Sie Ihre benutzerdefinierten Attribute (siehe Dokumentation)

Version 1.1.2.1
====================================================================================
- Duplicate URL-Key Fehler behoben

Version 1.1.2.0
====================================================================================
- Überarbeiteter DEBUG Modus. Kann nun im Backend (System -> Configuration -> Developer -> Log) aktiviert werden
- Log-Datei wird in /var/log/eurotext.log erstellt, schwere Fehler werden auch
bei ausgeschaltetem DEBUG Modus in eurotext_fatal.log gespeichert
- Basisverzeichnis jetzt in /var/eurotext und nicht mehr /eurotext
- FTP-Prüfung jetzt mit DEBUG Ausgabe
- Diverse Verbesserungen in der Verwendung von Magento-Verzeichnissen

Version 1.1.1.0
====================================================================================
- Es werden nur noch die ausgewählten Sprachdateien exportiert
- Fehlerbehebungen

Version 1.1.0.0
====================================================================================
- Eigene Artikelattribute übersetzen: Exportieren Sie Ihre benutzerdefinierten Attribute (siehe Dokumentation)
- Absicherung: Bei Übertragungsfehlern zum Eurotext Server wird das Paket nach /var/export gesichert.
- Fehlerkorrekturen und Sicherheitsupdates
- Verbesserte und stabilere Programmbasis

Version 1.0.0.9
====================================================================================
- Der Lagerstatus wird bei der Produktauswahl nun auch für 'virtuelle' Artikel/Hauptartikel wie im Backend eingestellt angezeigt

Version 1.0.0.8
====================================================================================
- Bei der Produktauswahl kann nun auch nach Artikeltyp gefiltert werden

Version 1.0.0.7
====================================================================================
- Javascript-Fehler beseitigt
- Update-Skript arbeitet nun toleranter

Version 1.0.0.6
====================================================================================
- Bei der Auswahl der Produkte sind nun zwei zusätzliche Filter (nach Status und nach Lagerbestand) verfügbar
- Es können nun auch statische Blöcke exportiert und wieder importiert werden. Die statischen Blöcke werden mit bei der Auswahl der CMS-Seiten angezeigt

Version 1.0.0.5
====================================================================================
- Es werden im Kommentar-Feld der control.xml mehr Projektinformationen angegeben
- Ein Projekt kann nun jeweils um einen Schritt zurückgesetzt werden
- Es wird nun die korrekte Modulversion in der control.xml angegeben

Version 1.0.0.4
====================================================================================
- Das Modul exportiert Attribute nun auch, wenn für das Quell-Storeview keine Bezeichnung
vorhanden ist. Es wird dann der Admin-Standardtext exportiert.

Version 1.0.0.3
====================================================================================
- URL-Key wird im Ziel-Storeview nun nur noch (anhand der Produkt-/Kategorie-Bezeichnung)
gesetzt, wenn zuvor noch kein Wert gesetzt war (also "Use default values" /
"Standardwerte übernehmen" aktiv war)
Zuvor wurde der URL-Key gesetzt, wenn der Wert leer bzw. gleich dem Standardwert gesetzt war
- Einstellung "Use default values" / "Standardwerte übernehmen" sollte bei Feldern, die in den
Übersetzungsdateien nicht vorhanden sind, nun nicht mehr verloren gehen.

Version 1.0.0.2
====================================================================================
- Wurden zwischen Export und Import Produktbilder im Magento-Backend gelöscht für die Übersetzungen erzeugt wurden, konnte der Import fehlschlagen.
Beim Import fehlende Produktbilder werden nun übersprungen.

Version 1.0.0.1
====================================================================================
- Erweiterung der Produktauswahl um die Möglichkeit auch nach Kategorien zu filtern
und so ggf. schnell ganze Kategorien übersetzen zu können
- Wurden URL-Felder nicht mit exportiert, wurden diese beim Import auf die Standardwerte gesetzt statt diese anhand der (übersetzten) Bezeichnung zu generieren

Download this release

Release Info

Developer Eurotext AG
Extension eurotext_translationMANAGER
Version 1.1.2.2
Comparing to
See all releases


Version 1.1.2.2

Files changed (63) hide show
  1. app/code/community/Eurotext/TranslationManager/.DS_Store +0 -0
  2. app/code/community/Eurotext/TranslationManager/Block/Help.php +6 -0
  3. app/code/community/Eurotext/TranslationManager/Block/Projects.php +158 -0
  4. app/code/community/Eurotext/TranslationManager/Block/Register.php +39 -0
  5. app/code/community/Eurotext/TranslationManager/Block/Response/Ajax.php +9 -0
  6. app/code/community/Eurotext/TranslationManager/Block/Selectcategories.php +139 -0
  7. app/code/community/Eurotext/TranslationManager/Block/Selectcmspages.php +116 -0
  8. app/code/community/Eurotext/TranslationManager/Block/Selectemails.php +81 -0
  9. app/code/community/Eurotext/TranslationManager/Block/Selectlangfiles.php +65 -0
  10. app/code/community/Eurotext/TranslationManager/Block/Selectproducts.php +329 -0
  11. app/code/community/Eurotext/TranslationManager/Block/Settings.php +34 -0
  12. app/code/community/Eurotext/TranslationManager/Helper/Data.php +566 -0
  13. app/code/community/Eurotext/TranslationManager/Helper/Eurotext.php +247 -0
  14. app/code/community/Eurotext/TranslationManager/Model/.DS_Store +0 -0
  15. app/code/community/Eurotext/TranslationManager/Model/Resource/Setup.php +6 -0
  16. app/code/community/Eurotext/TranslationManager/controllers/.DS_Store +0 -0
  17. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/.DS_Store +0 -0
  18. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/.DS_Store +0 -0
  19. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/HelpController.php +17 -0
  20. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/ProjectsController.php +3240 -0
  21. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/RegisterController.php +230 -0
  22. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectcategoriesController.php +134 -0
  23. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectcmspagesController.php +123 -0
  24. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectemailsController.php +88 -0
  25. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectlangfilesController.php +89 -0
  26. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectproductsController.php +224 -0
  27. app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SettingsController.php +72 -0
  28. app/code/community/Eurotext/TranslationManager/etc/adminhtml.xml +60 -0
  29. app/code/community/Eurotext/TranslationManager/etc/config.xml +79 -0
  30. app/code/community/Eurotext/TranslationManager/etc/jstranslator.xml +36 -0
  31. app/code/community/Eurotext/TranslationManager/etc/system.xml +32 -0
  32. app/code/community/Eurotext/TranslationManager/sql/eurotext_translationmanager_setup/mysql4-install-1.0.0.0.php +246 -0
  33. app/code/community/Eurotext/TranslationManager/sql/eurotext_translationmanager_setup/mysql4-upgrade-1.0.0.5-1.0.0.6.php +32 -0
  34. app/code/community/Eurotext/TranslationManager/sql/eurotext_translationmanager_setup/mysql4-upgrade-1.0.0.7-1.0.0.8.php +14 -0
  35. app/design/adminhtml/base/default/layout/eurotext/translationmanager.xml +128 -0
  36. app/design/adminhtml/base/default/template/eurotext/translationmanager/help.phtml +88 -0
  37. app/design/adminhtml/base/default/template/eurotext/translationmanager/projects.phtml +392 -0
  38. app/design/adminhtml/base/default/template/eurotext/translationmanager/register.phtml +344 -0
  39. app/design/adminhtml/base/default/template/eurotext/translationmanager/selectcategories.phtml +130 -0
  40. app/design/adminhtml/base/default/template/eurotext/translationmanager/selectcmspages.phtml +169 -0
  41. app/design/adminhtml/base/default/template/eurotext/translationmanager/selectemails.phtml +113 -0
  42. app/design/adminhtml/base/default/template/eurotext/translationmanager/selectlangfiles.phtml +129 -0
  43. app/design/adminhtml/base/default/template/eurotext/translationmanager/selectproducts.phtml +351 -0
  44. app/design/adminhtml/base/default/template/eurotext/translationmanager/settings.phtml +202 -0
  45. app/etc/modules/Eurotext_TranslationManager.xml +11 -0
  46. app/locale/de_DE/Eurotext_TranslationManager.csv +223 -0
  47. app/locale/en_US/Eurotext_TranslationManager.csv +227 -0
  48. package.xml +98 -0
  49. skin/adminhtml/base/default/eurotext/translationmanager/css/styles.css +206 -0
  50. skin/adminhtml/base/default/eurotext/translationmanager/images/btt_suche.png +0 -0
  51. skin/adminhtml/base/default/eurotext/translationmanager/images/cat-minus.png +0 -0
  52. skin/adminhtml/base/default/eurotext/translationmanager/images/cat-none.png +0 -0
  53. skin/adminhtml/base/default/eurotext/translationmanager/images/cat-plus.png +0 -0
  54. skin/adminhtml/base/default/eurotext/translationmanager/images/eurotext_logo.png +0 -0
  55. skin/adminhtml/base/default/eurotext/translationmanager/images/eurotext_table_head.png +0 -0
  56. skin/adminhtml/base/default/eurotext/translationmanager/images/logo_dixeno.png +0 -0
  57. skin/adminhtml/base/default/eurotext/translationmanager/images/logo_eurotext.png +0 -0
  58. skin/adminhtml/base/default/eurotext/translationmanager/images/pager_arrow_left.gif +0 -0
  59. skin/adminhtml/base/default/eurotext/translationmanager/images/pager_arrow_left_off.gif +0 -0
  60. skin/adminhtml/base/default/eurotext/translationmanager/images/pager_arrow_right.gif +0 -0
  61. skin/adminhtml/base/default/eurotext/translationmanager/images/pager_arrow_right_off.gif +0 -0
  62. skin/adminhtml/base/default/eurotext/translationmanager/js/eurotext-jquery-1.9.1.js +9597 -0
  63. skin/adminhtml/base/default/eurotext/translationmanager/js/eurotext.js +898 -0
app/code/community/Eurotext/TranslationManager/.DS_Store ADDED
Binary file
app/code/community/Eurotext/TranslationManager/Block/Help.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Help extends Mage_Adminhtml_Block_Template
4
+ {
5
+
6
+ }
app/code/community/Eurotext/TranslationManager/Block/Projects.php ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Projects extends Mage_Adminhtml_Block_Template
4
+ {
5
+ public function getSelectUrl($selectType, $project_id)
6
+ {
7
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_'.$selectType.'/index/',array('id' => $project_id));
8
+ }
9
+
10
+ public function getNewProjectUrl()
11
+ {
12
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_projects/addproject');
13
+ }
14
+
15
+ public function getSaveProjectUrl()
16
+ {
17
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_projects/saveproject');
18
+ }
19
+
20
+ public function getProjectUrl($project_id)
21
+ {
22
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_projects/index/id/'.intval($project_id));
23
+ }
24
+
25
+ public function getProjectDeleteUrl($project_id)
26
+ {
27
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_projects/delete/id/'.$project_id);
28
+ }
29
+
30
+ public function getProjectResetUrl($project_id)
31
+ {
32
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_projects/reset/id/'.$project_id);
33
+ }
34
+
35
+ public function getAjaxImportStepUrl()
36
+ {
37
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_projects/importstep');
38
+ }
39
+
40
+ public function getUploadUrl()
41
+ {
42
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_projects/upload');
43
+ }
44
+
45
+ public function getPostBackUrl2()
46
+ {
47
+ // Postback-URL für Projekt-Einstellungen
48
+ return ""; //return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_export/save');
49
+ }
50
+
51
+ public function getAjaxExportUrl()
52
+ {
53
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_projects/ajaxexport');
54
+ }
55
+
56
+ public function getProjects()
57
+ {
58
+ $tableName = Mage::getSingleton('core/resource')->getTableName('eurotext_translationmanager/project');
59
+
60
+ $dbres = Mage::getSingleton('core/resource');
61
+ $dbr=$dbres->getConnection('core_read');
62
+
63
+ $projects=$dbr->fetchAll("SELECT * FROM `".$tableName."` WHERE deleted=false ORDER BY id ASC");
64
+
65
+ return $projects;
66
+ }
67
+
68
+ public function getSelectedProjectId()
69
+ {
70
+ return intval(Mage::app()->getRequest()->getParam('id',-1));
71
+ }
72
+
73
+ public function getSelectedProject()
74
+ {
75
+ $id=$this->getSelectedProjectId();
76
+
77
+ $tableName = Mage::getSingleton('core/resource')->getTableName('eurotext_translationmanager/project');
78
+
79
+ $dbres = Mage::getSingleton('core/resource');
80
+ $dbr=$dbres->getConnection('core_read');
81
+
82
+ $projects=$dbr->fetchAll("SELECT * FROM `".$tableName."` WHERE id=".$id);
83
+
84
+ return $projects[0];
85
+ }
86
+
87
+ public function GetCheckedStr($val)
88
+ {
89
+ if (($val=="1") || ($val==true))
90
+ {
91
+ return "checked='checked'";
92
+ }
93
+
94
+ return "";
95
+ }
96
+
97
+ public function getStatusText($status_id)
98
+ {
99
+ if ($status_id==0)
100
+ {
101
+ return $this->__("New");
102
+ }
103
+ else if ($status_id==1)
104
+ {
105
+ return $this->__("In progress");
106
+ }
107
+ else if ($status_id==2)
108
+ {
109
+ return $this->__("In progress");
110
+ }
111
+ else if ($status_id==3)
112
+ {
113
+ return $this->__("Loaded");
114
+ }
115
+ }
116
+
117
+ public function getStoreviewTitle($store_id)
118
+ {
119
+ if ($store_id<0)
120
+ {
121
+ return $this->__("Not yet selected");
122
+ }
123
+
124
+ try
125
+ {
126
+ $store=Mage::app()->getStore($store_id);
127
+ $locale_code=Mage::getStoreConfig('general/locale/code', $store->getId());
128
+ return $store->getName()." (".$locale_code.")";
129
+ }
130
+ catch(Exception $e)
131
+ {
132
+ return $this->__("A storeview does not exist (anymore)")." (ID: '".$store_id."')";
133
+ }
134
+ }
135
+
136
+ public function getSpracheSelect($selectName, $selectedId,$disabledStr)
137
+ {
138
+ $html="<select ".$disabledStr." id='".$selectName."' autocomplete='off'>";
139
+ $html.="<option value='-1'>".$this->__("-- Select storeview --")."</option>";
140
+
141
+ $stores=Mage::app()->getStores();
142
+ foreach($stores as $store)
143
+ {
144
+ $locale_code=Mage::getStoreConfig('general/locale/code', $store->getId());
145
+
146
+ $selAttr="";
147
+ if ($store->getId()==$selectedId)
148
+ {
149
+ $selAttr="selected='selected'";
150
+ }
151
+
152
+ $html.="<option value='".$store->getId()."' ".$selAttr.">".$store->getName()." (".$locale_code.")</option>";
153
+ }
154
+ $html.="</select>";
155
+
156
+ return $html;
157
+ }
158
+ }
app/code/community/Eurotext/TranslationManager/Block/Register.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Register extends Mage_Adminhtml_Block_Template
4
+ {
5
+ public function getModuleVersion()
6
+ {
7
+ return (string) Mage::getConfig()->getNode()->modules->Eurotext_TranslationManager->version;
8
+ }
9
+
10
+ public function getPostBackUrl()
11
+ {
12
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_register/save');
13
+ }
14
+
15
+ // returns "saved", if the email was sent
16
+ public function getMessage()
17
+ {
18
+ if ($this->isSaved())
19
+ {
20
+ return $this->__("Last save").": ".$this->getSetting("register_mailsent_date","?");
21
+ }
22
+ else
23
+ {
24
+ return $this->__("Not yet saved");
25
+ }
26
+ }
27
+
28
+ // returns true, if the registration data was sent previously
29
+ public function isSaved()
30
+ {
31
+ return ($this->getSetting("register_mailsent","0")=="1");
32
+ }
33
+
34
+ public function getSetting($key)
35
+ {
36
+ $helper=Mage::helper('eurotext_translationmanager');
37
+ return $helper->getSetting($key,"");
38
+ }
39
+ }
app/code/community/Eurotext/TranslationManager/Block/Response/Ajax.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Response_Ajax extends Mage_Adminhtml_Block_Abstract {
4
+
5
+ public function returnJson(){
6
+ return $this->toJson($this->getResponseArray());
7
+ }
8
+
9
+ }
app/code/community/Eurotext/TranslationManager/Block/Selectcategories.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Selectcategories extends Mage_Adminhtml_Block_Template
4
+ {
5
+ private function getTableName($tblName)
6
+ {
7
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
8
+ }
9
+
10
+ private function getProjectId()
11
+ {
12
+ return intval($this->getRequest()->getParam("id"));
13
+ }
14
+
15
+ public function getSelectCategoriesUrl()
16
+ {
17
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectcategories/index',array('id' => $this->getProjectId()));
18
+ }
19
+
20
+ public function getSelectCategoriesSaveUrl()
21
+ {
22
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectcategories/save',array('id' => $this->getProjectId()));
23
+ }
24
+
25
+ public function getProject()
26
+ {
27
+ $id=$this->getProjectId();
28
+
29
+ $tableName = Mage::getSingleton('core/resource')->getTableName('eurotext_translationmanager/project');
30
+
31
+ $dbres = Mage::getSingleton('core/resource');
32
+ $dbr=$dbres->getConnection('core_read');
33
+
34
+ $projects=$dbr->fetchAll("SELECT * FROM `".$tableName."` WHERE id=".$id);
35
+
36
+ return $projects[0];
37
+ }
38
+
39
+ public function getRootCategory()
40
+ {
41
+ $project=$this->getProject();
42
+ $store=Mage::app()->getStore($project['storeview_src']);
43
+ $rootCategoryId=$store->getRootCategoryId();
44
+
45
+ $rootCategory=Mage::getModel("catalog/category")->load($rootCategoryId);
46
+ return $rootCategory;
47
+ }
48
+
49
+ public function getOpenedPathIds()
50
+ {
51
+ return array();
52
+ }
53
+
54
+ public function getSelectedCategoryIds()
55
+ {
56
+ $dbres = Mage::getSingleton('core/resource');
57
+ $dbr=$dbres->getConnection('core_read');
58
+
59
+ $rv=array();
60
+
61
+ $selectedCategories=$dbr->fetchAll("SELECT category_id FROM `".$this->getTableName('eurotext_project_categories')."` WHERE project_id=?",array($this->getProjectId()));
62
+ foreach($selectedCategories as $selectedCategory)
63
+ {
64
+ array_push($rv,$selectedCategory['category_id']);
65
+ }
66
+
67
+ return $rv;
68
+ }
69
+
70
+ public function getSearchResult()
71
+ {
72
+ $pagesize=20;
73
+
74
+ $rv=array();
75
+ $rv['find']=trim($this->getRequest()->getParam("find"));
76
+ $page_current=intval($this->getRequest()->getParam("page"));
77
+
78
+ $dbres = Mage::getSingleton('core/resource');
79
+ $dbr=$dbres->getConnection('core_read');
80
+
81
+ $eavAttribute = Mage::getModel('eav/entity_attribute');
82
+ $name_id = $eavAttribute->getIdByCode('catalog_category', 'name');
83
+
84
+ $findme="%".$rv['find']."%";
85
+
86
+ $sql="SELECT a.store_id, a.value as article_name, e.sku, e.entity_id FROM `".$this->getTableName('catalog_product_entity_varchar')."` a, `".$this->getTableName('catalog_product_entity')."` e WHERE (a.entity_id=e.entity_id) AND a.store_id=0 AND (a.attribute_id=?) AND ((UPPER(e.sku) LIKE ?) OR (UPPER(a.value) LIKE ?)) ORDER BY article_name ASC, e.entity_id ASC";
87
+ $allProducts=$dbr->fetchAll($sql,array($name_id,$findme,$findme));
88
+
89
+ $pageCount=intval(count($allProducts)/$pagesize);
90
+ if (($pageCount*$pagesize)<count($allProducts))
91
+ {
92
+ $pageCount++;
93
+ }
94
+ $rv['page_last']=$pageCount;
95
+
96
+ if ($page_current>$pageCount)
97
+ {
98
+ $page_current=$pageCount;
99
+ }
100
+ if ($page_current<1)
101
+ {
102
+ $page_current=1;
103
+ }
104
+
105
+ if ($pageCount<1)
106
+ {
107
+ $pageCount=1;
108
+ }
109
+
110
+ // Result-Array:
111
+ $resultProducts=array();
112
+ $ofs_start=($page_current-1)*$pageCount;
113
+ $ofs_end=$ofs_start+$pagesize;
114
+ if ($ofs_end>=count($allProducts))
115
+ {
116
+ $ofs_end=count($allProducts)-1;
117
+ }
118
+
119
+ for ($i=$ofs_start; $i<=$ofs_end; $i++)
120
+ {
121
+ $prod=$allProducts[$i];
122
+ $prod['checked']=false;
123
+
124
+ // Already selected?
125
+ $sql="SELECT product_id FROM `".$this->getTableName('eurotext_project_products')."` p WHERE (p.project_id=?) AND (p.product_id=?)";
126
+ $selProducts=$dbr->fetchAll($sql,array($this->getProjectId(),$prod['entity_id']));
127
+ $prod['checked']=(count($selProducts)>0);
128
+
129
+ array_push($resultProducts,$prod);
130
+ }
131
+
132
+ $rv['page_current']=$page_current;
133
+ $rv['page_last']=$pageCount;
134
+ $rv['result_count']=count($allProducts);
135
+ $rv['products']=$resultProducts;
136
+
137
+ return $rv;
138
+ }
139
+ }
app/code/community/Eurotext/TranslationManager/Block/Selectcmspages.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Selectcmspages extends Mage_Adminhtml_Block_Template
4
+ {
5
+ private function getTableName($tblName)
6
+ {
7
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
8
+ }
9
+
10
+ private function getProjectId()
11
+ {
12
+ return intval($this->getRequest()->getParam("id"));
13
+ }
14
+
15
+ public function getSelectCmsPagesUrl()
16
+ {
17
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectcmspages/index',array('id' => $this->getProjectId()));
18
+ }
19
+
20
+ public function getSelectCmsPagesSaveUrl()
21
+ {
22
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectcmspages/save',array('id' => $this->getProjectId()));
23
+ }
24
+
25
+ public function getProject()
26
+ {
27
+ $id=$this->getProjectId();
28
+
29
+ $tableName = Mage::getSingleton('core/resource')->getTableName('eurotext_translationmanager/project');
30
+
31
+ $dbres = Mage::getSingleton('core/resource');
32
+ $dbr=$dbres->getConnection('core_read');
33
+
34
+ $projects=$dbr->fetchAll("SELECT * FROM `".$tableName."` WHERE id=".$id);
35
+
36
+ return $projects[0];
37
+ }
38
+
39
+ public function getCMSPages()
40
+ {
41
+ $dbres = Mage::getSingleton('core/resource');
42
+ $dbr=$dbres->getConnection('core_read');
43
+
44
+ $project=$this->getProject();
45
+ $storeview_src=$project['storeview_src'];
46
+
47
+ $rv=array();
48
+ $selectedCMSPages=array();
49
+
50
+ $selectedPageIds=$dbr->fetchAll("SELECT page_id FROM `".$this->getTableName('eurotext_project_cmspages')."` WHERE project_id=?",array($this->getProjectId()));
51
+ foreach($selectedPageIds as $selectedPageId)
52
+ {
53
+ array_push($selectedCMSPages,$selectedPageId['page_id']);
54
+ }
55
+
56
+ // cms-pages:
57
+ $mPages=$dbr->fetchAll("SELECT * FROM `".$this->getTableName('cms_page')."` ORDER BY title ASC");
58
+ foreach($mPages as $mPage)
59
+ {
60
+ // Is this CMS-Page activated for the source storeview?
61
+ $storeCount=intval($dbr->fetchOne("SELECT COUNT(*) FROM `".$this->getTableName('cms_page_store')."` WHERE page_id=".$mPage['page_id']." AND (store_id=0 OR store_id=?)",array($storeview_src)));
62
+ if ($storeCount>0) // Has activated store
63
+ {
64
+ $page=array();
65
+ $page['page_id']=$mPage['page_id'];
66
+ $page['title']=$mPage['title'];
67
+ $page['checked']=in_array($mPage['page_id'],$selectedCMSPages);
68
+ $page['identifier']=$mPage['identifier'];
69
+ $page['type']=$this->__("CMS-Page");
70
+
71
+ array_push($rv,$page);
72
+ }
73
+ }
74
+
75
+ return $rv;
76
+ }
77
+
78
+ public function getCMSBlocks()
79
+ {
80
+ $dbres = Mage::getSingleton('core/resource');
81
+ $dbr=$dbres->getConnection('core_read');
82
+
83
+ $project=$this->getProject();
84
+ $storeview_src=$project['storeview_src'];
85
+
86
+ $rv=array();
87
+ $selectedCMSBlocks=array();
88
+
89
+ $selectedBlockIds=$dbr->fetchAll("SELECT block_id FROM `".$this->getTableName('eurotext_project_cmsblocks')."` WHERE project_id=?",array($this->getProjectId()));
90
+ foreach($selectedBlockIds as $selectedBlockId)
91
+ {
92
+ array_push($selectedCMSBlocks,$selectedBlockId['block_id']);
93
+ }
94
+
95
+ // static blocks:
96
+ $mPages=$dbr->fetchAll("SELECT * FROM `".$this->getTableName('cms_block')."` ORDER BY title ASC");
97
+ foreach($mPages as $mPage)
98
+ {
99
+ // Is this CMS-Block activated for the source storeview?
100
+ $storeCount=intval($dbr->fetchOne("SELECT COUNT(*) FROM `".$this->getTableName('cms_block_store')."` WHERE block_id=".$mPage['block_id']." AND (store_id=0 OR store_id=?)",array($storeview_src)));
101
+ if ($storeCount>0) // Has activated store
102
+ {
103
+ $page=array();
104
+ $page['block_id']=$mPage['block_id'];
105
+ $page['title']=$mPage['title'];
106
+ $page['checked']=in_array($mPage['block_id'],$selectedCMSBlocks);
107
+ $page['identifier']=$mPage['identifier'];
108
+ $page['type']=$this->__("CMS-Block");
109
+
110
+ array_push($rv,$page);
111
+ }
112
+ }
113
+
114
+ return $rv;
115
+ }
116
+ }
app/code/community/Eurotext/TranslationManager/Block/Selectemails.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Selectemails extends Mage_Adminhtml_Block_Template
4
+ {
5
+ private function getTableName($tblName)
6
+ {
7
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
8
+ }
9
+
10
+ private function getProjectId()
11
+ {
12
+ return intval($this->getRequest()->getParam("id"));
13
+ }
14
+
15
+ public function getSelectEmailsUrl()
16
+ {
17
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectemails/index',array('id' => $this->getProjectId()));
18
+ }
19
+
20
+ public function getSelectEmailsSaveUrl()
21
+ {
22
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectemails/save',array('id' => $this->getProjectId()));
23
+ }
24
+
25
+ public function getProject()
26
+ {
27
+ $id=$this->getProjectId();
28
+
29
+ $tableName = Mage::getSingleton('core/resource')->getTableName('eurotext_translationmanager/project');
30
+
31
+ $dbres = Mage::getSingleton('core/resource');
32
+ $dbr=$dbres->getConnection('core_read');
33
+
34
+ $projects=$dbr->fetchAll("SELECT * FROM `".$tableName."` WHERE id=".$id);
35
+
36
+ $project=$projects[0];
37
+
38
+ // storeview_src_locale:
39
+ $project['storeview_src_locale']="en_US";
40
+ if ($project['storeview_src']>=0)
41
+ {
42
+ $project['storeview_src_locale']=Mage::getStoreConfig('general/locale/code', $project['storeview_src']);
43
+ }
44
+
45
+ // storeview_dst_locale:
46
+ $project['storeview_dst_locale']="en_US";
47
+ if ($project['storeview_dst']>=0)
48
+ {
49
+ $project['storeview_dst_locale']=Mage::getStoreConfig('general/locale/code', $project['storeview_dst']);
50
+ }
51
+
52
+ return $project;
53
+ }
54
+
55
+ public function getEMailTemplates()
56
+ {
57
+ $dbres = Mage::getSingleton('core/resource');
58
+ $dbr=$dbres->getConnection('core_read');
59
+
60
+ $project=$this->getProject();
61
+ $storeview_src=$project['storeview_src'];
62
+
63
+ $rv=array();
64
+
65
+ $helper=Mage::helper('eurotext_translationmanager');
66
+ $helper->ajaxexportAction_CollectEMailTemplates($project);
67
+
68
+ $langfiles=$dbr->fetchAll("SELECT file_hash, filename, translate_flag FROM `".$this->getTableName('eurotext_emailtemplates')."` WHERE project_id=".$project['id']." AND locale_dst='".$project['storeview_src_locale']."' ORDER BY filename ASC");
69
+ foreach($langfiles as $langfile)
70
+ {
71
+ $rvItem=array();
72
+ $rvItem['file_hash']=$langfile['file_hash'];
73
+ $rvItem['filename']=$langfile['filename'];
74
+ $rvItem['checked']=((intval($langfile['translate_flag']))>0);
75
+
76
+ array_push($rv,$rvItem);
77
+ }
78
+
79
+ return $rv;
80
+ }
81
+ }
app/code/community/Eurotext/TranslationManager/Block/Selectlangfiles.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Selectlangfiles extends Mage_Adminhtml_Block_Template
4
+ {
5
+ private function getTableName($tblName)
6
+ {
7
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
8
+ }
9
+
10
+ private function getProjectId()
11
+ {
12
+ return intval($this->getRequest()->getParam("id"));
13
+ }
14
+
15
+ public function getSelectLangfilesUrl()
16
+ {
17
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectlangfiles/index',array('id' => $this->getProjectId()));
18
+ }
19
+
20
+ public function getSelectLangfilesSaveUrl()
21
+ {
22
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectlangfiles/save',array('id' => $this->getProjectId()));
23
+ }
24
+
25
+ public function getProject()
26
+ {
27
+ $id=$this->getProjectId();
28
+
29
+ $tableName = Mage::getSingleton('core/resource')->getTableName('eurotext_translationmanager/project');
30
+
31
+ $dbres = Mage::getSingleton('core/resource');
32
+ $dbr=$dbres->getConnection('core_read');
33
+
34
+ $projects=$dbr->fetchAll("SELECT * FROM `".$tableName."` WHERE id=".$id);
35
+
36
+ return $projects[0];
37
+ }
38
+
39
+ public function getLangfiles()
40
+ {
41
+ $dbres = Mage::getSingleton('core/resource');
42
+ $dbr=$dbres->getConnection('core_read');
43
+
44
+ $project=$this->getProject();
45
+ $storeview_src=$project['storeview_src'];
46
+
47
+ $rv=array();
48
+
49
+ $projectsController=Mage::helper('eurotext_translationmanager');
50
+ $projectsController->ajaxexportAction_CollectLangfiles($project);
51
+
52
+ $langfiles=$dbr->fetchAll("SELECT line_hash, filename, translate_flag FROM `".$this->getTableName('eurotext_csv')."` WHERE locale_dst='en_US' AND project_id=".$project['id']." ORDER BY filename ASC");
53
+ foreach($langfiles as $langfile)
54
+ {
55
+ $rvItem=array();
56
+ $rvItem['line_hash']=$langfile['line_hash'];
57
+ $rvItem['filename']=$langfile['filename'];
58
+ $rvItem['checked']=((intval($langfile['translate_flag']))>0);
59
+
60
+ array_push($rv,$rvItem);
61
+ }
62
+
63
+ return $rv;
64
+ }
65
+ }
app/code/community/Eurotext/TranslationManager/Block/Selectproducts.php ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Selectproducts extends Mage_Adminhtml_Block_Template
4
+ {
5
+ public function updateFilterData()
6
+ {
7
+ if ($this->getRequest()->getParam("status")!="")
8
+ {
9
+ $filter_status=intval($this->getRequest()->getParam("status"));
10
+ $filter_stock=intval($this->getRequest()->getParam("stock"));
11
+ $filter_product_type=$this->getRequest()->getParam("producttype");
12
+ $project_id=$this->getProjectId();
13
+
14
+ $dbres = Mage::getSingleton('core/resource');
15
+ $dbw=$dbres->getConnection('core_write');
16
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project')."` SET filter_status=?, filter_stock=?, filter_product_type=? WHERE id=?;",array($filter_status,$filter_stock,$filter_product_type,$project_id));
17
+ }
18
+ }
19
+
20
+ private function getProjectId()
21
+ {
22
+ return intval($this->getRequest()->getParam("id"));
23
+ }
24
+
25
+ public function getSelectProductsUrl()
26
+ {
27
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectproducts/index',array('id' => $this->getProjectId()));
28
+ }
29
+
30
+ public function getSelectProductsSaveUrl()
31
+ {
32
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_selectproducts/save'); //,array('id' => $this->getProjectId()));
33
+ }
34
+
35
+ private function getTableName($tblName)
36
+ {
37
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
38
+ }
39
+
40
+ public function getOpenCatIds()
41
+ {
42
+ // Parse catids (list of open categories)
43
+ $open_catids_str=$this->getRequest()->getParam("catids");
44
+
45
+ $open_catids_list=explode(",",$open_catids_str);
46
+ $open_catids=array();
47
+ foreach($open_catids_list as $opencatid)
48
+ {
49
+ $cat_id=intval($opencatid); // prevent non-numeric ids
50
+ array_push($open_catids,$cat_id);
51
+ }
52
+ array_push($open_catids,1); // root-category is always open
53
+ $open_catids=array_unique($open_catids);
54
+
55
+ return $open_catids;
56
+ }
57
+
58
+ public function getCategoryTreeHTML()
59
+ {
60
+ $pagesize=20;
61
+
62
+ $dbres = Mage::getSingleton('core/resource');
63
+ $dbr=$dbres->getConnection('core_read');
64
+
65
+ $rv=array();
66
+ $rv['find']=trim($this->getRequest()->getParam("find"));
67
+ $page_current=intval($this->getRequest()->getParam("page"));
68
+
69
+ $pagesize=intval($this->getRequest()->getParam("pagesize"));
70
+ if ($pagesize<20)
71
+ {
72
+ $pagesize=20;
73
+ }
74
+
75
+ $helper=Mage::helper('eurotext_translationmanager');
76
+
77
+ $open_catids=$this->getOpenCatIds();
78
+ $root=$helper->getCategoryTree(1,$open_catids);
79
+ $baselink=$this->getSelectProductsUrl()."find/".urlencode($rv['find'])."/pagesize/".$pagesize;
80
+
81
+ $current_path=array();
82
+ $project_id=$this->getProjectId();
83
+
84
+ return $this->getCategoryTreeHTMLInternal($dbr,$project_id,$root,-1,$open_catids,$current_path,$baselink);
85
+ }
86
+
87
+ private function getCategoryTreeHTMLInternal($dbr, $project_id, $treeNode, $lvl, $open_catids, $current_path, $baselink)
88
+ {
89
+
90
+ $rv = '';
91
+ $_lvl=max(0,$lvl);
92
+ $marginLeft=($_lvl)*22;
93
+
94
+ $current_path_new=array_merge(array(),$current_path);
95
+ array_push($current_path_new,$treeNode['id']);
96
+ $current_path_new=array_unique($current_path_new);
97
+
98
+ $showChildren=false;
99
+ $isSelected=in_array($treeNode['id'],$open_catids);
100
+
101
+ $cssClass="eurotext_category_none";
102
+ if ($treeNode['hasChildren'])
103
+ {
104
+ if ($isSelected)
105
+ {
106
+ $showChildren=true;
107
+ $cssClass="eurotext_category_minus";
108
+ }
109
+ else
110
+ {
111
+ $cssClass="eurotext_category_plus id_".$treeNode['id']." path_".implode("_",$open_catids);
112
+ }
113
+ }
114
+ if ($treeNode['id']==1)
115
+ {
116
+ $cssClass="eurotext_category_none"; // no 'open'-icon on root-category
117
+ }
118
+
119
+ $linkStyle="color:black;text-decoration:none;padding:2px;";
120
+ if ($isSelected)
121
+ {
122
+ if ( ($treeNode['id']==1) && (count($open_catids)>1))
123
+ {
124
+ // Ignore selection on root category
125
+ }
126
+ else
127
+ {
128
+ $linkStyle="color:black;text-decoration:none;background-color: rgb(245, 214, 199);padding:2px;";
129
+ }
130
+ }
131
+
132
+ // Determine category state:
133
+ $categoryState=Mage::helper('eurotext_translationmanager')->getTreeNodeTranslationState($dbr,$treeNode['id'],$project_id);
134
+
135
+ $checkedStr="";
136
+ if ($categoryState=="checked")
137
+ {
138
+ $checkedStr="checked='checked'";
139
+ }
140
+
141
+
142
+ $rv .= "<table style='margin-left:".$marginLeft."px;'> \r\n";
143
+ $rv .= " <tr> \r\n";
144
+ $rv .= " <td><div class='".$cssClass."'></div></td> \r\n"; // Icon
145
+ $rv .= " <td><input type='checkbox' autocomplete='off' x-state='".$categoryState."' x-catid='".$treeNode['id']."' class='eurotext_catsel' id='eurotext_catsel_".$treeNode['id']."' ".$checkedStr." /> </td> \r\n";
146
+ $rv .= " <td><a style='".$linkStyle."' href='".$baselink."/catids/".implode(",",$current_path_new)."'>".htmlentities($treeNode['name'])."</a></td> \r\n";
147
+ $rv .= " </tr> \r\n";
148
+ $rv .= "</table> \r\n";
149
+
150
+ if ($categoryState=="indeterminate")
151
+ {
152
+ $rv .= "<script>document.getElementById('eurotext_catsel_".$treeNode['id']."').indeterminate = true;</script> \r\n";
153
+ }
154
+
155
+ if ($showChildren)
156
+ {
157
+ foreach($treeNode['childs'] as $child)
158
+ {
159
+ $rv .= $this->getCategoryTreeHTMLInternal($dbr,$project_id,$child,($lvl+1),$open_catids,$current_path_new,$baselink);
160
+ }
161
+ }
162
+
163
+ return $rv;
164
+
165
+ }
166
+
167
+ private $filterDataRead=false;
168
+ private $filter_status=1;
169
+ private $filter_stock=1;
170
+ private $filter_product_type="";
171
+
172
+ private function readFilterData()
173
+ {
174
+ if ($this->filterDataRead)
175
+ {
176
+ return;
177
+ }
178
+
179
+ $dbres = Mage::getSingleton('core/resource');
180
+ $dbr=$dbres->getConnection('core_read');
181
+
182
+ $project_id=$this->getProjectId();
183
+
184
+ $allProducts=$dbr->fetchAll("SELECT filter_status, filter_stock, filter_product_type FROM `".$this->getTableName('eurotext_project')."` WHERE id=?",array($project_id));
185
+ foreach($allProducts as $row)
186
+ {
187
+ $this->filter_status=$row['filter_status'];
188
+ $this->filter_stock=$row['filter_stock'];
189
+ $this->filter_product_type=$row['filter_product_type'];
190
+ }
191
+
192
+ $this->filterDataRead=true;
193
+ }
194
+
195
+ public function getFilterStatus()
196
+ {
197
+ $this->readFilterData();
198
+
199
+ return $this->filter_status;
200
+ }
201
+
202
+ public function getFilterStock()
203
+ {
204
+ $this->readFilterData();
205
+
206
+ return $this->filter_stock;
207
+ }
208
+
209
+ public function getFilterProductType()
210
+ {
211
+ $this->readFilterData();
212
+
213
+ $tmp=$this->filter_product_type;
214
+
215
+ $validProductTypes=array("simple","grouped","configurable","virtual","bundle","downloadable");
216
+ if (in_array($tmp,$validProductTypes))
217
+ {
218
+ return $tmp;
219
+ }
220
+ else
221
+ {
222
+ return "";
223
+ }
224
+ }
225
+
226
+ public function getSearchResult()
227
+ {
228
+ $pagesize=20;
229
+
230
+ $rv=array();
231
+ $rv['find']=trim($this->getRequest()->getParam("find"));
232
+ $page_current=intval($this->getRequest()->getParam("page"));
233
+
234
+ $pagesize=intval($this->getRequest()->getParam("pagesize"));
235
+ if ($pagesize<20)
236
+ {
237
+ $pagesize=20;
238
+ }
239
+
240
+ $dbres = Mage::getSingleton('core/resource');
241
+ $dbr=$dbres->getConnection('core_read');
242
+
243
+ $eavAttribute = Mage::getModel('eav/entity_attribute');
244
+ $name_id = $eavAttribute->getIdByCode('catalog_product', 'name');
245
+
246
+ $attr_status_id=$eavAttribute->getIdByCode('catalog_product', 'status');
247
+
248
+ $findme="%".$rv['find']."%";
249
+
250
+ $sql_categoryfilter="";
251
+ $open_catids=$this->getOpenCatIds();
252
+ if (count($open_catids)>0)
253
+ {
254
+ $selected_catid=$open_catids[count($open_catids)-1]; // Last ID is the selected category
255
+ if ($selected_catid>1) // root-category has id 1
256
+ {
257
+ $search_catids=Mage::helper('eurotext_translationmanager')->getAllSubCategories($selected_catid); // Get the IDs of all children (direct+indirect)
258
+ array_push($search_catids,$selected_catid); // add selected category to list
259
+
260
+ // Filter to products which are assigned to any category in $search_catids:
261
+ $sql_categoryfilter=" AND e.entity_id IN (SELECT cat.product_id FROM catalog_category_product cat WHERE cat.category_id IN (".implode(",",$search_catids)."))";
262
+ }
263
+ }
264
+
265
+ $sql_stockfilter=" AND e.entity_id IN (SELECT stock.product_id FROM cataloginventory_stock_item stock WHERE stock.is_in_stock=".$this->getFilterStock().")";
266
+ $sql_statusfilter=" AND e.entity_id IN (SELECT pstatus.entity_id FROM catalog_product_entity_int pstatus WHERE pstatus.attribute_id=".$attr_status_id." AND pstatus.value=".$this->getFilterStatus().")";
267
+
268
+ if ($this->getFilterProductType()!=="")
269
+ {
270
+ $sql_statusfilter.=" AND e.type_id='".$this->getFilterProductType()."'";
271
+ }
272
+
273
+ $sql="SELECT a.store_id, a.value as article_name, e.sku, e.entity_id FROM `".$this->getTableName('catalog_product_entity_varchar')."` a, `".$this->getTableName('catalog_product_entity')."` e WHERE (a.entity_id=e.entity_id) AND a.store_id=0 AND (a.attribute_id=?) AND ((UPPER(e.sku) LIKE ?) OR (UPPER(a.value) LIKE ?)) $sql_categoryfilter $sql_stockfilter $sql_statusfilter ORDER BY article_name ASC, e.entity_id ASC";
274
+
275
+ //echo $sql;
276
+ $allProducts=$dbr->fetchAll($sql,array($name_id,$findme,$findme));
277
+
278
+ $pageCount=intval(count($allProducts)/$pagesize);
279
+ if (($pageCount*$pagesize)<count($allProducts))
280
+ {
281
+ $pageCount++;
282
+ }
283
+ $rv['page_last']=$pageCount;
284
+
285
+ if ($page_current>$pageCount)
286
+ {
287
+ $page_current=$pageCount;
288
+ }
289
+ if ($page_current<1)
290
+ {
291
+ $page_current=1;
292
+ }
293
+
294
+ if ($pageCount<1)
295
+ {
296
+ $pageCount=1;
297
+ }
298
+
299
+ // Result-Array:
300
+ $resultProducts=array();
301
+ $ofs_start=($page_current-1)*$pageCount;
302
+ $ofs_end=$ofs_start+$pagesize;
303
+ if ($ofs_end>=count($allProducts))
304
+ {
305
+ $ofs_end=count($allProducts)-1;
306
+ }
307
+
308
+ for ($i=$ofs_start; $i<=$ofs_end; $i++)
309
+ {
310
+ $prod=$allProducts[$i];
311
+ $prod['checked']=false;
312
+
313
+ // Already selected?
314
+ $sql="SELECT product_id FROM `".$this->getTableName('eurotext_project_products')."` p WHERE (p.project_id=?) AND (p.product_id=?)";
315
+ $selProducts=$dbr->fetchAll($sql,array($this->getProjectId(),$prod['entity_id']));
316
+ $prod['checked']=(count($selProducts)>0);
317
+
318
+ array_push($resultProducts,$prod);
319
+ }
320
+
321
+ $rv['page_current']=$page_current;
322
+ $rv['page_last']=$pageCount;
323
+ $rv['page_size']=$pagesize;
324
+ $rv['result_count']=count($allProducts);
325
+ $rv['products']=$resultProducts;
326
+
327
+ return $rv;
328
+ }
329
+ }
app/code/community/Eurotext/TranslationManager/Block/Settings.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Block_Settings extends Mage_Adminhtml_Block_Template
4
+ {
5
+ public function getSaveUrl()
6
+ {
7
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_settings/save');
8
+ }
9
+
10
+ public function getUpgradeScopeUrl()
11
+ {
12
+ return Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_settings/upgradescope');
13
+ }
14
+
15
+ public function getStoreviews()
16
+ {
17
+ $array_stores=array();
18
+
19
+ $stores=Mage::app()->getStores();
20
+ foreach($stores as $store)
21
+ {
22
+ $array_store=array();
23
+ $array_store['store_id']=$store->getId();
24
+ $array_store['name']=$store->getName();
25
+ $array_store['code']=$store->getCode();
26
+ $array_store['locale']=Mage::getStoreConfig('general/locale/code', $store->getId());
27
+
28
+ array_push($array_stores,$array_store);
29
+ }
30
+
31
+ return $array_stores;
32
+ }
33
+
34
+ }
app/code/community/Eurotext/TranslationManager/Helper/Data.php ADDED
@@ -0,0 +1,566 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ */
4
+ class Eurotext_TranslationManager_Helper_Data extends Mage_Core_Helper_Abstract
5
+ {
6
+ const XML_PATH_EUROTEXT_DEBUGMODE = 'dev/log/ettm_debug';
7
+ private $help_url="http://www.eurotext.de";
8
+
9
+ private $live_ftp_host="eurotext-services.de";
10
+ private $live_ftp_port=21;
11
+ private $live_registration_email="magento@eurotext.de";
12
+ private $live_registration_email_name="Eurotext Magento (Live)";
13
+
14
+ private $exportProducts_minPerFile=6;
15
+ private $exportCategories_minPerFile=6;
16
+
17
+ private $debug_registration_email="debug@eurotext.de";
18
+ private $debug_registration_email_name="Eurotext Magento (Debug)";
19
+
20
+ public function getExportProductsMinPerFile()
21
+ {
22
+ return $this->exportProducts_minPerFile;
23
+ }
24
+
25
+ public function getExportCategoriesMinPerFile()
26
+ {
27
+ return $this->exportCategories_minPerFile;
28
+ }
29
+
30
+ public function getDebugMode()
31
+ {
32
+ return Mage::getStoreConfigFlag(self::XML_PATH_EUROTEXT_DEBUGMODE);
33
+ }
34
+
35
+ public function getCustomProductAttributesForExport(){
36
+ // @TODO Modul ist noch nicht Scope-Fähig, wir lassen das erstmal so und stellen das später auf Mage::getStoreConfig um.
37
+ $custom_attributes = Mage::getConfig()->getNode('default/eurotext/translation_manager/custom_product_attributes');
38
+ return $custom_attributes ? $custom_attributes->asArray() : false;
39
+ }
40
+
41
+ public function getCustomCategoryAttributesForExport(){
42
+ // @TODO Modul ist noch nicht Scope-Fähig, wir lassen das erstmal so und stellen das später auf Mage::getStoreConfig um.
43
+ $custom_attributes = Mage::getConfig()->getNode('default/eurotext/translation_manager/custom_category_attributes');
44
+ return $custom_attributes ? $custom_attributes->asArray() : false;
45
+ }
46
+
47
+ // Prüft, ob der URL-Key von Produkt/Kategorie auf "global" statt "storeview" steht
48
+ // Liefert true, zurück wenn mindestens ein Scope NICHT(!) auf "storeview" steht
49
+ public function urlKeyScopeIsGlobal()
50
+ {
51
+ $tbl_eav_attribute = Mage::getSingleton('core/resource')->getTableName('eav_attribute');
52
+ $tbl_catalog_eav_attribute = Mage::getSingleton('core/resource')->getTableName('catalog_eav_attribute');
53
+
54
+ $dbres = Mage::getSingleton('core/resource');
55
+ $dbr=$dbres->getConnection('core_read');
56
+
57
+ $result=$dbr->fetchAll("SELECT v.is_global, a.attribute_id, a.attribute_code FROM `".$tbl_eav_attribute."` a, `".$tbl_catalog_eav_attribute."` v WHERE (a.attribute_id=v.attribute_id) AND ((a.attribute_code='url_key') OR (a.attribute_code='url_path'))");
58
+ foreach($result as $row)
59
+ {
60
+ if ($row['is_global']>0)
61
+ {
62
+ return true;
63
+ }
64
+ }
65
+
66
+ return false;
67
+ }
68
+
69
+ /**
70
+ * Checks for a incremental url
71
+ * if increment value is detected it'll +1 the value
72
+ * else will create an increment
73
+ *
74
+ * @param string $url
75
+ */
76
+ public function getUniqueUrl($url)
77
+ {
78
+ if (preg_match('/^(.*\-)(\d+)$/i', $url, $matches) == 1) {
79
+ return $matches[1] . ++$matches[2];
80
+ } else {
81
+ return $url . '-1';
82
+ }
83
+ }
84
+
85
+ public function getLocaleInfoByMagentoLocale($locale)
86
+ {
87
+ $backendLocale = Mage::app()->getLocale()->getLocaleCode();
88
+
89
+ $rv=array();
90
+ $rv['locale']=$locale;
91
+ $rv['locale_eurotext']="-";
92
+ $rv['lang_name']=$this->__("Unsupported language");
93
+ $rv['supported']=false;
94
+
95
+ $dbres = Mage::getSingleton('core/resource');
96
+ $dbr= $dbres->getConnection('core_read');
97
+
98
+ $tbl_eurotext_languages = Mage::getSingleton('core/resource')->getTableName('eurotext_languages');
99
+
100
+ // Try current backend language first:
101
+ $localeInfos=$dbr->fetchAll("SELECT * FROM `".$tbl_eurotext_languages."` WHERE UPPER(locale_magento)=?",array(strtoupper($locale)));
102
+ if (count($localeInfos)>0)
103
+ {
104
+ $localeInfo=$localeInfos[0];
105
+ $rv['lang_name']=$localeInfo['lang_name'];
106
+ $rv['locale_eurotext']=$localeInfo['locale_eurotext'];
107
+ $rv['supported']=true;
108
+
109
+ return $rv;
110
+ }
111
+
112
+ return $rv;
113
+ }
114
+
115
+ public function getLocaleInfoByEurotextLocale($locale)
116
+ {
117
+ $backendLocale = Mage::app()->getLocale()->getLocaleCode();
118
+
119
+ $rv=array();
120
+ $rv['locale']=$locale;
121
+ $rv['locale_eurotext']=$locale;
122
+ $rv['lang_name']=$this->__("Unsupported language");
123
+ $rv['supported']=false;
124
+
125
+ $dbres = Mage::getSingleton('core/resource');
126
+ $dbr= $dbres->getConnection('core_read');
127
+
128
+ $tbl_eurotext_languages = Mage::getSingleton('core/resource')->getTableName('eurotext_languages');
129
+
130
+ // Try current backend language first:
131
+ $localeInfos=$dbr->fetchAll("SELECT * FROM `".$tbl_eurotext_languages."` WHERE UPPER(locale_eurotext)=?",array(strtoupper($locale)));
132
+ if (count($localeInfos)>0)
133
+ {
134
+ $localeInfo=$localeInfos[0];
135
+ $rv['locale']=$localeInfo['locale_magento'];
136
+ $rv['lang_name']=$localeInfo['lang_name'];
137
+ $rv['locale_eurotext']=$localeInfo['locale_eurotext'];
138
+ $rv['supported']=true;
139
+
140
+ return $rv;
141
+ }
142
+
143
+ return $rv;
144
+ }
145
+
146
+
147
+ public function getRegistrationRecipient()
148
+ {
149
+ $rv=array();
150
+
151
+ if ($this->getDebugMode())
152
+ {
153
+ $rv["email"]=$this->debug_registration_email;
154
+ $rv["name"]=$this->debug_registration_email_name;
155
+ }
156
+ else
157
+ {
158
+ $rv["email"]=$this->live_registration_email;
159
+ $rv["name"]=$this->live_registration_email_name;
160
+ }
161
+
162
+ return $rv;
163
+ }
164
+
165
+ public function getHelpUrl()
166
+ {
167
+ return $this->help_url;
168
+ }
169
+
170
+ public function saveSetting($key, $val)
171
+ {
172
+ $dbres = Mage::getSingleton('core/resource');
173
+ $dbw= $dbres->getConnection('core_write');
174
+
175
+ $tbl_eurotext_config = Mage::getSingleton('core/resource')->getTableName('eurotext_config');
176
+
177
+ $val = $this->sanitize($val);
178
+
179
+ $dbw->query("INSERT IGNORE INTO `".$tbl_eurotext_config."` (config_key, config_value) VALUES (?,?);",array($key,$val));
180
+ $dbw->query("UPDATE `".$tbl_eurotext_config."` SET config_value=? WHERE config_key=?;",array($val,$key));
181
+ }
182
+
183
+ public function log($message, $level = Zend_Log::DEBUG){
184
+ if($this->getDebugMode()) {
185
+ Mage::log($message, $level, 'eurotext.log', true);
186
+ } elseif ($level < Zend_Log::ERR) {
187
+ Mage::log($message, $level, 'eurotext_fatal.log', true);
188
+ }
189
+ }
190
+
191
+ public function openFtpConnection()
192
+ {
193
+ if (!function_exists("ftp_connect")) {
194
+ $this->log('There is no FTP Client available: ftp_connect does not exist.', Zend_Log::CRIT);
195
+
196
+ return false;
197
+ }
198
+
199
+
200
+ $ftp_host=$this->live_ftp_host;
201
+ $ftp_port=$this->live_ftp_port;
202
+
203
+
204
+ return ftp_connect($ftp_host,$ftp_port,30);
205
+
206
+ }
207
+
208
+ public function testFtpConnection()
209
+ {
210
+ $helper=Mage::helper('eurotext_translationmanager');
211
+ $et_username=$helper->getSetting("eurotext_username","");
212
+ $et_password=Mage::helper('core')->decrypt($helper->getSetting("eurotext_password",""));
213
+
214
+ $ftp_username=$et_username;
215
+ $ftp_password=$et_password;
216
+
217
+
218
+ $rv=array();
219
+ $rv['ok']=false;
220
+ $rv['statusmessage']="Unknown error";
221
+
222
+ if (trim($ftp_username)=="")
223
+ {
224
+ $this->log('Login data is not set.', Zend_Log::ERR);
225
+ $rv['statusmessage']="<span class='et_error'>".$this->__("There seems to be a problem with your login data. Please check username and password!")."</span>";
226
+ }
227
+ else
228
+ {
229
+ $ftpConn=$this->openFtpConnection();
230
+ if ($ftpConn===false)
231
+ {
232
+ // Could not connect to host
233
+ $this->log('Could not connect to Translation Portal Server.', Zend_Log::ERR);
234
+ $rv['statusmessage']="<span class='et_error'>".$this->__("Could not connect to server. Could be a temporary error or firewall problem. You could also check for a new module version.")."</span>";
235
+ }
236
+ else
237
+ {
238
+ // Login:
239
+ if (@ftp_login($ftpConn,$ftp_username,$ftp_password))
240
+ {
241
+ $this->log('Translation Portal Server successfully connected.', Zend_Log::INFO);
242
+ $rv['statusmessage']="<span class='et_ok'>".$this->__("Translation portal successfully connected!")."</span>";
243
+ $rv['ok']=true;
244
+ }
245
+ else
246
+ {
247
+ $this->log('Could not login to Translation Portal Server.', Zend_Log::ERR);
248
+ $rv['statusmessage']="<span class='et_error'>".$this->__("There seems to be a problem with your login data. Please check username and password!")."</span>";
249
+ }
250
+
251
+ ftp_close($ftpConn);
252
+ }
253
+ }
254
+
255
+ return $rv;
256
+ }
257
+
258
+ public function getSetting($key, $defaultValue="")
259
+ {
260
+ $dbres = Mage::getSingleton('core/resource');
261
+ $dbr= $dbres->getConnection('core_read');
262
+
263
+ $tbl_eurotext_config = Mage::getSingleton('core/resource')->getTableName('eurotext_config');
264
+
265
+ $result=$dbr->fetchOne("SELECT config_value FROM `".$tbl_eurotext_config."` WHERE config_key=?;",array($key));
266
+ if ($result!==false)
267
+ {
268
+ return $result;
269
+ }
270
+ else
271
+ {
272
+ return $defaultValue;
273
+ }
274
+ }
275
+
276
+ public function ajaxexportAction_CollectLangfiles($project)
277
+ {
278
+ $dbres = Mage::getSingleton('core/resource');
279
+ $dbw=$dbres->getConnection('core_write');
280
+
281
+ $project_id = $this->sanitize($project['id']);
282
+
283
+ $dbw->query("UPDATE `".$this->getTableName("eurotext_csv")."` SET deleteflag=1 WHERE project_id=".$project_id);
284
+
285
+ $base_dir=Mage::getBaseDir('app');
286
+ $this->ajaxexportAction_CollectLangfiles2($dbw,$project,$base_dir);
287
+
288
+ $dbw->query("DELETE FROM `".$this->getTableName("eurotext_csv")."` WHERE deleteflag=1 AND project_id=".$project_id);
289
+ }
290
+
291
+ public function ajaxexportAction_CollectLangfilesLocaleCSV($dbw,$project,$locale,$localeFolder)
292
+ {
293
+
294
+ $project_id = intval($this->sanitize($project['id']));
295
+
296
+ $pathNames=scandir($localeFolder);
297
+ foreach($pathNames as $path)
298
+ {
299
+ $full_path=$localeFolder.DS.$path;
300
+ if ((is_file($full_path)) && (stripos($path,".csv")!==false))
301
+ {
302
+ $base_dir=Mage::getBaseDir('app');
303
+ $filename=substr($full_path,strlen($base_dir));
304
+
305
+ $line_hash=sha1($project_id."_".$filename);
306
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_csv')."` (line_hash,project_id,filename,locale_dst) VALUES (?,?,?,?);",array($line_hash,$project_id,$filename,$locale));
307
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_csv')."` SET deleteflag=0 WHERE line_hash=?;",array($line_hash));
308
+ }
309
+ }
310
+ }
311
+
312
+ public function ajaxexportAction_CollectLangfilesLocale($dbw,$project,$localeFolder)
313
+ {
314
+ $pathNames=scandir($localeFolder);
315
+ foreach($pathNames as $path)
316
+ {
317
+ $full_path=$localeFolder.DS.$path;
318
+ if (($path==".") || ($path==".."))
319
+ {
320
+ // Ignore
321
+ }
322
+ elseif (is_dir($full_path))
323
+ {
324
+ $this->ajaxexportAction_CollectLangfilesLocaleCSV($dbw,$project,$path,$full_path);
325
+ }
326
+ }
327
+ }
328
+
329
+ public function ajaxexportAction_CollectLangfiles2($dbw,$project,$curdir)
330
+ {
331
+ $pathNames=scandir($curdir);
332
+ foreach($pathNames as $path)
333
+ {
334
+ $full_path=$curdir.DS.$path;
335
+ if (($path==".") || ($path==".."))
336
+ {
337
+ // Ignore
338
+ }
339
+ elseif (is_dir($full_path))
340
+ {
341
+ if ($path=="locale")
342
+ {
343
+ $this->ajaxexportAction_CollectLangfilesLocale($dbw,$project,$full_path);
344
+ }
345
+ else
346
+ {
347
+ $this->ajaxexportAction_CollectLangfiles2($dbw,$project,$full_path);
348
+ }
349
+ }
350
+ }
351
+ }
352
+
353
+ public function ajaxexportAction_CollectEMailTemplates2($helper, $dbw, $project, $locale, $localeFolder)
354
+ {
355
+ $project_id = intval($this->sanitize($project['id']));
356
+
357
+ $templates=$helper->getDirectoryContent($localeFolder,true,true,false);
358
+ foreach($templates as $template)
359
+ {
360
+ $filename=$template['full_path'];
361
+ if ($helper->endsWith(strtolower($filename),".html"))
362
+ {
363
+ $short_filename=substr($filename,strlen($localeFolder));
364
+ $short_filename=substr($filename,strlen($localeFolder));
365
+
366
+ $file_hash=sha1($project_id."_".$locale."_".$short_filename);
367
+
368
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_emailtemplates')."` (file_hash,project_id,filename,locale_dst) VALUES (?,?,?,?);",array($file_hash,$project_id,$short_filename,$locale));
369
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_emailtemplates')."` SET deleteflag=0 WHERE file_hash=?;",array($file_hash));
370
+ }
371
+ }
372
+ }
373
+
374
+ public function ajaxexportAction_CollectEMailTemplates($project)
375
+ {
376
+ $dbres = Mage::getSingleton('core/resource');
377
+ $dbw=$dbres->getConnection('core_write');
378
+
379
+ $project_id = intval($this->sanitize($project['id']));
380
+
381
+ $dbw->query("UPDATE `".$this->getTableName("eurotext_emailtemplates")."` SET deleteflag=1 WHERE project_id=".$project_id);
382
+
383
+ $baseLocaleFolder=Mage::getBaseDir('locale');
384
+
385
+ $helper_et=Mage::helper('eurotext_translationmanager/eurotext');
386
+ $localeFolders=$helper_et->getDirectoryContent($baseLocaleFolder,false,false,true);
387
+ foreach($localeFolders as $localeFolder)
388
+ {
389
+ $templateFolder=$localeFolder['full_path'].DS."template";
390
+ if (is_dir($templateFolder))
391
+ {
392
+ $this->ajaxexportAction_CollectEMailTemplates2($helper_et, $dbw,$project, $localeFolder['name'], $templateFolder);
393
+ }
394
+ }
395
+
396
+ $dbw->query("DELETE FROM `".$this->getTableName("eurotext_emailtemplates")."` WHERE deleteflag=1 AND project_id=".$project_id);
397
+ }
398
+
399
+ private function getTableName($tblName)
400
+ {
401
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
402
+ }
403
+
404
+ // Gets an array with the ids of all children of $cat_id
405
+ public function getAllSubCategories($cat_id, $recurse=true)
406
+ {
407
+ $rv=array();
408
+
409
+ $_cat=Mage::getModel("catalog/category")->load($cat_id);
410
+
411
+ $subcat_ids_str=$_cat->getChildren();
412
+
413
+ // Free memory:
414
+ unset($_cat);
415
+ $_cat=null;
416
+
417
+ $subcat_ids=array();
418
+ if (strlen($subcat_ids_str)>0)
419
+ {
420
+ $subcat_ids=explode(",",$subcat_ids_str);
421
+
422
+ $rv=array_merge($rv,$subcat_ids);
423
+
424
+ if ($recurse)
425
+ {
426
+ foreach($subcat_ids as $subcat_id)
427
+ {
428
+ $rv=array_merge($rv,Mage::helper('eurotext_translationmanager')->getAllSubCategories($subcat_id,true));
429
+ }
430
+ }
431
+ }
432
+
433
+ return $rv;
434
+ }
435
+
436
+ // Generates a tree (recurses to all cats which ids are in open_catids
437
+ public function getCategoryTree($cat_id, $open_catids)
438
+ {
439
+ $node=array();
440
+
441
+ $_cat=Mage::getModel("catalog/category")->load($cat_id);
442
+
443
+ $node['childs']=array();
444
+ $node['hasChildren']=false;
445
+
446
+ if ($cat_id==1) // Pseudo-Category
447
+ {
448
+ $node['id']=1;
449
+ $node['name']=$this->__("(All products)"); // ." (".$cat_id.")";
450
+ }
451
+ else
452
+ {
453
+ $node['id']=$cat_id;
454
+ $node['name']=$_cat->getName(); // ." (".$cat_id.")";
455
+ }
456
+
457
+ // get children ids (comma-seperated list as string)
458
+ $subcat_ids_str=$_cat->getChildren();
459
+
460
+ // Free memory:
461
+ unset($_cat);
462
+ $_cat=null;
463
+
464
+ $subcat_ids=array();
465
+ if (strlen($subcat_ids_str)>0)
466
+ {
467
+ $subcat_ids=explode(",",$subcat_ids_str);
468
+ }
469
+
470
+ if (count($subcat_ids)>0)
471
+ {
472
+ $node['hasChildren']=true;
473
+
474
+ if (in_array($cat_id, $open_catids))
475
+ {
476
+ // Load sub categories:
477
+ foreach($subcat_ids as $subcat_id)
478
+ {
479
+ $childItem=$this->getCategoryTree($subcat_id,$open_catids);
480
+ array_push($node['childs'],$childItem);
481
+ }
482
+ }
483
+ }
484
+
485
+ return $node;
486
+ }
487
+
488
+ public static function getCategoryProducts($dbr,$cat_id)
489
+ {
490
+ $sql_categoryfilter="";
491
+
492
+ $search_catids=Mage::helper('eurotext_translationmanager')->getAllSubCategories($cat_id); // Get the IDs of all children (direct+indirect)
493
+ array_push($search_catids,$cat_id); // add selected category to list
494
+
495
+ // Filter to products which are assigned to any category in $search_catids:
496
+ $sql_categoryfilter=" AND e.entity_id IN (SELECT cat.product_id FROM catalog_category_product cat WHERE cat.category_id IN (".implode(",",$search_catids)."))";
497
+
498
+ $sql1="SELECT e.entity_id FROM `".Mage::getSingleton('core/resource')->getTableName('catalog_product_entity')."` e WHERE (1=1)".$sql_categoryfilter;
499
+ $res1=$dbr->fetchAll($sql1);
500
+
501
+ $rv=array();
502
+ for($i=0; $i<count($res1); $i++)
503
+ {
504
+ array_push($rv,$res1[$i]['entity_id']);
505
+ }
506
+
507
+ return $rv;
508
+ }
509
+
510
+ // Returns 'checked', 'indeterminate' or 'unchecked' for a given category id
511
+ // checked: all products of the category are selected for translation
512
+ // indeterminate: some products of the category are selected for translation
513
+ // unchecked: no products are selected for translation
514
+ public function getTreeNodeTranslationState($dbr,$cat_id, $project_id)
515
+ {
516
+ $sql_categoryfilter="";
517
+
518
+ $search_catids=Mage::helper('eurotext_translationmanager')->getAllSubCategories($cat_id); // Get the IDs of all children (direct+indirect)
519
+ array_push($search_catids,$cat_id); // add selected category to list
520
+
521
+ // Filter to products which are assigned to any category in $search_catids:
522
+ if ($cat_id>1)
523
+ {
524
+ $sql_categoryfilter=" AND e.entity_id IN (SELECT cat.product_id FROM catalog_category_product cat WHERE cat.category_id IN (".implode(",",$search_catids)."))";
525
+ }
526
+
527
+ $sql1="SELECT COUNT(e.product_id) cnt FROM eurotext_project_products e WHERE e.project_id=".intval($project_id)." AND e.product_id IN (SELECT e.entity_id FROM `".Mage::getSingleton('core/resource')->getTableName('catalog_product_entity')."` e WHERE (1=1)".$sql_categoryfilter.")";
528
+ //echo $sql1;
529
+ //die($sql1);
530
+ $res1=$dbr->fetchAll($sql1);
531
+ $translatedProducts=$res1[0]['cnt'];
532
+
533
+ $sql2="SELECT COUNT(e.entity_id) cnt FROM `".Mage::getSingleton('core/resource')->getTableName('catalog_product_entity')."` e WHERE (1=1)".$sql_categoryfilter;
534
+ //echo $sql2;
535
+ $res2=$dbr->fetchAll($sql2);
536
+ $allProducts=$res2[0]['cnt'];
537
+
538
+ if ($allProducts==0) // empty category
539
+ {
540
+ return "unchecked";
541
+ }
542
+ else if ($translatedProducts==$allProducts)
543
+ {
544
+ return "checked";
545
+ }
546
+ else if ($translatedProducts==0)
547
+ {
548
+ return "unchecked";
549
+ }
550
+ else if ($translatedProducts<$allProducts)
551
+ {
552
+ return "indeterminate";
553
+ }
554
+ else
555
+ {
556
+ return "unchecked";
557
+ }
558
+ }
559
+
560
+ public function sanitize($value){
561
+ $value = strip_tags($value);
562
+ $value = trim($value);
563
+ $value = htmlspecialchars($value);
564
+ return $value;
565
+ }
566
+ }
app/code/community/Eurotext/TranslationManager/Helper/Eurotext.php ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // allow utf8-detection: öäü€
4
+
5
+ // Helper Functions
6
+ class Eurotext_TranslationManager_Helper_Eurotext extends Mage_Core_Helper_Abstract
7
+ {
8
+ // Like realpath, but also works on non-existing paths
9
+ // Source: http://de2.php.net/manual/de/function.realpath.php#84012
10
+ public function getAbsolutePath($path)
11
+ {
12
+ $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
13
+ $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
14
+ $absolutes = array();
15
+ foreach ($parts as $part) {
16
+ if ('.' == $part) continue;
17
+ if ('..' == $part) {
18
+ array_pop($absolutes);
19
+ } else {
20
+ $absolutes[] = $part;
21
+ }
22
+ }
23
+ return DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $absolutes);
24
+ }
25
+
26
+ // Extract directory-part of path
27
+ // "/foo/bar/filename.dat" => "/foo/bar"
28
+ // "/foo/bar/" => "/foo/bar"
29
+ // "/foo/bar" => "/foo"
30
+ public function GetDirectoryFromPath($path)
31
+ {
32
+ $rpath=$this->getAbsolutePath($path);
33
+ $lastSep=strrpos($rpath,DIRECTORY_SEPARATOR);
34
+ if ($lastSep>=0)
35
+ {
36
+ return substr($rpath,0,$lastSep);
37
+ }
38
+ else
39
+ {
40
+ return $path;
41
+ }
42
+ }
43
+
44
+ public function GetFilenameFromPath($path)
45
+ {
46
+ $tmpPath=str_replace("\\","/",$path);
47
+ $lastPos=strrpos($tmpPath,"/");
48
+ if ($lastPos===false)
49
+ {
50
+ return $path;
51
+ }
52
+
53
+ $rv=substr($tmpPath,$lastPos+1);
54
+
55
+ return trim($rv);
56
+ }
57
+
58
+ // Converts $str to a string that is safe to use in filenames
59
+ // (Replaces unsafe characters to '-')
60
+ public function GetFilenameSafeString($str)
61
+ {
62
+ $strTmp=trim(strtolower($str));
63
+ $allowedChars=array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','-','_','.','0','1','2','3','4','5','6','7','8','9');
64
+ $str2="";
65
+ for($i=0; $i<strlen($strTmp); $i++)
66
+ {
67
+ if (in_array($strTmp[$i],$allowedChars))
68
+ {
69
+ $str2.=$strTmp[$i];
70
+ }
71
+ else
72
+ {
73
+ $str2.="-";
74
+ }
75
+ }
76
+
77
+ while(stripos($str2,"--")!==false)
78
+ {
79
+ $str2=str_replace("--","-",$str2);
80
+ }
81
+
82
+ while(stripos($str2,"..")!==false)
83
+ {
84
+ $str2=str_replace("..",".",$str2);
85
+ }
86
+
87
+ return $str2;
88
+ }
89
+
90
+ // Tests if $haystack ends with $needle
91
+ public function endsWith($haystack, $needle)
92
+ {
93
+ if (strlen($haystack)>=strlen($needle))
94
+ {
95
+ $lastOfHaystack=substr($haystack,-strlen($needle));
96
+ return ($lastOfHaystack==$needle);
97
+ }
98
+
99
+ return false;
100
+ }
101
+
102
+ public function removeTrailingDirectorySeparator($path)
103
+ {
104
+ if ($this->endsWith($path,DIRECTORY_SEPARATOR))
105
+ {
106
+ return substr($path,0,strlen($path)-1);
107
+ }
108
+
109
+ return $path;
110
+ }
111
+
112
+ public function compareFileItems($a, $b)
113
+ {
114
+ return strcmp($a['full_path'],$b['full_path']);
115
+ }
116
+
117
+ public function getDirectoryContent($directory, $recurse=false, $enumerateFiles=true, $enumerateDirs=true, $sortResult=true)
118
+ {
119
+ $result=array();
120
+
121
+ $dirpath=$this->removeTrailingDirectorySeparator($directory);
122
+
123
+ $dir=opendir($dirpath);
124
+ if ($dir)
125
+ {
126
+ while (false !== ($item = readdir($dir)))
127
+ {
128
+ $full_path=$dirpath.DIRECTORY_SEPARATOR.$item;
129
+ if ( ($item==".") || ($item=="..") )
130
+ {
131
+ // Skip
132
+ }
133
+ elseif ( (is_file($full_path)) && ($enumerateFiles) )
134
+ {
135
+ $rvItem=array();
136
+ $rvItem['full_path']=$full_path;
137
+ $rvItem['name']=$item;
138
+ $rvItem['type']="file";
139
+ array_push($result,$rvItem);
140
+ }
141
+ elseif (is_dir($full_path))
142
+ {
143
+ if ($enumerateDirs)
144
+ {
145
+ $rvItem=array();
146
+ $rvItem['full_path']=$full_path;
147
+ $rvItem['name']=$item;
148
+ $rvItem['type']="dir";
149
+ array_push($result,$rvItem);
150
+ }
151
+
152
+ if ($recurse)
153
+ {
154
+ $subresult=$this->getDirectoryContent($full_path,$recurse,$enumerateFiles, $enumerateDirs, false);
155
+ $result=array_merge($result,$subresult);
156
+ }
157
+ }
158
+ }
159
+
160
+ closedir($dir);
161
+ }
162
+
163
+ if ($sortResult)
164
+ {
165
+ usort($result, array($this, "compareFileItems"));
166
+ }
167
+
168
+ return $result;
169
+ }
170
+
171
+ public function extractZip($zipFile, $dstDirectory)
172
+ {
173
+ $dirpath=$this->removeTrailingDirectorySeparator($dstDirectory);
174
+
175
+ $zip = new ZipArchive;
176
+ if ($zip->open($zipFile)!==true)
177
+ {
178
+ return false;
179
+ }
180
+
181
+ $rv=true;
182
+
183
+ if (!$zip->extractTo($dirpath))
184
+ {
185
+ $rv=false;
186
+ }
187
+
188
+ $zip->close();
189
+
190
+ return $rv;
191
+ }
192
+
193
+ public function zipFolder($directory, $zipFile, $comment="")
194
+ {
195
+
196
+ $helper = Mage::helper('eurotext_translationmanager');
197
+ if (!class_exists("ZipArchive"))
198
+ {
199
+ $helper->log('ZipArchive Class does not exist!', Zend_Log::CRIT);
200
+ return false;
201
+ }
202
+
203
+ $dirpath=$this->removeTrailingDirectorySeparator($directory);
204
+ $items=$this->getDirectoryContent($dirpath, true);
205
+
206
+ $mode = file_exists($zipFile) ? ZipArchive::OVERWRITE : ZipArchive::CREATE;
207
+
208
+ $zip = new ZipArchive;
209
+ $zipOpeningResult = $zip->open($zipFile,$mode);
210
+
211
+ if ($zipOpeningResult !== true)
212
+ {
213
+ $helper->log('Could not open ZIP Archive at '.$zipFile.'!', Zend_Log::CRIT);
214
+ $helper->log('Reason: '.print_r($zipOpeningResult, 1), Zend_log::CRIT);
215
+ return false;
216
+ }
217
+
218
+ if ($comment!="")
219
+ {
220
+ $zip->setArchiveComment($comment);
221
+ }
222
+
223
+ foreach($items as $item)
224
+ {
225
+ if ($item['full_path']==$zipFile)
226
+ {
227
+ // Skip
228
+ }
229
+ else
230
+ {
231
+ $inZipPath=substr($item['full_path'],strlen($dirpath)+1);
232
+
233
+ if ($item['type']=="dir")
234
+ {
235
+ $zip->addEmptyDir($inZipPath);
236
+ }
237
+ else
238
+ {
239
+ $zip->addFile($item['full_path'],$inZipPath);
240
+ }
241
+ }
242
+ }
243
+
244
+ $zip->close();
245
+ return true;
246
+ }
247
+ }
app/code/community/Eurotext/TranslationManager/Model/.DS_Store ADDED
Binary file
app/code/community/Eurotext/TranslationManager/Model/Resource/Setup.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Model_Resource_Setup extends Mage_Core_Model_Resource_Setup
4
+ {
5
+
6
+ }
app/code/community/Eurotext/TranslationManager/controllers/.DS_Store ADDED
Binary file
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/.DS_Store ADDED
Binary file
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/.DS_Store ADDED
Binary file
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/HelpController.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Adminhtml_Eurotext_Translationmanager_HelpController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ protected function _isAllowed()
6
+ {
7
+ return Mage::getSingleton('admin/session')
8
+ ->isAllowed('eurotext_translationmanager/help');
9
+ }
10
+
11
+
12
+ public function indexAction()
13
+ {
14
+ $this->loadLayout();
15
+ $this->renderLayout();
16
+ }
17
+ }
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/ProjectsController.php ADDED
@@ -0,0 +1,3240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Adminhtml_Eurotext_Translationmanager_ProjectsController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ /** @var Eurotext_TranslationManager_Helper_Data */
6
+ protected $helper = false;
7
+ /** @var Eurotext_TranslationManager_Helper_Eurotext */
8
+ protected $eurotextHelper = false;
9
+
10
+ protected function _isAllowed()
11
+ {
12
+ return Mage::getSingleton('admin/session')
13
+ ->isAllowed('eurotext_translationmanager/export');
14
+ }
15
+
16
+ protected function _construct(){
17
+ $this->helper = Mage::helper('eurotext_translationmanager');
18
+ $this->eurotextHelper = Mage::helper('eurotext_translationmanager/eurotext');
19
+ }
20
+
21
+ protected function getHelper(){
22
+ return $this->helper;
23
+ }
24
+
25
+ protected function getEurotextHelper(){
26
+ return $this->eurotextHelper;
27
+ }
28
+
29
+ public function getModuleVersion()
30
+ {
31
+ return (string) Mage::getConfig()->getNode('modules/Eurotext_TranslationManager/version');
32
+ }
33
+
34
+ protected function _initAction()
35
+ {
36
+ $this->loadLayout()
37
+ // Make the active menu match the menu config nodes (without 'children' inbetween)
38
+ ->_setActiveMenu('eurotext_translationmanager_projects/export')
39
+ ->_title('Eurotext')->_title($this->__('Export'))
40
+ ->_addBreadcrumb('Eurotext', $this->__('Export'));
41
+
42
+ return $this;
43
+ }
44
+
45
+ public function indexAction()
46
+ {
47
+ $this->loadLayout();
48
+ $this->renderLayout();
49
+ }
50
+
51
+ public function addprojectAction()
52
+ {
53
+ // Create new project:
54
+ $tableName = Mage::getSingleton('core/resource')->getTableName('eurotext_translationmanager/project');
55
+
56
+ $dbres = Mage::getSingleton('core/resource');
57
+ $dbw=$dbres->getConnection('core_write');
58
+
59
+ $create_id=rand();
60
+
61
+ $dbw->query("INSERT INTO `".$tableName."` (create_id,project_name,last_update) VALUES (?,?,NOW());",array($create_id,$this->__("New Project")));
62
+ $project_id=$dbw->fetchOne("SELECT id FROM `".$tableName."` WHERE create_id=".$create_id);
63
+
64
+ $url=Mage::helper('adminhtml')->getUrl('*/*/index', array('id' => $project_id));
65
+ $this->_redirectUrl($url);
66
+ }
67
+
68
+ public function deleteAction()
69
+ {
70
+ $project_id=intval(Mage::app()->getRequest()->getParam('id',-1));
71
+
72
+ $dbres = Mage::getSingleton('core/resource');
73
+ $dbw=$dbres->getConnection('core_write');
74
+
75
+ $dbw->query("DELETE FROM `".Mage::getSingleton('core/resource')->getTableName('eurotext_project')."` WHERE id=?;",array($project_id));
76
+ $dbw->query("DELETE FROM `".Mage::getSingleton('core/resource')->getTableName('eurotext_csv')."` WHERE project_id=?;",array($project_id));
77
+ $dbw->query("DELETE FROM `".Mage::getSingleton('core/resource')->getTableName('eurotext_csv_data')."` WHERE project_id=?;",array($project_id));
78
+ $dbw->query("DELETE FROM `".Mage::getSingleton('core/resource')->getTableName('eurotext_emailtemplates')."` WHERE project_id=?;",array($project_id));
79
+ $dbw->query("DELETE FROM `".Mage::getSingleton('core/resource')->getTableName('eurotext_import')."` WHERE project_id=?;",array($project_id));
80
+ $dbw->query("DELETE FROM `".Mage::getSingleton('core/resource')->getTableName('eurotext_project_categories')."` WHERE project_id=?;",array($project_id));
81
+ $dbw->query("DELETE FROM `".Mage::getSingleton('core/resource')->getTableName('eurotext_project_cmspages')."` WHERE project_id=?;",array($project_id));
82
+ $dbw->query("DELETE FROM `".Mage::getSingleton('core/resource')->getTableName('eurotext_project_cmsblocks')."` WHERE project_id=?;",array($project_id));
83
+ $dbw->query("DELETE FROM `".Mage::getSingleton('core/resource')->getTableName('eurotext_project_products')."` WHERE project_id=?;",array($project_id));
84
+
85
+ $url=Mage::helper('adminhtml')->getUrl('*/*/index', array('id' => '-1'));
86
+ $this->_redirectUrl($url);
87
+ }
88
+
89
+ public function resetAction()
90
+ {
91
+ $project_id=intval(Mage::app()->getRequest()->getParam('id',-1));
92
+
93
+ $dbres = Mage::getSingleton('core/resource');
94
+ $dbw=$dbres->getConnection('core_write');
95
+
96
+ $dbw->query("UPDATE `".Mage::getSingleton('core/resource')->getTableName('eurotext_project')."` SET project_status=(project_status-1) WHERE project_status>0 AND id=?;",array($project_id));
97
+
98
+ $url=Mage::helper('adminhtml')->getUrl('*/*/index', array('id' => $project_id));
99
+ $this->_redirectUrl($url);
100
+ }
101
+
102
+ public function saveprojectAction()
103
+ {
104
+ $this->loadLayout('adminhtml_eurotext_translationmanager_ajax');
105
+ $block = $this->getLayout()->getBlock('et.tm.response.ajax');
106
+
107
+ $block->setStatusCode('ok');
108
+ $block->setStatusMsg($this->__("Saved Project!"));
109
+
110
+ // Update project:
111
+ try
112
+ {
113
+ $project_id=intval(Mage::app()->getRequest()->getParam('id',-1));
114
+ $project_name=$this->getHelper()->sanitize(Mage::app()->getRequest()->getParam('project_name'));
115
+ $storeview_src=intval(Mage::app()->getRequest()->getParam('storeview_src',-1));
116
+ $storeview_dst=intval(Mage::app()->getRequest()->getParam('storeview_dst',-1));
117
+ $export_seo=intval(Mage::app()->getRequest()->getParam('export_seo',1));
118
+ $export_attributes=intval(Mage::app()->getRequest()->getParam('export_attributes',1));
119
+ $export_urlkeys=intval(Mage::app()->getRequest()->getParam('export_urlkeys',0));
120
+ $productmode=intval(Mage::app()->getRequest()->getParam('productmode',0));
121
+ $categorymode=intval(Mage::app()->getRequest()->getParam('categorymode',0));
122
+ $cmsmode=intval(Mage::app()->getRequest()->getParam('cmsmode',0));
123
+ $langfilesmode=intval(Mage::app()->getRequest()->getParam('langfilesmode',0));
124
+ $templatemode=intval(Mage::app()->getRequest()->getParam('templatemode',0));
125
+
126
+ $tableName = Mage::getSingleton('core/resource')->getTableName('eurotext_translationmanager/project');
127
+
128
+ $dbres = Mage::getSingleton('core/resource');
129
+ $dbw=$dbres->getConnection('core_write');
130
+
131
+ if (strlen($project_name)==0)
132
+ {
133
+ $block->setStatusCode('error');
134
+ $block->setStatusMsg($this->__("Please enter project name!"));
135
+ }
136
+
137
+ if ($storeview_src<0)
138
+ {
139
+ $block->setStatusCode('error');
140
+ $block->setStatusMsg($this->__("Please select Source Storeview!"));
141
+ }
142
+ elseif ($storeview_dst<0)
143
+ {
144
+ $block->setStatusCode('error');
145
+ $block->setStatusMsg($this->__("Please select Destination Storeview!"));
146
+ }
147
+ elseif ($storeview_dst==$storeview_src)
148
+ {
149
+ $block->setStatusCode('error');
150
+ $block->setStatusMsg($this->__("Please select different Storeviews for Source and Destination!"));
151
+ }
152
+
153
+ if($block->getStatusCode() == 'ok'){
154
+ $dbw->query("UPDATE `".$tableName."` SET create_id=-1, last_update=NOW(), project_name=?, storeview_src=?, storeview_dst=?, export_seo=?, export_attributes=?, productmode=?, categorymode=?, cmsmode=?, langfilesmode=?, templatemode=?, export_urlkeys=? WHERE id=".$project_id, array($project_name,$storeview_src,$storeview_dst,$export_seo,$export_attributes,$productmode,$categorymode,$cmsmode,$langfilesmode,$templatemode,$export_urlkeys));
155
+ }
156
+ }
157
+ catch(Exception $e)
158
+ {
159
+ $block->setStatusCode('error');
160
+ $block->setStatusMsg($e->getMessage());
161
+ }
162
+
163
+ $this->renderLayout();
164
+
165
+ }
166
+
167
+ private function getProject($project_id)
168
+ {
169
+ $tableName = Mage::getSingleton('core/resource')->getTableName('eurotext_translationmanager/project');
170
+
171
+ $dbres = Mage::getSingleton('core/resource');
172
+ $dbr=$dbres->getConnection('core_read');
173
+
174
+ $projects=$dbr->fetchAll("SELECT * FROM `".$tableName."` WHERE id=".$project_id);
175
+
176
+ $project=$projects[0];
177
+
178
+ // storeview_src_locale:
179
+ $project['storeview_src_locale']="en_US";
180
+ if ($project['storeview_src']>=0)
181
+ {
182
+ $project['storeview_src_locale']=Mage::getStoreConfig('general/locale/code', $project['storeview_src']);
183
+ }
184
+
185
+ // storeview_dst_locale:
186
+ $project['storeview_dst_locale']="en_US";
187
+ if ($project['storeview_dst']>=0)
188
+ {
189
+ $project['storeview_dst_locale']=Mage::getStoreConfig('general/locale/code', $project['storeview_dst']);
190
+ }
191
+
192
+ return $project;
193
+ }
194
+
195
+ private function getTableName($tblName)
196
+ {
197
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
198
+ }
199
+
200
+ public function ajaxexportAction_CollectLangfiles($project)
201
+ {
202
+ $helper=$this->getHelper();
203
+ $helper->ajaxexportAction_CollectLangfiles($project);
204
+ }
205
+
206
+ public function ajaxexportAction_ImportLangfiles($project,$offset)
207
+ {
208
+
209
+ $dbres = Mage::getSingleton('core/resource');
210
+ $dbw=$dbres->getConnection('core_write');
211
+ $dbr=$dbres->getConnection('core_read');
212
+
213
+ $rv=array();
214
+ $rv['offset']=-1;
215
+ $rv['status_msg']=$this->__("Finished loading language files.");
216
+
217
+ $addWhere="";
218
+ if ($project['langfilesmode']==0)
219
+ {
220
+ $addWhere=" AND translate_flag=1";
221
+ }
222
+
223
+ if ($offset==0)
224
+ {
225
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_csv_data')."` WHERE project_id=?;",array($project['id']));
226
+ }
227
+
228
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('eurotext_csv')."` WHERE project_id=".$project['id'].$addWhere));
229
+
230
+ $rows=$dbr->fetchAll("SELECT * FROM `".$this->getTableName('eurotext_csv')."` WHERE project_id=".$project['id'].$addWhere." ORDER BY filename ASC, line_hash ASC LIMIT $offset,1");
231
+ if (count($rows) > 0)
232
+ {
233
+ $row=$rows[0];
234
+
235
+ $filename=$row['filename'];
236
+ $locale=$row['locale_dst'];
237
+ $rv['status_msg']=$this->__("Analysing")." ".($offset+1)."/".$cnt.": '".$filename."'";
238
+
239
+ $full_path=Mage::getBaseDir('app').$filename;
240
+
241
+ // Read CSV:
242
+ $fp=fopen($full_path,"r");
243
+ $lineIndex=1;
244
+ do
245
+ {
246
+ $fields=fgetcsv($fp,0,",","\"");
247
+ if ($fields!==false)
248
+ {
249
+ if (count($fields)==2)
250
+ {
251
+ $line_hash=sha1($project['id']."_".$filename."_".$fields[0]);
252
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_csv_data')."` (line_hash,project_id,filename,locale_dst,text_src,text_src_hash,text_dst,csvline) VALUES (?,?,?,?,?,?,?,?);",array($line_hash,$project['id'],$filename,$locale,$fields[0],sha1($fields[0]),$fields[1],$lineIndex));
253
+ }
254
+ }
255
+
256
+ $lineIndex++;
257
+ }
258
+ while($fields!==false);
259
+ fclose($fp);
260
+
261
+ $rv['offset']=$offset+1;
262
+ }
263
+
264
+ return $rv;
265
+ }
266
+
267
+ public function getImportXMLPath($project)
268
+ {
269
+ return $this->getProjectXMLPath($project,"import");
270
+ }
271
+
272
+ public function getExportXMLPath($project)
273
+ {
274
+ return $this->getProjectXMLPath($project,"export");
275
+ }
276
+
277
+ private function getProjectXMLPath($project,$folder)
278
+ {
279
+ $dir=Mage::getBaseDir('var');
280
+ $dir.=DS."eurotext";
281
+ if (!is_dir($dir))
282
+ {
283
+ if(!mkdir($dir)){
284
+ $this->getHelper()->log('Working directory could not be created in '.Mage::getBaseDir('var'), Zend_Log::CRIT);
285
+ throw new Magento_Exception('Eurotext working directory could not be created.');
286
+ }
287
+ }
288
+
289
+ $htaccessFilename=$dir.DS.".htaccess";
290
+ if (!is_file($htaccessFilename))
291
+ {
292
+ file_put_contents($htaccessFilename,"# Eurotext Export folder\r\nOrder Deny,Allow\r\nDeny From All");
293
+ }
294
+
295
+ $dir.=DS."projects";
296
+ if (!is_dir($dir))
297
+ {
298
+ mkdir($dir);
299
+ }
300
+
301
+ $dir.=DS.$project['id'];
302
+ if (!is_dir($dir))
303
+ {
304
+ mkdir($dir);
305
+ }
306
+
307
+ $dir.=DS.$folder;
308
+ if (!is_dir($dir))
309
+ {
310
+ mkdir($dir);
311
+ }
312
+
313
+ return $dir;
314
+ }
315
+
316
+ private function getTempDir()
317
+ {
318
+ $dir=Mage::getBaseDir('tmp');
319
+ $dir.=DS."eurotext";
320
+ if (!is_dir($dir))
321
+ {
322
+ if(!mkdir($dir)){
323
+ $this->getHelper()->log('Temporary directory could not be created in '.Mage::getBaseDir('var'), Zend_Log::CRIT);
324
+ throw new Magento_Exception('Eurotext temporary directory could not be created.');
325
+ }
326
+ }
327
+ $htaccessFilename=$dir.DS.".htaccess";
328
+ if (!is_file($htaccessFilename))
329
+ {
330
+ file_put_contents($htaccessFilename,"# Eurotext Temp folder\r\nOrder Deny,Allow\r\nDeny From All");
331
+ }
332
+
333
+ return $dir;
334
+ }
335
+
336
+ public function ajaxexportAction_BuildLangXML($project, $offset)
337
+ {
338
+ $rv=array();
339
+ $rv["status_msg"]=$this->__("Generating language files...");
340
+ $rv["offset"]=$offset;
341
+ $rv["step"]=$this->STEP_BUILD_LANGXML;
342
+
343
+ $dbres = Mage::getSingleton('core/resource');
344
+ $dbw=$dbres->getConnection('core_write');
345
+ $dbr=$dbres->getConnection('core_read');
346
+
347
+ $xmlDir=$this->getExportXMLPath($project);
348
+ $xmlDir.=DS."framework";
349
+ if (!is_dir($xmlDir))
350
+ {
351
+ mkdir($xmlDir);
352
+ }
353
+
354
+ $cnt=count($dbr->fetchAll("SELECT filename FROM `".$this->getTableName('eurotext_csv')."` WHERE project_id=".$project['id']." GROUP BY filename"));
355
+
356
+ $rows=$dbr->fetchAll("SELECT filename FROM `".$this->getTableName('eurotext_csv')."` WHERE project_id=".$project['id']." GROUP BY filename ORDER BY filename ASC LIMIT ".$offset.",1");
357
+ if (count($rows)>0)
358
+ {
359
+ $row=$rows[0];
360
+
361
+ $en_filename=$row['filename'];
362
+ $dst_filename=str_replace("en_US",$project['storeview_dst_locale'],$en_filename);
363
+ $src_filename=str_replace("en_US",$project['storeview_src_locale'],$en_filename);
364
+
365
+ $rv["status_msg"]=$this->__("Analyzing")." ".($offset+1)."/".$cnt." '".$en_filename."'";
366
+
367
+ $short_filename=$this->getEurotextHelper()->GetFilenameFromPath($en_filename);
368
+ $short_filename_safe=$this->getEurotextHelper()->GetFilenameSafeString($short_filename);
369
+ $short_filename_safe=str_replace(".csv","",$short_filename_safe);
370
+
371
+ $xml_filename=$xmlDir.DS.$short_filename_safe."_".sha1($en_filename).".xml";
372
+
373
+ $doc = new DOMDocument('1.0', 'UTF-8');
374
+ $doc->formatOutput = true;
375
+ $translation = $doc->createElement( "translation" );
376
+
377
+ $translationAttr1=$doc->createAttribute("src_filename");
378
+ $translationAttr1->value=$src_filename;
379
+ $translation->appendChild($translationAttr1);
380
+
381
+ $translationAttr2=$doc->createAttribute("dst_filename");
382
+ $translationAttr2->value=$dst_filename;
383
+ $translation->appendChild($translationAttr2);
384
+
385
+ $doc->appendChild($translation);
386
+
387
+ $itemCount=0;
388
+
389
+ $tbl=$this->getTableName('eurotext_csv_data');
390
+
391
+ $sql="SELECT text_src FROM `".$tbl."` WHERE project_id=? AND (filename=? OR filename=? OR filename=?) GROUP BY text_src";
392
+ $srcLines=$dbr->fetchAll($sql,array($project['id'],$src_filename,$dst_filename,$en_filename));
393
+ foreach($srcLines as $srcLine)
394
+ {
395
+ $txt_en=$srcLine['text_src'];
396
+ $txt_src="";
397
+ $txt_dst="";
398
+
399
+ // Src-Sprache lesen, falls vorhanden:
400
+ if ($project['storeview_src_locale']=="en_US")
401
+ {
402
+ $txt_src=$txt_en;
403
+ }
404
+ else
405
+ {
406
+ $tmpData=$dbr->fetchAll("SELECT text_dst, csvline FROM `".$tbl."` WHERE project_id=? AND text_src=? AND filename=? AND locale_dst=?",array($project['id'],$txt_en,$src_filename,$project['storeview_src_locale']));
407
+ foreach($tmpData as $tmpLine)
408
+ {
409
+ $txt_src=$tmpLine['text_dst'];
410
+ }
411
+ }
412
+
413
+ // Dst-Sprache lesen, falls vorhanden:
414
+ $tmpData=$dbr->fetchAll("SELECT text_dst, csvline FROM `".$tbl."` WHERE project_id=? AND text_src=? AND filename=? AND locale_dst=?",array($project['id'],$txt_en,$dst_filename,$project['storeview_dst_locale']));
415
+ foreach($tmpData as $tmpLine)
416
+ {
417
+ $txt_dst=$tmpLine['text_dst'];
418
+ }
419
+
420
+ if (true)
421
+ {
422
+ // Needs to be translated:
423
+ $itemCount++;
424
+
425
+ // Add comment as separator:
426
+ $comment=$doc->createComment("Line ".$itemCount);
427
+ $translation->appendChild($comment);
428
+
429
+ {
430
+ $lineExport=$doc->createElement("line".$itemCount);
431
+ $translation->appendChild($lineExport);
432
+
433
+ // context
434
+ {
435
+ $line=$doc->createElement("line-context");
436
+ {
437
+ $lineIndex=$doc->createAttribute("num");
438
+ $lineIndex->value=$itemCount;
439
+ $line->appendChild($lineIndex);
440
+ }
441
+ {
442
+ $srchashAttribute=$doc->createAttribute("srchash");
443
+ $srchashAttribute->value=sha1($txt_en);
444
+ $line->appendChild($srchashAttribute);
445
+ }
446
+ {
447
+ $contextAttribute=$doc->createAttribute("context");
448
+ $contextAttribute->value="yes";
449
+ $line->appendChild($contextAttribute);
450
+ }
451
+
452
+ {
453
+ $contextAttribute=$doc->createAttribute("locale");
454
+ $contextAttribute->value="en_US";
455
+ $line->appendChild($contextAttribute);
456
+ }
457
+
458
+ $this->appendTextChild($line,$txt_en);
459
+ $lineExport->appendChild($line);
460
+ }
461
+
462
+ // context
463
+ {
464
+ $line=$doc->createElement("line-context");
465
+ {
466
+ $lineIndex=$doc->createAttribute("num");
467
+ $lineIndex->value=$itemCount;
468
+ $line->appendChild($lineIndex);
469
+ }
470
+ {
471
+ $srchashAttribute=$doc->createAttribute("srchash");
472
+ $srchashAttribute->value=sha1($txt_en);
473
+ $line->appendChild($srchashAttribute);
474
+ }
475
+ {
476
+ $contextAttribute=$doc->createAttribute("context");
477
+ $contextAttribute->value="yes";
478
+ $line->appendChild($contextAttribute);
479
+ }
480
+ {
481
+ $contextAttribute=$doc->createAttribute("locale");
482
+ $contextAttribute->value=$project['storeview_dst_locale'];
483
+ $line->appendChild($contextAttribute);
484
+ }
485
+ $this->appendTextChild($line,$txt_dst);
486
+ $lineExport->appendChild($line);
487
+ }
488
+
489
+ // content
490
+ {
491
+
492
+ $line=$doc->createElement("line");
493
+ {
494
+ $lineIndex=$doc->createAttribute("num");
495
+ $lineIndex->value=$itemCount;
496
+ $line->appendChild($lineIndex);
497
+ }
498
+ {
499
+ $srchashAttribute=$doc->createAttribute("srchash");
500
+ $srchashAttribute->value=sha1($txt_en);
501
+ $line->appendChild($srchashAttribute);
502
+ }
503
+ {
504
+ $contextAttribute=$doc->createAttribute("locale-src");
505
+ $contextAttribute->value=$project['storeview_src_locale'];
506
+ $line->appendChild($contextAttribute);
507
+ }
508
+ {
509
+ $contextAttribute=$doc->createAttribute("locale-dst");
510
+ $contextAttribute->value=$project['storeview_dst_locale'];
511
+ $line->appendChild($contextAttribute);
512
+ }
513
+ $this->appendTextChild($line,$txt_src);
514
+ $lineExport->appendChild($line);
515
+ }
516
+ }
517
+ }
518
+ }
519
+
520
+ if ($itemCount>0)
521
+ {
522
+ $doc->save($xml_filename);
523
+ }
524
+
525
+ $rv['offset']=($offset+1);
526
+ }
527
+ else
528
+ {
529
+ $rv['step']=$this->STEP_COLLECT_PRODUCTS;
530
+ $rv['offset']=0;
531
+ }
532
+
533
+ return $rv;
534
+ }
535
+
536
+ private function appendTextChild($xmlNode, $textContent)
537
+ {
538
+ $doc=$xmlNode->ownerDocument;
539
+
540
+ $needsCDATA = true; preg_match('/[<>&]/', $textContent);
541
+
542
+ if ($needsCDATA)
543
+ {
544
+ $xmlNode->appendChild($doc->createCDATASection($textContent));
545
+ }
546
+ else
547
+ {
548
+ $xmlNode->appendChild($doc->createTextNode($textContent));
549
+ }
550
+ }
551
+
552
+ public function ajaxexportAction_CollectProducts($project, $offset)
553
+ {
554
+ $rv=array();
555
+ $rv["status_msg"]=$this->__("Checking Product")."...";
556
+ $rv["offset"]=$offset;
557
+ $rv["step"]=$this->STEP_COLLECT_PRODUCTS;
558
+
559
+ $helper=$this->getHelper();
560
+ $maxItems=intval($helper->getSetting("et_products_per_file",20));
561
+ if ($maxItems<1) { $maxItems=20; }
562
+
563
+ $dbres = Mage::getSingleton('core/resource');
564
+ $dbw=$dbres->getConnection('core_write');
565
+ $dbr=$dbres->getConnection('core_read');
566
+
567
+ $product_id=-1;
568
+
569
+ $manualSelected=false;
570
+
571
+ if ($project['productmode']==1)
572
+ {
573
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('catalog_product_entity')."`"));
574
+
575
+ $rows=$dbr->fetchAll("SELECT entity_id, sku FROM `".$this->getTableName('catalog_product_entity')."` ORDER BY sku ASC, entity_id ASC LIMIT $offset,$maxItems");
576
+ }
577
+ else
578
+ {
579
+ $manualSelected=true;
580
+
581
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('eurotext_project_products')."` WHERE project_id=?",array($project['id'])));
582
+
583
+ $rows=$dbr->fetchAll("SELECT e.entity_id, e.sku FROM `".$this->getTableName('catalog_product_entity')."` e, `".$this->getTableName('eurotext_project_products')."` p WHERE (e.entity_id=p.product_id) AND project_id=".$project['id']." ORDER BY e.sku ASC, e.entity_id ASC LIMIT $offset,$maxItems");
584
+ }
585
+
586
+ $doc = new DOMDocument('1.0', 'UTF-8');
587
+ $doc->formatOutput = true;
588
+
589
+ $nodeArticles=$doc->createElement("articles");
590
+ $doc->appendChild($nodeArticles);
591
+
592
+ $itemsFound=false;
593
+ foreach($rows as $row)
594
+ {
595
+ $itemsFound=true;
596
+
597
+ $product_id=intval($row['entity_id']);
598
+ $sku=$row['sku'];
599
+
600
+ $rv["status_msg"]="(".($offset+1)." / ".$cnt.") ".$this->__("Checking Product")." '".$sku."'...";
601
+
602
+ // Compare languages:
603
+ $productSrc = Mage::getModel('catalog/product')->setStoreId($project['storeview_src'])->load($product_id);
604
+ $productDst = Mage::getModel('catalog/product')->setStoreId($project['storeview_dst'])->load($product_id);
605
+
606
+ $hasChangedProperty=false;
607
+
608
+ $nodeArticle = $doc->createElement("article");
609
+
610
+ {
611
+ $nodeArticleId = $doc->createElement("Id");
612
+ $nodeArticleId->appendChild($doc->createTextNode($product_id));
613
+ $nodeArticle->appendChild($nodeArticleId);
614
+ }
615
+
616
+ {
617
+ $nodeArticleUrl = $doc->createElement("Url");
618
+ $this->appendTextChild($nodeArticleUrl,$productSrc->getProductUrl());
619
+ $nodeArticle->appendChild($nodeArticleUrl);
620
+ }
621
+
622
+ // Name:
623
+ if ($productSrc->getName()!="")
624
+ {
625
+ if ( ($productSrc->getName()==$productDst->getName()) || ($productDst->getName()=="") || ($manualSelected))
626
+ {
627
+ $hasChangedProperty=true;
628
+
629
+ $item=$doc->createElement("Title");
630
+ $this->appendTextChild($item,$productSrc->getName());
631
+ $nodeArticle->appendChild($item);
632
+ }
633
+ }
634
+
635
+ // Description:
636
+ if ($productSrc->getDescription()!="")
637
+ {
638
+ if ( ($productSrc->getDescription()==$productDst->getDescription()) || ($productDst->getDescription()=="") || ($manualSelected))
639
+ {
640
+ $hasChangedProperty=true;
641
+
642
+ $item=$doc->createElement("Longdesc");
643
+ $this->appendTextChild($item,$productSrc->getDescription());
644
+ $nodeArticle->appendChild($item);
645
+ }
646
+ }
647
+
648
+ // Short-Description:
649
+ if ($productSrc->getShortDescription()!="")
650
+ {
651
+ if ( ($productSrc->getShortDescription()==$productDst->getShortDescription()) || ($productDst->getShortDescription()=="") || ($manualSelected))
652
+ {
653
+ $hasChangedProperty=true;
654
+
655
+ $item=$doc->createElement("Shortdesc");
656
+ $this->appendTextChild($item,$productSrc->getShortDescription());
657
+ $nodeArticle->appendChild($item);
658
+ }
659
+ }
660
+
661
+ // Custom Attributes through config xml
662
+ if($custom_attributes = Mage::helper('eurotext_translationmanager')->getCustomProductAttributesForExport()){
663
+ $hasChangedProperty = true;
664
+ $nodeCustomAttributes = $doc->createElement('custom_attributes');
665
+ // Custom Attribute Mapping is not yet used nor implemented
666
+ foreach($custom_attributes as $custom_attribute => $custom_attribute_mapping){
667
+ $item = $doc->createElement((string) $custom_attribute);
668
+ $this->appendTextChild($item, (string) $productSrc->getData($custom_attribute));
669
+ $nodeCustomAttributes->appendChild($item);
670
+ }
671
+ $nodeArticle->appendChild($nodeCustomAttributes);
672
+ }
673
+
674
+ // Images:
675
+ {
676
+ $images_orig=array();
677
+ $images_orig_url=array();
678
+ $images_dst_disabled=array();
679
+ $images_dst_position=array();
680
+
681
+ $images_dstLabel=array();
682
+
683
+ $rows=$dbr->fetchAll("SELECT g.value_id, g.value, v.store_id, v.label, v.disabled, position FROM `".$this->getTableName('catalog_product_entity_media_gallery')."` g, `".$this->getTableName('catalog_product_entity_media_gallery_value')."` v WHERE (v.value_id=g.value_id) AND g.entity_id=".$product_id." ORDER BY v.store_id ASC");
684
+ foreach($rows as $row)
685
+ {
686
+ $img_store_id=$row['store_id'];
687
+ $img_value_id=$row['value_id'];
688
+ $img_value=$row['value'];
689
+ $img_label=$row['label'];
690
+ $img_disabled=$row['disabled'];
691
+ $img_position=$row['position'];
692
+
693
+ $images_dst_disabled[$img_value_id]=$img_disabled;
694
+ $images_dst_position[$img_value_id]=$img_position;
695
+
696
+ if (($img_store_id==0) || ($img_store_id==$project['storeview_src']))
697
+ {
698
+ $images_orig[$img_value_id]=$img_label;
699
+ $images_orig_url[$img_value_id]=$img_value;
700
+ }
701
+
702
+ if ($img_store_id==$project['storeview_dst'])
703
+ {
704
+ $images_dstLabel[$img_value_id]=$img_label;
705
+ }
706
+ }
707
+
708
+ $hasImages=false;
709
+ $imagesNode=$doc->createElement("Images");
710
+
711
+ foreach($images_orig as $img_value_id => $img_label)
712
+ {
713
+ if(!array_key_exists($img_value_id,$images_dstLabel))
714
+ {
715
+ $images_dstLabel[$img_value_id]="";
716
+ }
717
+
718
+ $needsUpdate=$manualSelected;
719
+ if (strlen(trim($images_dstLabel[$img_value_id]))==0)
720
+ {
721
+ $needsUpdate=true;
722
+ }
723
+
724
+ if ($needsUpdate)
725
+ {
726
+ if (strlen(trim($img_label))>0)
727
+ {
728
+ $hasImages=true;
729
+ $hasChangedProperty=true;
730
+
731
+ $imageNode=$doc->createElement("Image");
732
+ $imagesNode->appendChild($imageNode);
733
+
734
+ $imageNodeId=$doc->createAttribute("value_id");
735
+ $imageNodeId->value=$img_value_id;
736
+ $imageNode->appendChild($imageNodeId);
737
+
738
+ $imageNodePosition=$doc->createAttribute("position");
739
+ $imageNodePosition->value=$images_dst_position[$img_value_id];
740
+ $imageNode->appendChild($imageNodePosition);
741
+
742
+ $imageNodeDisabled=$doc->createAttribute("disabled");
743
+ $imageNodeDisabled->value=$images_dst_disabled[$img_value_id];
744
+ $imageNode->appendChild($imageNodeDisabled);
745
+
746
+ // URL:
747
+ $img_url=Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA)."catalog/product".$images_orig_url[$img_value_id];
748
+
749
+ $imageNodeUrl=$doc->createElement("Url");
750
+ $this->appendTextChild($imageNodeUrl,$img_url);
751
+ $imageNode->appendChild($imageNodeUrl);
752
+
753
+ // Label:
754
+ $labelNode=$doc->createElement("Label");
755
+ $this->appendTextChild($labelNode,$img_label);
756
+ $imageNode->appendChild($labelNode);
757
+ }
758
+ }
759
+ }
760
+
761
+ if ($hasImages)
762
+ {
763
+ $nodeArticle->appendChild($imagesNode);
764
+ }
765
+ }
766
+
767
+ // Options:
768
+ {
769
+ $optionsNode=$doc->createElement("Options");
770
+ $hasOptions=false;
771
+
772
+ $_options=$productSrc->getOptions();
773
+ if ($_options!==false)
774
+ {
775
+ foreach($_options as $_option)
776
+ /** @var Mage_Catalog_Model_Product_Option $_option */
777
+ {
778
+ $optionNode=$doc->createElement("Option");
779
+
780
+ $optionNodeIdAttribute=$doc->createAttribute("Id");
781
+ $optionNodeIdAttribute->value=$_option->getId();
782
+ $optionNode->appendChild($optionNodeIdAttribute);
783
+
784
+ $_optionTitle=$_option->getTitle();
785
+ if ($_optionTitle!==null)
786
+ {
787
+ $optionsNode->appendChild($optionNode);
788
+ $hasOptions=true;
789
+
790
+ $optionsNodeTitle=$doc->createElement("Title");
791
+ $this->appendTextChild($optionsNodeTitle,$_optionTitle);
792
+ $optionNode->appendChild($optionsNodeTitle);
793
+
794
+ // Values:
795
+ $hasValues=false;
796
+ $_values=$_option->getValues();
797
+ if ($_values!==null)
798
+ {
799
+ $valuesNode=$doc->createElement("Values");
800
+
801
+ foreach($_values as $_value)
802
+ {
803
+ $_valueTitle=$_value->getTitle();
804
+ if ($_valueTitle!==null)
805
+ {
806
+ $_valueId=$_value->getId();
807
+
808
+ $valueNode=$doc->createElement("Value");
809
+
810
+ $valueNodeIdAttribute=$doc->createAttribute("Id");
811
+ $valueNodeIdAttribute->value=$_valueId;
812
+ $valueNode->appendChild($valueNodeIdAttribute);
813
+
814
+ $valueNodeTitle=$doc->createElement("Title");
815
+ $this->appendTextChild($valueNodeTitle,$_valueTitle);
816
+ $valueNode->appendChild($valueNodeTitle);
817
+
818
+ $valuesNode->appendChild($valueNode);
819
+
820
+ $hasValues=true;
821
+ }
822
+ }
823
+ }
824
+ if ($hasValues)
825
+ {
826
+ $optionNode->appendChild($valuesNode);
827
+ }
828
+ }
829
+ }
830
+ }
831
+
832
+ if ($hasOptions)
833
+ {
834
+ $nodeArticle->appendChild($optionsNode);
835
+ }
836
+ }
837
+
838
+ if ($project['export_urlkeys']=="1")
839
+ {
840
+ // URL-Key:
841
+ if ($productSrc->getUrlKey()!="")
842
+ {
843
+ if ( ($productSrc->getUrlKey()==$productDst->getUrlKey()) || ($productDst->getUrlKey()=="") || ($manualSelected))
844
+ {
845
+ $hasChangedProperty=true;
846
+
847
+ $item=$doc->createElement("UrlKey");
848
+ $this->appendTextChild($item,$productSrc->getUrlKey());
849
+ $nodeArticle->appendChild($item);
850
+ }
851
+ }
852
+ }
853
+
854
+ if ($project['export_seo']=="1")
855
+ {
856
+ // Meta-Title:
857
+ if ($productSrc->getMetaTitle()!="")
858
+ {
859
+ if ( ($productSrc->getMetaTitle()==$productDst->getMetaTitle()) || ($productDst->getMetaTitle()=="") || ($manualSelected))
860
+ {
861
+ $hasChangedProperty=true;
862
+
863
+ $item=$doc->createElement("SeoTitle");
864
+ $this->appendTextChild($item,$productSrc->getMetaTitle());
865
+ $nodeArticle->appendChild($item);
866
+ }
867
+ }
868
+
869
+ // Meta-Description:
870
+ if ($productSrc->getMetaDescription()!="")
871
+ {
872
+ if ( ($productSrc->getMetaDescription()==$productDst->getMetaDescription()) || ($productDst->getMetaDescription()=="") || ($manualSelected))
873
+ {
874
+ $hasChangedProperty=true;
875
+
876
+ $item=$doc->createElement("SeoDescription");
877
+ $this->appendTextChild($item,$productSrc->getMetaDescription());
878
+ $nodeArticle->appendChild($item);
879
+ }
880
+ }
881
+
882
+ // Meta-Keywords:
883
+ if ($productSrc->getMetaKeyword()!="")
884
+ {
885
+ if ( ($productSrc->getMetaKeyword()==$productDst->getMetaKeyword()) || ($productDst->getMetaKeyword()=="") || ($manualSelected))
886
+ {
887
+ $hasChangedProperty=true;
888
+
889
+ $item=$doc->createElement("SeoKeywords");
890
+ $this->appendTextChild($item,$productSrc->getMetaKeyword());
891
+ $nodeArticle->appendChild($item);
892
+ }
893
+ }
894
+ }
895
+
896
+ if ($hasChangedProperty)
897
+ {
898
+ $nodeArticles->appendChild($nodeArticle);
899
+ }
900
+
901
+ $rv["offset"]=($offset+$maxItems);
902
+ }
903
+
904
+ if ($itemsFound)
905
+ {
906
+ $xmlDir=$this->getExportXMLPath($project);
907
+ $xmlDir.=DS."articles";
908
+ if (!is_dir($xmlDir))
909
+ {
910
+ mkdir($xmlDir);
911
+ }
912
+
913
+ $fileNum=($offset / $maxItems)+1;
914
+ $fileNum=intval($fileNum);
915
+
916
+ $xmlFilename=$xmlDir.DS."a".$fileNum.".xml";
917
+
918
+ $doc->save($xmlFilename);
919
+ }
920
+ else
921
+ {
922
+ // no further product found, go to next step:
923
+ $rv['step']=$this->STEP_COLLECT_CATEGORIES;
924
+ $rv["offset"]=0;
925
+ }
926
+
927
+ return $rv;
928
+ }
929
+
930
+ public function ajaxexportAction_CollectCMSPages($project, $offset)
931
+ {
932
+ $rv=array();
933
+ $rv["status_msg"]=$this->__("Checking CMS-Pages")."...";
934
+ $rv["offset"]=$offset;
935
+ $rv["step"]=$this->STEP_COLLECT_CMSPAGES;
936
+
937
+ $dbres = Mage::getSingleton('core/resource');
938
+ $dbw=$dbres->getConnection('core_write');
939
+ $dbr=$dbres->getConnection('core_read');
940
+
941
+ $page_id=-1;
942
+
943
+ $manualSelected=false;
944
+
945
+ if ($project['cmsmode']==1)
946
+ {
947
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('cms_page')."` p, `".$this->getTableName('cms_page_store')."` s WHERE (s.page_id=p.page_id) AND ((s.store_id=0) OR (s.store_id=".$project['storeview_src']."))"));
948
+
949
+ // Pages:
950
+ $rows=$dbr->fetchAll("SELECT p.page_id, p.title FROM `".$this->getTableName('cms_page')."` p, `".$this->getTableName('cms_page_store')."` s WHERE (s.page_id=p.page_id) AND ((s.store_id=0) OR (s.store_id=".$project['storeview_src'].")) ORDER BY page_id ASC LIMIT $offset,1");
951
+ if (count($rows)>0)
952
+ {
953
+ $page_id=intval($rows[0]['page_id']);
954
+ $title=$rows[0]['title'];
955
+
956
+ $rv["status_msg"]="(".($offset+1)." / ".$cnt.") ".$this->__("Checking CMS-Page")." '".$title."'...";
957
+ }
958
+ }
959
+ else
960
+ {
961
+ $manualSelected=true;
962
+
963
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('eurotext_project_cmspages')."` WHERE project_id=".$project['id']));
964
+
965
+ $rows=$dbr->fetchAll("SELECT p.page_id, p.title FROM `".$this->getTableName('cms_page')."` p, `".$this->getTableName('eurotext_project_cmspages')."` e WHERE (e.page_id=p.page_id) AND project_id=".$project['id']." ORDER BY p.page_id ASC LIMIT $offset,1");
966
+ if (count($rows)>0)
967
+ {
968
+ $page_id=intval($rows[0]['page_id']);
969
+ $title=$rows[0]['title'];
970
+
971
+ $rv["status_msg"]="(".($offset+1)." / ".$cnt.") ".$this->__("CMS-Page")." '".$title."'...";
972
+ }
973
+ }
974
+
975
+ if ($page_id>0)
976
+ {
977
+ // Compare languages:
978
+ $pageSrc = Mage::getModel('cms/page')->load($page_id);
979
+ $pageDst = Mage::getModel('cms/page')->load($page_id);
980
+
981
+ $page_id_dst=-1;
982
+
983
+ $identifier=$pageSrc->getIdentifier();
984
+ // Find matching page:
985
+ $matchingPages=$dbr->fetchAll("SELECT p.page_id FROM `".$this->getTableName('cms_page')."` p, `".$this->getTableName('cms_page_store')."` s WHERE (p.page_id=s.page_id) AND UPPER(p.identifier)=? AND s.store_id>0 AND s.store_id=? ORDER BY p.page_id ASC",array($identifier,$project['storeview_dst']));
986
+ if (count($matchingPages)>0)
987
+ {
988
+ $matchingPage=$matchingPages[0];
989
+ $page_id_dst=$matchingPage['page_id'];
990
+
991
+ $pageDst = Mage::getModel('cms/page')->load($page_id_dst);
992
+ }
993
+
994
+ $hasChangedProperty=false;
995
+
996
+ $doc = new DOMDocument('1.0', 'UTF-8');
997
+ $doc->formatOutput = true;
998
+ $cmsSites=$doc->createElement("cms-sites");
999
+ $doc->appendChild($cmsSites);
1000
+ $cmsSite = $doc->createElement("cms-site");
1001
+ $cmsSites->appendChild($cmsSite);
1002
+
1003
+ // ID:
1004
+ {
1005
+ $item=$doc->createElement("Id");
1006
+ $item->appendChild($doc->createTextNode($page_id));
1007
+ $cmsSite->appendChild($item);
1008
+ }
1009
+
1010
+ // Storeview_src:
1011
+ {
1012
+ $item=$doc->createElement("StoreviewSrc");
1013
+ $item->appendChild($doc->createTextNode($project['storeview_src']));
1014
+ $cmsSite->appendChild($item);
1015
+ }
1016
+
1017
+ // Storeview_dst:
1018
+ {
1019
+ $item=$doc->createElement("StoreviewDst");
1020
+ $item->appendChild($doc->createTextNode($project['storeview_dst']));
1021
+ $cmsSite->appendChild($item);
1022
+ }
1023
+
1024
+ // page_id_dst:
1025
+ {
1026
+ $item=$doc->createElement("PageIdDst");
1027
+ $item->appendChild($doc->createTextNode($page_id_dst));
1028
+ $cmsSite->appendChild($item);
1029
+ }
1030
+
1031
+ // Title:
1032
+ if ($pageSrc->getTitle()!="")
1033
+ {
1034
+ if ( ($pageSrc->getTitle()==$pageDst->getTitle()) || ($pageDst->getTitle()=="") || ($manualSelected))
1035
+ {
1036
+ $hasChangedProperty=true;
1037
+
1038
+ $item=$doc->createElement("Title");
1039
+ $this->appendTextChild($item,$pageSrc->getTitle());
1040
+ $cmsSite->appendChild($item);
1041
+ }
1042
+ }
1043
+
1044
+ // Content:
1045
+ if ($pageSrc->getContent()!="")
1046
+ {
1047
+ if ( ($pageSrc->getContent()==$pageDst->getContent()) || ($pageDst->getContent()=="") || ($manualSelected))
1048
+ {
1049
+ $hasChangedProperty=true;
1050
+
1051
+ $item=$doc->createElement("Content");
1052
+ $this->appendTextChild($item,$pageSrc->getContent());
1053
+ $cmsSite->appendChild($item);
1054
+ }
1055
+ }
1056
+
1057
+ // Content-Heading:
1058
+ if ($pageSrc->getContentHeading()!="")
1059
+ {
1060
+ if ( ($pageSrc->getContentHeading()==$pageDst->getContentHeading()) || ($pageDst->getContentHeading()=="") || ($manualSelected))
1061
+ {
1062
+ $hasChangedProperty=true;
1063
+
1064
+ $item=$doc->createElement("ContentHeading");
1065
+ $this->appendTextChild($item,$pageSrc->getContentHeading());
1066
+ $cmsSite->appendChild($item);
1067
+ }
1068
+ }
1069
+
1070
+ if ($project['export_seo']=="1")
1071
+ {
1072
+ // Meta-Keywords
1073
+ if ($pageSrc->getMetaKeywords()!="")
1074
+ {
1075
+ if ( ($pageSrc->getMetaKeywords()==$pageDst->getMetaKeywords()) || ($pageDst->getMetaKeywords()=="") || ($manualSelected))
1076
+ {
1077
+ $hasChangedProperty=true;
1078
+
1079
+ $item=$doc->createElement("SeoKeywords");
1080
+ $this->appendTextChild($item,$pageSrc->getMetaKeywords());
1081
+ $cmsSite->appendChild($item);
1082
+ }
1083
+ }
1084
+
1085
+ // Meta-Description:
1086
+ if ($pageSrc->getMetaDescription()!="")
1087
+ {
1088
+ if ( ($pageSrc->getMetaDescription()==$pageDst->getMetaDescription()) || ($pageDst->getMetaDescription()=="") || ($manualSelected))
1089
+ {
1090
+ $hasChangedProperty=true;
1091
+
1092
+ $item=$doc->createElement("SeoDescription");
1093
+ $this->appendTextChild($item,$pageSrc->getMetaDescription());
1094
+ $cmsSite->appendChild($item);
1095
+ }
1096
+ }
1097
+ }
1098
+
1099
+ if ($hasChangedProperty)
1100
+ {
1101
+ $xmlDir=$this->getExportXMLPath($project);
1102
+ $xmlDir.=DS."cms-sites";
1103
+ if (!is_dir($xmlDir))
1104
+ {
1105
+ mkdir($xmlDir);
1106
+ }
1107
+
1108
+ $xmlFilename=$xmlDir.DS."cms-".$this->getEurotextHelper()->GetFilenameSafeString($identifier)."-".$page_id.".xml";
1109
+
1110
+ $doc->save($xmlFilename);
1111
+ }
1112
+
1113
+ $rv["offset"]=($offset+1);
1114
+ }
1115
+ else
1116
+ {
1117
+ // no further cms-page found, go to next step:
1118
+ $rv['step']=$this->STEP_COLLECT_CMSBLOCKS;
1119
+ $rv["offset"]=0;
1120
+ }
1121
+
1122
+ return $rv;
1123
+ }
1124
+
1125
+ public function ajaxexportAction_CollectCMSBlocks($project, $offset)
1126
+ {
1127
+ $rv=array();
1128
+ $rv["status_msg"]=$this->__("Checking CMS-Blocks")."...";
1129
+ $rv["offset"]=$offset;
1130
+ $rv["step"]=$this->STEP_COLLECT_CMSBLOCKS;
1131
+
1132
+ $dbres = Mage::getSingleton('core/resource');
1133
+ $dbw=$dbres->getConnection('core_write');
1134
+ $dbr=$dbres->getConnection('core_read');
1135
+
1136
+ $block_id=-1;
1137
+
1138
+ $manualSelected=false;
1139
+
1140
+ if ($project['cmsmode']==1)
1141
+ {
1142
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('cms_block')."` p, `".$this->getTableName('cms_block_store')."` s WHERE (s.block_id=p.block_id) AND ((s.store_id=0) OR (s.store_id=".$project['storeview_src']."))"));
1143
+
1144
+ // Blocks:
1145
+ $rows=$dbr->fetchAll("SELECT p.block_id, p.title FROM `".$this->getTableName('cms_block')."` p, `".$this->getTableName('cms_block_store')."` s WHERE (s.block_id=p.block_id) AND ((s.store_id=0) OR (s.store_id=".$project['storeview_src'].")) ORDER BY block_id ASC LIMIT $offset,1");
1146
+ if (count($rows)>0)
1147
+ {
1148
+ $block_id=intval($rows[0]['block_id']);
1149
+ $title=$rows[0]['title'];
1150
+
1151
+ $rv["status_msg"]="(".($offset+1)." / ".$cnt.") ".$this->__("Checking CMS-Block")." '".$title."'...";
1152
+ }
1153
+ }
1154
+ else
1155
+ {
1156
+ $manualSelected=true;
1157
+
1158
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('eurotext_project_cmsblocks')."` WHERE project_id=".$project['id']));
1159
+
1160
+ $rows=$dbr->fetchAll("SELECT p.block_id, p.title FROM `".$this->getTableName('cms_block')."` p, `".$this->getTableName('eurotext_project_cmsblocks')."` e WHERE (e.block_id=p.block_id) AND project_id=".$project['id']." ORDER BY p.block_id ASC LIMIT $offset,1");
1161
+ if (count($rows)>0)
1162
+ {
1163
+ $block_id=intval($rows[0]['block_id']);
1164
+ $title=$rows[0]['title'];
1165
+
1166
+ $rv["status_msg"]="(".($offset+1)." / ".$cnt.") ".$this->__("Checking CMS-Block")." '".$title."'...";
1167
+ }
1168
+ }
1169
+
1170
+ if ($block_id>0)
1171
+ {
1172
+ // Compare languages:
1173
+ $blockSrc = Mage::getModel('cms/block')->load($block_id);
1174
+ $blockDst = Mage::getModel('cms/block')->load($block_id);
1175
+
1176
+ $block_id_dst=-1;
1177
+
1178
+ $identifier=$blockSrc->getIdentifier();
1179
+ // Find matching block:
1180
+ $matchingPages=$dbr->fetchAll("SELECT p.block_id FROM `".$this->getTableName('cms_block')."` p, `".$this->getTableName('cms_block_store')."` s WHERE (p.block_id=s.block_id) AND UPPER(p.identifier)=? AND s.store_id>0 AND s.store_id=? ORDER BY p.block_id ASC",array($identifier,$project['storeview_dst']));
1181
+ if (count($matchingPages)>0)
1182
+ {
1183
+ $matchingPage=$matchingPages[0];
1184
+ $block_id_dst=$matchingPage['block_id'];
1185
+
1186
+ $blockDst = Mage::getModel('cms/block')->load($block_id_dst);
1187
+ }
1188
+
1189
+ $hasChangedProperty=false;
1190
+
1191
+ $doc = new DOMDocument('1.0', 'UTF-8');
1192
+ $doc->formatOutput = true;
1193
+ $cmsSites=$doc->createElement("cms-sites");
1194
+ $doc->appendChild($cmsSites);
1195
+ $cmsSite = $doc->createElement("cms-site");
1196
+ $cmsSites->appendChild($cmsSite);
1197
+
1198
+ // ID:
1199
+ {
1200
+ $item=$doc->createElement("Id");
1201
+ $item->appendChild($doc->createTextNode($block_id));
1202
+ $cmsSite->appendChild($item);
1203
+ }
1204
+
1205
+ // Storeview_src:
1206
+ {
1207
+ $item=$doc->createElement("StoreviewSrc");
1208
+ $item->appendChild($doc->createTextNode($project['storeview_src']));
1209
+ $cmsSite->appendChild($item);
1210
+ }
1211
+
1212
+ // Storeview_dst:
1213
+ {
1214
+ $item=$doc->createElement("StoreviewDst");
1215
+ $item->appendChild($doc->createTextNode($project['storeview_dst']));
1216
+ $cmsSite->appendChild($item);
1217
+ }
1218
+
1219
+ // block_id_dst:
1220
+ {
1221
+ $item=$doc->createElement("PageIdDst");
1222
+ $item->appendChild($doc->createTextNode($block_id_dst));
1223
+ $cmsSite->appendChild($item);
1224
+ }
1225
+
1226
+ // Title:
1227
+ if ($blockSrc->getTitle()!="")
1228
+ {
1229
+ if ( ($blockSrc->getTitle()==$blockDst->getTitle()) || ($blockDst->getTitle()=="") || ($manualSelected))
1230
+ {
1231
+ $hasChangedProperty=true;
1232
+
1233
+ $item=$doc->createElement("Title");
1234
+ $this->appendTextChild($item,$blockSrc->getTitle());
1235
+ $cmsSite->appendChild($item);
1236
+ }
1237
+ }
1238
+
1239
+ // Content:
1240
+ if ($blockSrc->getContent()!="")
1241
+ {
1242
+ if ( ($blockSrc->getContent()==$blockDst->getContent()) || ($blockDst->getContent()=="") || ($manualSelected))
1243
+ {
1244
+ $hasChangedProperty=true;
1245
+
1246
+ $item=$doc->createElement("Content");
1247
+ $this->appendTextChild($item,$blockSrc->getContent());
1248
+ $cmsSite->appendChild($item);
1249
+ }
1250
+ }
1251
+
1252
+ // Content-Heading:
1253
+ if ($blockSrc->getContentHeading()!="")
1254
+ {
1255
+ if ( ($blockSrc->getContentHeading()==$blockDst->getContentHeading()) || ($blockDst->getContentHeading()=="") || ($manualSelected))
1256
+ {
1257
+ $hasChangedProperty=true;
1258
+
1259
+ $item=$doc->createElement("ContentHeading");
1260
+ $this->appendTextChild($item,$blockSrc->getContentHeading());
1261
+ $cmsSite->appendChild($item);
1262
+ }
1263
+ }
1264
+
1265
+ if ($project['export_seo']=="1")
1266
+ {
1267
+ // Meta-Keywords
1268
+ if ($blockSrc->getMetaKeywords()!="")
1269
+ {
1270
+ if ( ($blockSrc->getMetaKeywords()==$blockDst->getMetaKeywords()) || ($blockDst->getMetaKeywords()=="") || ($manualSelected))
1271
+ {
1272
+ $hasChangedProperty=true;
1273
+
1274
+ $item=$doc->createElement("SeoKeywords");
1275
+ $this->appendTextChild($item,$blockSrc->getMetaKeywords());
1276
+ $cmsSite->appendChild($item);
1277
+ }
1278
+ }
1279
+
1280
+ // Meta-Description:
1281
+ if ($blockSrc->getMetaDescription()!="")
1282
+ {
1283
+ if ( ($blockSrc->getMetaDescription()==$blockDst->getMetaDescription()) || ($blockDst->getMetaDescription()=="") || ($manualSelected))
1284
+ {
1285
+ $hasChangedProperty=true;
1286
+
1287
+ $item=$doc->createElement("SeoDescription");
1288
+ $this->appendTextChild($item,$blockSrc->getMetaDescription());
1289
+ $cmsSite->appendChild($item);
1290
+ }
1291
+ }
1292
+ }
1293
+
1294
+ if ($hasChangedProperty)
1295
+ {
1296
+ $xmlDir=$this->getExportXMLPath($project);
1297
+ $xmlDir.=DS."cms-sites";
1298
+ if (!is_dir($xmlDir))
1299
+ {
1300
+ mkdir($xmlDir);
1301
+ }
1302
+
1303
+ $xmlFilename=$xmlDir.DS."cmsblock-".$this->getEurotextHelper()->GetFilenameSafeString($identifier)."-".$block_id.".xml";
1304
+
1305
+ $doc->save($xmlFilename);
1306
+ }
1307
+
1308
+ $rv["offset"]=($offset+1);
1309
+ }
1310
+ else
1311
+ {
1312
+ // no further cms-block found, go to next step:
1313
+ $rv['step']=$this->STEP_COLLECT_TEMPLATES;
1314
+ $rv["offset"]=0;
1315
+ }
1316
+
1317
+ return $rv;
1318
+ }
1319
+
1320
+ public function ajaxexportAction_CollectTemplates($project, $offset)
1321
+ {
1322
+ $rv=array();
1323
+ $rv["status_msg"]=$this->__("Checking eMail-Templates")."...";
1324
+ $rv["offset"]=$offset;
1325
+ $rv["step"]=$this->STEP_COLLECT_TEMPLATES;
1326
+
1327
+ $dbres = Mage::getSingleton('core/resource');
1328
+ $dbw=$dbres->getConnection('core_write');
1329
+ $dbr=$dbres->getConnection('core_read');
1330
+
1331
+ $manualSelected=false;
1332
+
1333
+ if ($offset==0)
1334
+ {
1335
+ $helper=$this->getHelper();
1336
+ $helper->ajaxexportAction_CollectEMailTemplates($project);
1337
+ }
1338
+
1339
+ $sql_cnt="SELECT COUNT(*) cnt FROM `".$this->getTableName('eurotext_emailtemplates')."` WHERE deleteflag=0 AND project_id=".$project['id']." AND locale_dst='".$project['storeview_src_locale']."'";
1340
+ $sql_fetch="SELECT * FROM `".$this->getTableName('eurotext_emailtemplates')."` WHERE deleteflag=0 AND project_id=".$project['id']." AND locale_dst='".$project['storeview_src_locale']."'";
1341
+
1342
+ if ($project['templatemode']==0)
1343
+ {
1344
+ $manualSelected=true;
1345
+
1346
+ $sql_cnt.=" AND translate_flag=1";
1347
+ $sql_fetch.=" AND translate_flag=1";
1348
+ }
1349
+
1350
+ $sql_fetch.=" ORDER BY filename ASC, file_hash ASC LIMIT $offset,1";
1351
+
1352
+ $cnt=intval($dbr->fetchOne($sql_cnt));
1353
+
1354
+ $rows=$dbr->fetchAll($sql_fetch);
1355
+ if (count($rows)>0)
1356
+ {
1357
+ $filename=$rows[0]['filename'];
1358
+ $locale_dst=$rows[0]['locale_dst'];
1359
+
1360
+ $rv["status_msg"]="(".($offset+1)." / ".$cnt.") ".$this->__("Checking eMail-Template")." '".$filename."'...";
1361
+
1362
+ $xmlDir=$this->getExportXMLPath($project);
1363
+ $xmlDir.=DS."emailtemplates";
1364
+ if (!is_dir($xmlDir))
1365
+ {
1366
+ mkdir($xmlDir);
1367
+ }
1368
+
1369
+ $baseLocaleFolder=Mage::getBaseDir('locale');
1370
+
1371
+ $src_filename=$baseLocaleFolder.DS.$locale_dst.DS."template".$filename;
1372
+ $dst_filename=$baseLocaleFolder.DS.$project['storeview_dst_locale'].DS."template".$filename;
1373
+
1374
+ if ((!$manualSelected) && (file_exists($dst_filename)))
1375
+ {
1376
+ // "Datei schon übersetzt!"
1377
+ }
1378
+ else
1379
+ {
1380
+ $copyto_filename=$xmlDir.$filename;
1381
+ $copyto_path=$this->getEurotextHelper()->GetDirectoryFromPath($copyto_filename);
1382
+ if (!is_dir($copyto_path))
1383
+ {
1384
+ mkdir($copyto_path,0777,true);
1385
+ }
1386
+
1387
+ copy($src_filename,$copyto_filename);
1388
+ }
1389
+
1390
+ $rv["offset"]=($offset+1);
1391
+ }
1392
+ else
1393
+ {
1394
+ // no further cms-page found, go to next step:
1395
+ $rv['step']=$this->STEP_EXPORT_ATTRIBUTES;
1396
+ $rv["offset"]=0;
1397
+ }
1398
+
1399
+ return $rv;
1400
+ }
1401
+
1402
+ public function getProjectZipFilename($project)
1403
+ {
1404
+ $xmlDir=$this->getExportXMLPath($project);
1405
+
1406
+ if ($project['zip_filename']!="")
1407
+ {
1408
+ return $xmlDir.DS.$project['zip_filename'];
1409
+ }
1410
+
1411
+ $helper=$this->getHelper();
1412
+
1413
+ $shopName=$this->getEurotextHelper()->GetFilenameSafeString($helper->getSetting("register_shopname","shop"));
1414
+ $customer_id = $this->getEurotextHelper()->GetFilenameSafeString($helper->getSetting('eurotext_customerid','no-customerid'));
1415
+ $filename="ET-".$customer_id.'-'.$shopName.'-'.date("Y-m-d_h-i-s").'_'.date_default_timezone_get().".zip";
1416
+
1417
+ $dbres = Mage::getSingleton('core/resource');
1418
+ $dbw=$dbres->getConnection('core_write');
1419
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project')."` SET zip_filename=? WHERE id=?",array($filename,$project['id']));
1420
+
1421
+ $project['zip_filename']=$filename;
1422
+
1423
+ return $xmlDir.DIRECTORY_SEPARATOR."".$filename;
1424
+ }
1425
+
1426
+ public function ajaxexportAction_GenerateControlFile($project)
1427
+ {
1428
+ $xmlDir=$this->getExportXMLPath($project);
1429
+
1430
+ $helper=$this->getHelper();
1431
+ $register_fname=$helper->getSetting("register_fname","");
1432
+ $register_lname=$helper->getSetting("register_lname","");
1433
+ $register_company=$helper->getSetting("register_company","");
1434
+ $register_email=$helper->getSetting("register_email","");
1435
+ $eurotext_customerid=$helper->getSetting("eurotext_customerid","");
1436
+
1437
+ $doc = new DOMDocument('1.0', 'UTF-8');
1438
+ $doc->formatOutput = true;
1439
+
1440
+ // utf8-detection comment:
1441
+ $defComment=$doc->createComment("allow utf8-detection: öäü€");
1442
+ $doc->appendChild($defComment);
1443
+
1444
+ $nodeRequest = $doc->createElement("Request");
1445
+ $doc->appendChild($nodeRequest);
1446
+ {
1447
+ $nodeGeneral = $doc->createElement("General");
1448
+ $nodeRequest->appendChild($nodeGeneral);
1449
+ {
1450
+ $nodeCustomerContact= $doc->createElement("CustomerContact");
1451
+ $nodeCustomerContact->appendChild($doc->createTextNode($register_fname." ".$register_lname));
1452
+ $nodeGeneral->appendChild($nodeCustomerContact);
1453
+
1454
+ $nodeCustomerMail= $doc->createElement("CustomerMail");
1455
+ $nodeCustomerMail->appendChild($doc->createTextNode($register_email));
1456
+ $nodeGeneral->appendChild($nodeCustomerMail);
1457
+
1458
+ $nodeCustomerID_of_Supplier= $doc->createElement("CustomerID_of_Supplier");
1459
+ $nodeCustomerID_of_Supplier->appendChild($doc->createTextNode($eurotext_customerid));
1460
+ $nodeGeneral->appendChild($nodeCustomerID_of_Supplier);
1461
+
1462
+ $nodeProjectName= $doc->createElement("ProjectName");
1463
+ $nodeProjectName->appendChild($doc->createTextNode($project['project_name']));
1464
+ $nodeGeneral->appendChild($nodeProjectName);
1465
+
1466
+ $moduleVersion=$this->getModuleVersion();
1467
+
1468
+ $src_name=Mage::getModel('core/store')->load($project['storeview_src'])->getName();
1469
+ $dst_name=Mage::getModel('core/store')->load($project['storeview_dst'])->getName();
1470
+
1471
+ $commentText="Magento-Project-Id: ".$project['id'].", Module-Version: $moduleVersion\n\n";
1472
+ $commentText.="Src-Storeview: '".$src_name."' (".$project['storeview_src_locale'].")\n";
1473
+ $commentText.="Dst-Storeview: '".$dst_name."' (".$project['storeview_dst_locale'].")\n";
1474
+ $commentText.="Export SEO content: ".($project['export_seo'] ? "Yes" : "No")."\n";
1475
+ $commentText.="Export attributes and attribute options? ".($project['export_attributes'] ? "Yes" : "No")."\n";
1476
+ $commentText.="Export URL keys? ".($project['export_urlkeys'] ? "Yes" : "No");
1477
+
1478
+ $nodeDescription= $doc->createElement("Description");
1479
+ $nodeDescription->appendChild($doc->createTextNode($commentText));
1480
+ $nodeGeneral->appendChild($nodeDescription);
1481
+
1482
+ $nodeDeadline= $doc->createElement("Deadline");
1483
+ $nodeGeneral->appendChild($nodeDeadline);
1484
+
1485
+ $nodeTargetProject= $doc->createElement("TargetProject");
1486
+ $nodeTargetProject->appendChild($doc->createTextNode("Quote"));
1487
+ $nodeGeneral->appendChild($nodeTargetProject);
1488
+ }
1489
+
1490
+ $nodeLanguageCombinations= $doc->createElement("LanguageCombinations");
1491
+ $nodeRequest->appendChild($nodeLanguageCombinations);
1492
+
1493
+ $locale_dst=$project['storeview_dst_locale'];
1494
+ $locale_src=$project['storeview_src_locale'];
1495
+
1496
+ // Shop-Translations:
1497
+ $allFiles=$this->getEurotextHelper()->getDirectoryContent($xmlDir,true,true,false);
1498
+ $frameworkFiles=array();
1499
+ $articlesFiles=array();
1500
+ $attributesFiles=array();
1501
+ $categoriesFiles=array();
1502
+ $cmsFiles=array();
1503
+ $emailtemplatesFiles=array();
1504
+
1505
+ foreach($allFiles as $file)
1506
+ {
1507
+ if (stripos($file['full_path'],DS."framework".DS)!==false)
1508
+ {
1509
+ array_push($frameworkFiles,$file);
1510
+ }
1511
+ elseif (stripos($file['full_path'],DS."articles".DS)!==false)
1512
+ {
1513
+ array_push($articlesFiles,$file);
1514
+ }
1515
+ elseif (stripos($file['full_path'],DS."attributes".DS)!==false)
1516
+ {
1517
+ array_push($attributesFiles,$file);
1518
+ }
1519
+ elseif (stripos($file['full_path'],DS."categories".DS)!==false)
1520
+ {
1521
+ array_push($categoriesFiles,$file);
1522
+ }
1523
+ elseif (stripos($file['full_path'],DS."cms-sites".DS)!==false)
1524
+ {
1525
+ array_push($cmsFiles,$file);
1526
+ }
1527
+ elseif (stripos($file['full_path'],DS."emailtemplates".DS)!==false)
1528
+ {
1529
+ array_push($emailtemplatesFiles,$file);
1530
+ }
1531
+ else
1532
+ {
1533
+ $rv=array();
1534
+ $rv['status_code']="error";
1535
+ $rv['status_msg']="Unknown file: '".$file['full_path']."'";
1536
+
1537
+ Mage::app()->getResponse()->clearBody();
1538
+ Mage::app()->getResponse()->setBody(json_encode($rv));
1539
+ Mage::app()->getResponse()->sendResponse();
1540
+
1541
+ // @todo Hässlicher Magento Hack, da man die Ausgabe anders nicht abbrechen kann
1542
+ exit();
1543
+
1544
+ }
1545
+ }
1546
+
1547
+ $fileGroups=array();
1548
+ array_push($fileGroups,$frameworkFiles);
1549
+ array_push($fileGroups,$articlesFiles);
1550
+ array_push($fileGroups,$attributesFiles);
1551
+ array_push($fileGroups,$categoriesFiles);
1552
+ array_push($fileGroups,$cmsFiles);
1553
+ array_push($fileGroups,$emailtemplatesFiles);
1554
+
1555
+ foreach($fileGroups as $fileGroup)
1556
+ {
1557
+ if (count($fileGroup)>0)
1558
+ {
1559
+ $nodeLanguageCombination=$doc->createElement("LanguageCombination");
1560
+ {
1561
+ $source=$doc->createAttribute("source");
1562
+
1563
+ $eurotext_locale_src=$helper->getLocaleInfoByMagentoLocale($locale_src);
1564
+ if ($eurotext_locale_src['supported']==false)
1565
+ {
1566
+ die("Sprache wird durch Eurotext nicht unterstützt: '".$locale_src."'");
1567
+ }
1568
+
1569
+ $source->value=$eurotext_locale_src['locale_eurotext'];
1570
+ $nodeLanguageCombination->appendChild($source);
1571
+ }
1572
+ {
1573
+ $eurotext_locale_dst=$helper->getLocaleInfoByMagentoLocale($locale_dst);
1574
+ if ($eurotext_locale_dst['supported']==false)
1575
+ {
1576
+ die("Sprache wird durch Eurotext nicht unterstützt: '".$locale_dst."'");
1577
+ }
1578
+
1579
+ $target=$doc->createAttribute("target");
1580
+ $target->value=$eurotext_locale_dst['locale_eurotext'];
1581
+ $nodeLanguageCombination->appendChild($target);
1582
+ }
1583
+ $nodeLanguageCombinations->appendChild($nodeLanguageCombination);
1584
+
1585
+ foreach($fileGroup as $otherFile)
1586
+ {
1587
+ $relative_filename=substr($otherFile['full_path'],strlen($xmlDir)+1);
1588
+ $fsize=filesize($otherFile['full_path']);
1589
+
1590
+ $nodeUploadedFile=$doc->createElement("uploadedFile");
1591
+ $nodeLanguageCombination->appendChild($nodeUploadedFile);
1592
+ {
1593
+ $attrfileName=$doc->createAttribute("fileName");
1594
+ $attrfileName->value=$relative_filename;
1595
+ $nodeUploadedFile->appendChild($attrfileName);
1596
+ }
1597
+ {
1598
+ $attrsize=$doc->createAttribute("size");
1599
+ $attrsize->value=$fsize;
1600
+ $nodeUploadedFile->appendChild($attrsize);
1601
+ }
1602
+ }
1603
+ }
1604
+ }
1605
+ }
1606
+
1607
+ $xmlFilename=$xmlDir.DS."control.xml";
1608
+ $doc->save($xmlFilename);
1609
+ }
1610
+
1611
+ public function ajaxexportAction_CompressFiles($project)
1612
+ {
1613
+ $xmlDir=$this->getExportXMLPath($project);
1614
+ $genZip=$this->getEurotextHelper()->zipFolder($xmlDir,$this->getProjectZipFilename($project),"Created by Eurotext Magento Module");
1615
+ }
1616
+
1617
+ public function ajaxexportAction_TransmitArchive($project)
1618
+ {
1619
+ $zipFile=$this->getProjectZipFilename($project);
1620
+ $helper=$this->getHelper();
1621
+ $fallback_filename=Mage::getBaseDir('export').DS.$project['zip_filename'];
1622
+ $ftp_upload_successful = false;
1623
+
1624
+ $ftp_username=$helper->getSetting("eurotext_username","");
1625
+ $ftp_password=Mage::helper('core')->decrypt($helper->getSetting("eurotext_password",""));
1626
+
1627
+ $testResult=$helper->testFtpConnection();
1628
+
1629
+ if ($testResult['ok']!=true)
1630
+ {
1631
+ die("Archive could not be transmitted. Please use the debug info in var/log/eurotext_fatal.log");
1632
+ }
1633
+ else {
1634
+ if($ftpConn = $helper->openFtpConnection()) {
1635
+ if (@ftp_login($ftpConn, $ftp_username, $ftp_password)) {
1636
+ // Switch to Passive-Mode:
1637
+ ftp_pasv($ftpConn, true);
1638
+
1639
+ ftp_chdir($ftpConn, "/");
1640
+
1641
+ // Delete old files with same name
1642
+ @ftp_delete($ftpConn, $project['zip_filename']);
1643
+ @ftp_delete($ftpConn, $project['zip_filename'] . ".uploading");
1644
+
1645
+ // Upload with .uploading extension
1646
+ if(ftp_put($ftpConn, $project['zip_filename'] . ".uploading", $zipFile, FTP_BINARY)) {
1647
+ $helper->log('File was successfully uploaded to ' . $project['zip_filename'].'.uploading', Zend_Log::INFO);
1648
+ }
1649
+
1650
+ // Remove .uploading extension
1651
+ if (ftp_rename($ftpConn, $project['zip_filename'] . ".uploading", $project['zip_filename'])) {
1652
+ $ftp_upload_successful = true;
1653
+ $helper->log('File was successfully renamed to ' . $project['zip_filename'], Zend_Log::INFO);
1654
+ }
1655
+ } else {
1656
+ $helper->log('Could not login to Translation Portal Server.', Zend_Log::ERR);
1657
+ }
1658
+
1659
+ ftp_close($ftpConn);
1660
+ }
1661
+ }
1662
+
1663
+ if(false === $ftp_upload_successful || true === $helper->getDebugMode())
1664
+ {
1665
+ if(false === copy($zipFile,$fallback_filename)) {
1666
+ $helper->log('Could not copy the project data file to '.$fallback_filename, Zend_Log::ERR);
1667
+ }
1668
+ }
1669
+
1670
+ // Clear project folder:
1671
+ $xmlPath=$this->getExportXMLPath($project);
1672
+ $this->deleteDirectoryRecursive($xmlPath);
1673
+
1674
+ $this->updateProjectState($project['id'],1); // 1 = Processing
1675
+ }
1676
+
1677
+ public function ajaxexportAction_CollectCategories($project, $offset)
1678
+ {
1679
+ $rv=array();
1680
+ $rv["status_msg"]=$this->__("Checking category")."...";
1681
+ $rv["offset"]=$offset;
1682
+ $rv["step"]=$this->STEP_COLLECT_CATEGORIES;
1683
+
1684
+ $dbres = Mage::getSingleton('core/resource');
1685
+ $dbw=$dbres->getConnection('core_write');
1686
+ $dbr=$dbres->getConnection('core_read');
1687
+
1688
+ $cat_id=-1;
1689
+
1690
+ $helper=$this->getHelper();
1691
+ $maxItems=intval($helper->getSetting("et_categories_per_file",20));
1692
+ if ($maxItems<1) { $maxItems=20; }
1693
+
1694
+ $manualSelected=false;
1695
+
1696
+ if ($project['categorymode']==1)
1697
+ {
1698
+ if ($offset==0)
1699
+ {
1700
+ // Reset table:
1701
+ //$dbw->query("DELETE FROM eurotext_project_products WHERE project_id=?;",array($project['id']));
1702
+ }
1703
+
1704
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('catalog_category_entity')."`"));
1705
+
1706
+ $rows=$dbr->fetchAll("SELECT entity_id FROM `".$this->getTableName('catalog_category_entity')."` ORDER BY entity_id ASC LIMIT $offset,$maxItems");
1707
+ }
1708
+ else
1709
+ {
1710
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('catalog_category_entity')."` e, `".$this->getTableName('eurotext_project_categories')."` p WHERE (e.entity_id=p.category_id) AND project_id=".$project['id']));
1711
+
1712
+ $manualSelected=true;
1713
+ $rows=$dbr->fetchAll("SELECT e.entity_id FROM `".$this->getTableName('catalog_category_entity')."` e, `".$this->getTableName('eurotext_project_categories')."` p WHERE (e.entity_id=p.category_id) AND project_id=".$project['id']." ORDER BY e.entity_id ASC LIMIT $offset,$maxItems");
1714
+ }
1715
+
1716
+ $itemsFound=false;
1717
+
1718
+ $doc = new DOMDocument('1.0', 'UTF-8');
1719
+ $doc->formatOutput = true;
1720
+
1721
+ $cats = $doc->createElement("categories");
1722
+ $doc->appendChild($cats);
1723
+
1724
+ foreach($rows as $row)
1725
+ {
1726
+ $itemsFound=true;
1727
+
1728
+ $cat_id=intval($row['entity_id']);
1729
+ $rv["status_msg"]="(".($offset+1)." / ".$cnt.") ".$this->__("Checking category")." ID=".$cat_id;
1730
+
1731
+ // Compare languages:
1732
+ $catSrc = Mage::getModel('catalog/category')->setStoreId($project['storeview_src'])->load($cat_id);
1733
+ $catDst = Mage::getModel('catalog/category')->setStoreId($project['storeview_dst'])->load($cat_id);
1734
+
1735
+ $hasChangedProperty=false;
1736
+
1737
+ $cat = $doc->createElement("category");
1738
+
1739
+ $nodeId = $doc->createElement("Id");
1740
+ $nodeId->appendChild($doc->createTextNode($cat_id));
1741
+ $cat->appendChild($nodeId);
1742
+
1743
+ $nodeUrl = $doc->createElement("Url");
1744
+ $this->appendTextChild($nodeUrl,$catSrc->getUrl());
1745
+ $cat->appendChild($nodeUrl);
1746
+
1747
+ // Name:
1748
+ if ($catSrc->getName()!="")
1749
+ {
1750
+ if ( ($catSrc->getName()==$catDst->getName()) || ($catDst->getName()=="") || ($manualSelected))
1751
+ {
1752
+ $hasChangedProperty=true;
1753
+
1754
+ $item=$doc->createElement("Title");
1755
+ $this->appendTextChild($item,$catSrc->getName());
1756
+ $cat->appendChild($item);
1757
+ }
1758
+ }
1759
+
1760
+ // Description:
1761
+ if ($catSrc->getDescription()!="")
1762
+ {
1763
+ if ( ($catSrc->getDescription()==$catDst->getDescription()) || ($catDst->getDescription()=="") || ($manualSelected))
1764
+ {
1765
+ $hasChangedProperty=true;
1766
+
1767
+ $item=$doc->createElement("Longdesc");
1768
+ $this->appendTextChild($item,$catSrc->getDescription());
1769
+ $cat->appendChild($item);
1770
+ }
1771
+ }
1772
+
1773
+ // Short-Description:
1774
+ if ($catSrc->getShortDescription()!="")
1775
+ {
1776
+ if ( ($catSrc->getShortDescription()==$catDst->getShortDescription()) || ($catDst->getShortDescription()=="") || ($manualSelected))
1777
+ {
1778
+ $hasChangedProperty=true;
1779
+
1780
+ $item=$doc->createElement("Shortdesc");
1781
+ $this->appendTextChild($item,$catSrc->getShortDescription());
1782
+ $cat->appendChild($item);
1783
+ }
1784
+ }
1785
+
1786
+ // Custom Attributes through config xml
1787
+ if($custom_attributes = Mage::helper('eurotext_translationmanager')->getCustomCategoryAttributesForExport()){
1788
+ $hasChangedProperty = true;
1789
+ $nodeCustomAttributes = $doc->createElement('custom_attributes');
1790
+ // Custom Attribute Mapping is not yet used nor implemented
1791
+ foreach($custom_attributes as $custom_attribute => $custom_attribute_mapping){
1792
+ $item = $doc->createElement((string) $custom_attribute);
1793
+ $this->appendTextChild($item, (string) $catSrc->getData($custom_attribute));
1794
+ $nodeCustomAttributes->appendChild($item);
1795
+ }
1796
+ $cat->appendChild($nodeCustomAttributes);
1797
+ }
1798
+
1799
+ if ($project['export_urlkeys']=="1")
1800
+ {
1801
+ // URL-Key:
1802
+ if ($catSrc->getUrlKey()!="")
1803
+ {
1804
+ if ( ($catSrc->getUrlKey()==$catDst->getUrlKey()) || ($catDst->getUrlKey()=="") || ($manualSelected))
1805
+ {
1806
+ $hasChangedProperty=true;
1807
+
1808
+ $item=$doc->createElement("UrlKey");
1809
+ $this->appendTextChild($item,$catSrc->getUrlKey());
1810
+ $cat->appendChild($item);
1811
+ }
1812
+ }
1813
+ }
1814
+
1815
+ if ($project['export_seo']=="1")
1816
+ {
1817
+ // Meta-Title:
1818
+ if ($catSrc->getMetaTitle()!="")
1819
+ {
1820
+ if ( ($catSrc->getMetaTitle()==$catDst->getMetaTitle()) || ($catDst->getMetaTitle()=="") || ($manualSelected))
1821
+ {
1822
+ $hasChangedProperty=true;
1823
+
1824
+ $item=$doc->createElement("SeoTitle");
1825
+ $this->appendTextChild($item,$catSrc->getMetaTitle());
1826
+ $cat->appendChild($item);
1827
+ }
1828
+ }
1829
+
1830
+ // Meta-Description:
1831
+ if ($catSrc->getMetaDescription()!="")
1832
+ {
1833
+ if ( ($catSrc->getMetaDescription()==$catDst->getMetaDescription()) || ($catDst->getMetaDescription()=="") || ($manualSelected))
1834
+ {
1835
+ $hasChangedProperty=true;
1836
+
1837
+ $item=$doc->createElement("SeoDescription");
1838
+ $this->appendTextChild($item,$catSrc->getMetaDescription());
1839
+ $cat->appendChild($item);
1840
+ }
1841
+ }
1842
+
1843
+ // Meta-Keywords:
1844
+ if ($catSrc->getMetaKeywords()!="")
1845
+ {
1846
+ if ( ($catSrc->getMetaKeywords()==$catDst->getMetaKeywords()) || ($catDst->getMetaKeywords()=="") || ($manualSelected))
1847
+ {
1848
+ $hasChangedProperty=true;
1849
+
1850
+ $item=$doc->createElement("SeoKeywords");
1851
+ $this->appendTextChild($item,$catSrc->getMetaKeywords());
1852
+ $cat->appendChild($item);
1853
+ }
1854
+ }
1855
+ }
1856
+
1857
+ if ($hasChangedProperty)
1858
+ {
1859
+ $cats->appendChild($cat);
1860
+ }
1861
+
1862
+ $rv["offset"]=($offset+$maxItems);
1863
+ }
1864
+
1865
+ if ($itemsFound)
1866
+ {
1867
+ $xmlDir=$this->getExportXMLPath($project);
1868
+ $xmlDir.=DS."categories";
1869
+ if (!is_dir($xmlDir))
1870
+ {
1871
+ mkdir($xmlDir);
1872
+ }
1873
+
1874
+ $fileNum=($offset/$maxItems)+1;
1875
+ $fileNum=intval($fileNum);
1876
+
1877
+ $xmlFilename=$xmlDir.DS."cat".$fileNum.".xml";
1878
+
1879
+ $doc->save($xmlFilename);
1880
+ }
1881
+ else
1882
+ {
1883
+ // no further cat found, go to next step:
1884
+ $rv['step']=$this->STEP_COLLECT_CMSPAGES;
1885
+ $rv["offset"]=0;
1886
+ }
1887
+
1888
+ return $rv;
1889
+ }
1890
+
1891
+ public function deleteDirectoryRecursive($dir)
1892
+ {
1893
+ foreach(glob($dir . DS.'*') as $file)
1894
+ {
1895
+ if(is_dir($file))
1896
+ {
1897
+ $this->deleteDirectoryRecursive($file);
1898
+ }
1899
+ else
1900
+ {
1901
+ unlink($file);
1902
+ }
1903
+ }
1904
+ rmdir($dir);
1905
+ }
1906
+
1907
+ public function ajaxexportAction_ExportAttributes($project, $offset)
1908
+ {
1909
+ $rv=array();
1910
+ $rv["status_msg"]=$this->__("Checking attributes")."...";
1911
+ $rv["offset"]=$offset;
1912
+ $rv["step"]=$this->STEP_EXPORT_ATTRIBUTES;
1913
+
1914
+ $dbres = Mage::getSingleton('core/resource');
1915
+ $dbr=$dbres->getConnection('core_read');
1916
+
1917
+ if ($project['export_attributes']==1)
1918
+ {
1919
+ $foundItems=0;
1920
+
1921
+ $doc = new DOMDocument('1.0', 'UTF-8');
1922
+ $doc->formatOutput = true;
1923
+
1924
+ $defComment=$doc->createComment("allow utf8-detection: öäü€");
1925
+ $doc->appendChild($defComment);
1926
+
1927
+ $nodeAttributes = $doc->createElement("attributes");
1928
+ $doc->appendChild($nodeAttributes);
1929
+
1930
+ // eav_attribute_label (min 1.4)
1931
+
1932
+ $attributesResult=$dbr->fetchAll("SELECT a.attribute_id, a.frontend_label, attribute_code, entity_type_id FROM `".$this->getTableName('eav_attribute')."` a WHERE is_user_defined=1");
1933
+ foreach($attributesResult as $attributeResult)
1934
+ {
1935
+ $foundItems++;
1936
+
1937
+ $src_val=$attributeResult['frontend_label'];
1938
+ $attribute_id=$attributeResult['attribute_id'];
1939
+
1940
+ $attribute_comment="(has no label)";
1941
+
1942
+ // Default Text available?
1943
+ $attributeDefaultTextResults=$dbr->fetchAll("SELECT `value` FROM `".$this->getTableName('eav_attribute_label')."` WHERE attribute_id=".$attribute_id." AND store_id=0");
1944
+ foreach($attributeDefaultTextResults as $attributeDefaultTextResult)
1945
+ {
1946
+ $src_val=$attributeDefaultTextResult['value'];
1947
+ $attribute_comment="(default label)";
1948
+ }
1949
+
1950
+ // Source Storeview-Text available?
1951
+ $attributeDefaultTextResults=$dbr->fetchAll("SELECT `value` FROM `".$this->getTableName('eav_attribute_label')."` WHERE attribute_id=".$attribute_id." AND store_id=".intval($project['storeview_src']));
1952
+ foreach($attributeDefaultTextResults as $attributeDefaultTextResult)
1953
+ {
1954
+ $src_val=$attributeDefaultTextResult['value'];
1955
+ $attribute_comment="(src-storeview label)";
1956
+ }
1957
+
1958
+ $nodeAttribute = $doc->createElement("attribute");
1959
+ $nodeAttributes->appendChild($nodeAttribute);
1960
+
1961
+ $attrId=$doc->createAttribute("id");
1962
+ $attrId->value=$attribute_id;
1963
+ $nodeAttribute->appendChild($attrId);
1964
+
1965
+ $attribute_comment.=" attribute_code='".$attributeResult['attribute_code']."', entity_type_id: ".$attributeResult['entity_type_id'];
1966
+ $nodeAttributeComment=$doc->createComment($attribute_comment);
1967
+ $nodeAttribute->appendChild($nodeAttributeComment);
1968
+
1969
+ $nodeName = $doc->createElement("AttributeName");
1970
+ $this->appendTextChild($nodeName,$src_val);
1971
+
1972
+ $attrTranslate=$doc->createAttribute("translate");
1973
+ $attrTranslate->value="1";
1974
+ $nodeName->appendChild($attrTranslate);
1975
+
1976
+ $nodeAttribute->appendChild($nodeName);
1977
+
1978
+ // Options:
1979
+ $attributeOptionsResult=$dbr->fetchAll("SELECT option_id FROM ".$this->getTableName('eav_attribute_option')." WHERE attribute_id=".$attribute_id." ORDER BY sort_order ASC, option_id ASC");
1980
+ if (count($attributeOptionsResult)>0)
1981
+ {
1982
+ $nodeOptions = $doc->createElement("options");
1983
+ $nodeAttribute->appendChild($nodeOptions);
1984
+
1985
+ foreach($attributeOptionsResult as $attributeOptionResult)
1986
+ {
1987
+ $nodeOption = $doc->createElement("option");
1988
+ $nodeOptions->appendChild($nodeOption);
1989
+
1990
+ $attrId=$doc->createAttribute("id");
1991
+ $attrId->value=$attributeOptionResult['option_id'];
1992
+ $nodeOption->appendChild($attrId);
1993
+
1994
+ $option_comment="(has no label)";
1995
+
1996
+ // Default Text available?
1997
+ $attributeOptionDefaultTextResults=$dbr->fetchAll("SELECT `value` FROM `".$this->getTableName('eav_attribute_option_value')."` WHERE option_id=".$attributeOptionResult['option_id']." AND store_id=0");
1998
+ foreach($attributeOptionDefaultTextResults as $attributeOptionDefaultTextResult)
1999
+ {
2000
+ $src_val=$attributeOptionDefaultTextResult['value'];
2001
+ $option_comment="(has default label)";
2002
+ }
2003
+
2004
+ // Source Storeview-Text available?
2005
+ $attributeOptionDefaultTextResults=$dbr->fetchAll("SELECT `value` FROM `".$this->getTableName('eav_attribute_option_value')."` WHERE option_id=".$attributeOptionResult['option_id']." AND store_id=".intval($project['storeview_src']));
2006
+ foreach($attributeOptionDefaultTextResults as $attributeOptionDefaultTextResult)
2007
+ {
2008
+ $src_val=$attributeOptionDefaultTextResult['value'];
2009
+ $option_comment="(has src-storeview label)";
2010
+ }
2011
+
2012
+ $nodeAttributeOptionComment=$doc->createComment($option_comment);
2013
+ $nodeOption->appendChild($nodeAttributeOptionComment);
2014
+
2015
+ $nodeName = $doc->createElement("OptionName");
2016
+ $this->appendTextChild($nodeName,$src_val);
2017
+
2018
+ $attrTranslate=$doc->createAttribute("translate");
2019
+ $attrTranslate->value="1";
2020
+ $nodeName->appendChild($attrTranslate);
2021
+
2022
+ $nodeOption->appendChild($nodeName);
2023
+ }
2024
+ }
2025
+ }
2026
+
2027
+ if ($foundItems>0)
2028
+ {
2029
+ $xmlDir=$this->getExportXMLPath($project);
2030
+ $xmlDir.=DS."attributes";
2031
+ if (!is_dir($xmlDir))
2032
+ {
2033
+ mkdir($xmlDir);
2034
+ }
2035
+
2036
+ $xml_filename=$xmlDir.DS."attributes.xml";
2037
+
2038
+ $doc->save($xml_filename);
2039
+ }
2040
+ }
2041
+
2042
+ $rv["step"]=$this->STEP_GENERATE_CONTROL_FILE;
2043
+ $rv["offset"]=0;
2044
+
2045
+ return $rv;
2046
+ }
2047
+
2048
+ private $STEP_START=0;
2049
+ private $STEP_COLLECT_LANGFILES=1;
2050
+ private $STEP_IMPORT_LANGFILES=2;
2051
+ private $STEP_BUILD_LANGXML=3;
2052
+ private $STEP_COLLECT_PRODUCTS=4;
2053
+ private $STEP_COLLECT_CATEGORIES=5;
2054
+ private $STEP_COLLECT_CMSPAGES=6;
2055
+ private $STEP_COLLECT_CMSBLOCKS=7;
2056
+ private $STEP_EXPORT_ATTRIBUTES=8;
2057
+ private $STEP_GENERATE_CONTROL_FILE=9;
2058
+ private $STEP_COMPRESS_FILES=10;
2059
+ private $STEP_TRANSMIT_ARCHIVE=11;
2060
+ private $STEP_DONE=12;
2061
+ private $STEP_COLLECT_TEMPLATES=13;
2062
+
2063
+ public function ajaxexportAction()
2064
+ {
2065
+ $this->loadLayout('adminhtml_eurotext_translationmanager_ajax');
2066
+ $block = $this->getLayout()->getBlock('et.tm.response.ajax');
2067
+
2068
+ $request=$this->getRequest();
2069
+ $step=intval($request->getParam("step"));
2070
+ $offset=intval($request->getParam("offset"));
2071
+ $project_id=intval($request->getParam("project_id"));
2072
+
2073
+ $project=$this->getProject($project_id);
2074
+
2075
+ // Defaults:
2076
+ $block->setStatus_code("ok");
2077
+ $block->setStatusMsg($this->__("Please wait")."...");
2078
+ $block->setOffset($offset);
2079
+ $block->setStep($step);
2080
+ $block->setFinished(0);
2081
+
2082
+ // steps:
2083
+ // -------------------------------------------------------------------------------------
2084
+ // 0: Jump to step 4, if language files is not selected for export
2085
+ // 1: Find language *.csv files and write found filenames to eurotext_csv
2086
+ // 2: For each offset import one *.csv file to eurotext_csv_data
2087
+ // 3: For each offset find missing translations for one *.csv and generate xml files
2088
+ // -------------------------------------------------------------------------------------
2089
+ // 4: Jump to step 5, if product files were selected manually
2090
+ // For each offset: Find missing translations for one product
2091
+ // 5: Jump to step 6, if category files where selected manually
2092
+ // For each offset: Find missing translations for one category
2093
+
2094
+ if ($step==$this->STEP_START)
2095
+ {
2096
+ // Clear project folder:
2097
+ $xmlPath=$this->getExportXMLPath($project);
2098
+ $this->deleteDirectoryRecursive($xmlPath);
2099
+
2100
+ $helper=$this->getHelper();
2101
+ $testResult=$helper->testFtpConnection();
2102
+
2103
+ if ($testResult['ok']!=true)
2104
+ {
2105
+ $block->setStatus_code("error");
2106
+ $block->setStatusMsg($testResult['statusmessage']);
2107
+ }
2108
+ else
2109
+ {
2110
+ if ($project['langfilesmode']==1)
2111
+ {
2112
+ // Search for missing translations:
2113
+ $block->setStep($this->STEP_COLLECT_LANGFILES);
2114
+ }
2115
+ else
2116
+ {
2117
+ $dbres = Mage::getSingleton('core/resource');
2118
+ $dbr=$dbres->getConnection('core_read');
2119
+
2120
+ $cnt=intval($dbr->fetchOne("SELECT COUNT(*) cnt FROM `".$this->getTableName('eurotext_csv')."` WHERE project_id=".$project['id']." AND translate_flag=1 AND locale_dst='en_US'"));
2121
+ if ($cnt>0)
2122
+ {
2123
+ // Customer has selected translation files:
2124
+ $block->setStep($this->STEP_COLLECT_LANGFILES);
2125
+ }
2126
+ else
2127
+ {
2128
+ $block->setStep($this->STEP_COLLECT_PRODUCTS);
2129
+ }
2130
+ }
2131
+ }
2132
+ }
2133
+ elseif ($step==$this->STEP_COLLECT_LANGFILES)
2134
+ {
2135
+ $this->ajaxexportAction_CollectLangfiles($project);
2136
+ $block->setStep($this->STEP_IMPORT_LANGFILES);
2137
+ $block->setOffset(0);
2138
+ }
2139
+ elseif ($step==$this->STEP_IMPORT_LANGFILES)
2140
+ {
2141
+ $rvSub=$this->ajaxexportAction_ImportLangfiles($project,$offset);
2142
+ if ($rvSub['offset']>=0)
2143
+ {
2144
+ $block->setStep($this->STEP_IMPORT_LANGFILES);
2145
+ $block->setStatusMsg($rvSub['status_msg']);
2146
+ $block->setOffset($rvSub['offset']);
2147
+ }
2148
+ else
2149
+ {
2150
+ $block->setStep($this->STEP_BUILD_LANGXML);
2151
+ $block->setOffset(0);
2152
+ $block->setStatusMsg($this->__("Generating Language files")."...");
2153
+ }
2154
+ }
2155
+ elseif ($step==$this->STEP_BUILD_LANGXML)
2156
+ {
2157
+ $rvSub=$this->ajaxexportAction_BuildLangXML($project,$offset);
2158
+ $block->setStep($rvSub['step']);
2159
+ $block->setOffset($rvSub['offset']);
2160
+ $block->setStatusMsg($rvSub['status_msg']);
2161
+ }
2162
+ elseif ($step==$this->STEP_COLLECT_PRODUCTS)
2163
+ {
2164
+ $rvSub=$this->ajaxexportAction_CollectProducts($project,$offset);
2165
+ $block->setStep($rvSub['step']);
2166
+ $block->setOffset($rvSub['offset']);
2167
+ $block->setStatusMsg($rvSub['status_msg']);
2168
+ }
2169
+ elseif ($step==$this->STEP_COLLECT_CATEGORIES)
2170
+ {
2171
+ $rvSub=$this->ajaxexportAction_CollectCategories($project,$offset);
2172
+ $block->setStep($rvSub['step']);
2173
+ $block->setOffset($rvSub['offset']);
2174
+ $block->setStatusMsg($rvSub['status_msg']);
2175
+ }
2176
+ elseif ($step==$this->STEP_COLLECT_CMSPAGES)
2177
+ {
2178
+ $rvSub=$this->ajaxexportAction_CollectCMSPages($project,$offset);
2179
+ $block->setStep($rvSub['step']);
2180
+ $block->setOffset($rvSub['offset']);
2181
+ $block->setStatusMsg($rvSub['status_msg']);
2182
+ }
2183
+ elseif ($step==$this->STEP_COLLECT_CMSBLOCKS)
2184
+ {
2185
+ $rvSub=$this->ajaxexportAction_CollectCMSBlocks($project,$offset);
2186
+ $block->setStep($rvSub['step']);
2187
+ $block->setOffset($rvSub['offset']);
2188
+ $block->setStatusMsg($rvSub['status_msg']);
2189
+ }
2190
+ elseif ($step==$this->STEP_COLLECT_TEMPLATES)
2191
+ {
2192
+ $rvSub=$this->ajaxexportAction_CollectTemplates($project,$offset);
2193
+ $block->setStep($rvSub['step']);
2194
+ $block->setOffset($rvSub['offset']);
2195
+ $block->setStatusMsg($rvSub['status_msg']);
2196
+ }
2197
+ elseif ($step==$this->STEP_EXPORT_ATTRIBUTES)
2198
+ {
2199
+ $rvSub=$this->ajaxexportAction_ExportAttributes($project,$offset);
2200
+ $block->setStep($rvSub['step']);
2201
+ $block->setOffset($rvSub['offset']);
2202
+ $block->setStatusMsg($rvSub['status_msg']);
2203
+ }
2204
+ elseif ($step==$this->STEP_GENERATE_CONTROL_FILE)
2205
+ {
2206
+ $this->ajaxexportAction_GenerateControlFile($project);
2207
+ $block->setStep($this->STEP_COMPRESS_FILES);
2208
+ $block->setStatusMsg($this->__("Generating ZIP-Archive"));
2209
+ }
2210
+ elseif ($step==$this->STEP_COMPRESS_FILES)
2211
+ {
2212
+ $this->ajaxexportAction_CompressFiles($project);
2213
+
2214
+ $block->setStep($this->STEP_TRANSMIT_ARCHIVE);
2215
+ $block->setStatusMsg($this->__("Sending data"));
2216
+ }
2217
+ elseif ($step==$this->STEP_TRANSMIT_ARCHIVE)
2218
+ {
2219
+ $this->ajaxexportAction_TransmitArchive($project);
2220
+ $block->setStep($this->STEP_DONE);
2221
+ $block->setStatusMsg($this->__("Done"));
2222
+ }
2223
+ else
2224
+ {
2225
+ $block->setStep($this->STEP_DONE);
2226
+ $block->setStatusMsg($this->__("Done"));
2227
+ $block->setFinished("1");
2228
+ }
2229
+
2230
+ $this->renderLayout();
2231
+ }
2232
+
2233
+ public function importextractAction()
2234
+ {
2235
+ $zipFound = false;
2236
+ $tmpdir=$this->getTempDir();
2237
+ $files=$this->getEurotextHelper()->getDirectoryContent($tmpdir, false, true, false, false);
2238
+ foreach($files as $file)
2239
+ {
2240
+ $filename=$file['full_path'];
2241
+ if ($this->getEurotextHelper()->endsWith(strtoupper($filename),".ZIP"))
2242
+ {
2243
+ // Extract file:
2244
+ if(!$this->getEurotextHelper()->extractZip($filename,$tmpdir)){
2245
+ $this->getHelper()->log('ZIP File could not be extracted', Zend_Log::CRIT);
2246
+ }
2247
+ $zipFound = true;
2248
+ }
2249
+ }
2250
+
2251
+ $this->getHelper()->log('ZIP File could '.($zipFound ? '' : 'not').' be found.');
2252
+ $import_url=Mage::helper('adminhtml')->getUrl('*/*/importparse');
2253
+ $this->_redirectUrl($import_url);
2254
+ }
2255
+
2256
+ public function importparseAction()
2257
+ {
2258
+ $tmpdir=$this->getTempDir();
2259
+ $controlFile=$tmpdir.DIRECTORY_SEPARATOR."control.xml";
2260
+
2261
+ if (!file_exists($controlFile))
2262
+ {
2263
+ $this->getHelper()->log('ZIP did not countain a control.xml file');
2264
+ Mage::throwException($this->__("Fehler: Die ZIP-Datei enthaelt keine control.xml"));
2265
+ }
2266
+
2267
+ $project_id=-1;
2268
+
2269
+ $doc = new DOMDocument();
2270
+ $doc->load($controlFile);
2271
+ $nodes=$doc->getElementsByTagName("Description");
2272
+ foreach($nodes as $node)
2273
+ {
2274
+ $description=$node->textContent;
2275
+ $pos1=stripos($description,"Magento-Project-Id: ");
2276
+ if ($pos1>=0)
2277
+ {
2278
+ $pos1+=strlen("Magento-Project-Id: ");
2279
+
2280
+ $pos2=stripos($description,",",$pos1);
2281
+ if ($pos2>$pos1)
2282
+ {
2283
+ $project_id_str=trim(substr($description,$pos1,($pos2-$pos1)));
2284
+ $project_id=intval($project_id_str);
2285
+ }
2286
+ }
2287
+ }
2288
+
2289
+ if ($project_id<=0)
2290
+ {
2291
+ Mage::throwException($this->__("Could not read project-id from Description-Field in control.xml"));
2292
+ }
2293
+
2294
+ $this->getHelper()->log('ZIP contains Data for Project ID '.$project_id);
2295
+
2296
+ $dbres = Mage::getSingleton('core/resource');
2297
+ $dbw=$dbres->getConnection('core_write');
2298
+
2299
+ // Remove old entries:
2300
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_import')."` WHERE project_id=".$project_id);
2301
+
2302
+ $project=$this->getProject($project_id);
2303
+ $storeview_dst=$project['storeview_dst'];
2304
+
2305
+ $filenames=array();
2306
+
2307
+ $uploadedFiles=$doc->getElementsByTagName("uploadedFile");
2308
+ foreach($uploadedFiles as $uploadedFile)
2309
+ {
2310
+ $filename="";
2311
+ foreach ($uploadedFile->attributes as $attrName => $attrNode)
2312
+ {
2313
+ if ($attrName=="fileName")
2314
+ {
2315
+ $filename=$attrNode->textContent;
2316
+ }
2317
+ }
2318
+ if ($filename!="")
2319
+ {
2320
+ $filenames[]=$filename;
2321
+ }
2322
+ }
2323
+
2324
+ sort($filenames);
2325
+
2326
+ $num=0;
2327
+ foreach($filenames as $filename)
2328
+ {
2329
+ $num++;
2330
+ $dbw->query("INSERT INTO `".$this->getTableName('eurotext_import')."` (project_id,filename,storeview_dst,is_imported,num) VALUES (?,?,?,?,?);",array($project_id,$filename,$storeview_dst,'0',$num));
2331
+ }
2332
+
2333
+ $this->updateProjectState($project_id,2); // 2 = Processing (2)
2334
+
2335
+ $import_url=Mage::helper('adminhtml')->getUrl('*/*/index',array('id'=>$project_id));
2336
+ $this->_redirectUrl($import_url);
2337
+ }
2338
+
2339
+ public function importstepAction()
2340
+ {
2341
+ $this->loadLayout('adminhtml_eurotext_translationmanager_ajax');
2342
+ $block = $this->getLayout()->getBlock('et.tm.response.ajax');
2343
+
2344
+ $request=$this->getRequest();
2345
+ $offset=intval($request->getParam("offset"));
2346
+ $project_id=intval($request->getParam("project_id"));
2347
+ $project=$this->getProject($project_id);
2348
+
2349
+ $dbres = Mage::getSingleton('core/resource');
2350
+ $dbr=$dbres->getConnection('core_read');
2351
+
2352
+ $finished=0;
2353
+ $status_msg="";
2354
+
2355
+ $tmpdir=$this->getTempDir();
2356
+
2357
+ $itemCount=intval($dbr->fetchOne("SELECT COUNT(*) FROM `".$this->getTableName('eurotext_import')."` WHERE project_id=?",array($project_id)));
2358
+
2359
+ $nextItems=$dbr->fetchAll("SELECT * FROM `".$this->getTableName('eurotext_import')."` WHERE project_id=? ORDER BY num ASC, filename ASC LIMIT $offset,1",array($project_id));
2360
+ if (count($nextItems)>0)
2361
+ {
2362
+ $nextItem=$nextItems[0];
2363
+ $filename=$nextItem['filename'];
2364
+
2365
+ $full_filename=$tmpdir.DIRECTORY_SEPARATOR.$filename;
2366
+
2367
+ if (stripos($filename,"cms-sites".DS."cms-")===0)
2368
+ {
2369
+ $this->importstepAction_ImportCMS($nextItem,$full_filename,$project,$dbr);
2370
+ }
2371
+ else if (stripos($filename,"cms-sites".DS."cmsblock-")===0)
2372
+ {
2373
+ $this->importstepAction_ImportBlocks($nextItem,$full_filename,$project,$dbr);
2374
+ }
2375
+ elseif (stripos($filename,"articles".DS)===0)
2376
+ {
2377
+ $this->importstepAction_ImportArticle($nextItem,$full_filename,$project);
2378
+ }
2379
+ elseif (stripos($filename,"categories".DS)===0)
2380
+ {
2381
+ $this->importstepAction_ImportCategory($nextItem,$full_filename,$project);
2382
+ }
2383
+ elseif(stripos($filename,"framework".DS)===0)
2384
+ {
2385
+ $this->importstepAction_ImportTranslation($nextItem,$full_filename,$project,$dbr);
2386
+ }
2387
+ elseif(stripos($filename,"attributes".DS)===0)
2388
+ {
2389
+ $this->importstepAction_ImportAttributes($nextItem,$full_filename,$project,$dbr);
2390
+ }
2391
+ elseif(stripos($filename,"emailtemplates".DS)===0)
2392
+ {
2393
+ $this->importstepAction_ImportTemplates($nextItem,$full_filename,$project,$dbr);
2394
+ }
2395
+
2396
+ $status_msg="[".($offset+1)." / ".$itemCount."] ".$this->__("Processing File")." '".$filename."'";
2397
+ }
2398
+ else
2399
+ {
2400
+ $this->updateProjectState($project_id,3); // 3 = Imported
2401
+
2402
+ $finished=1;
2403
+ $status_msg=$this->__("Done");
2404
+ }
2405
+
2406
+ $block->setStatusMsg($status_msg);
2407
+ $block->setStatus_code("ok");
2408
+ $block->setFinished($finished);
2409
+ $block->setOffset(($offset+1));
2410
+
2411
+ $this->renderLayout();
2412
+
2413
+ }
2414
+
2415
+ private function updateProjectState($project_id, $newStatusId)
2416
+ {
2417
+ $dbres = Mage::getSingleton('core/resource');
2418
+ $dbw=$dbres->getConnection('core_write');
2419
+
2420
+ // Update state:
2421
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project')."` SET project_status=? WHERE id=?",array($newStatusId,$project_id));
2422
+ }
2423
+
2424
+ private function getXMLChildNode($element,$childnodeName)
2425
+ {
2426
+ $childNodes=$element->childNodes;
2427
+ foreach($childNodes as $childNode)
2428
+ {
2429
+ if ($childNode->nodeType==XML_ELEMENT_NODE)
2430
+ {
2431
+ if ($childNode->tagName==$childnodeName)
2432
+ {
2433
+ return $childNode;
2434
+ }
2435
+ }
2436
+ }
2437
+
2438
+ return null;
2439
+ }
2440
+
2441
+ private function getXMLChildNodeText($element,$childnodeName,$defaultValue="")
2442
+ {
2443
+ $childNode=$this->getXMLChildNode($element,$childnodeName);
2444
+ if ($childNode==null)
2445
+ {
2446
+ return $defaultValue;
2447
+ }
2448
+
2449
+ return $childNode->textContent;
2450
+ }
2451
+
2452
+ public function importstepAction_ImportArticle($item,$full_filename,$project)
2453
+ {
2454
+ $this->getHelper()->log('=== Importing Products ===');
2455
+
2456
+ $dbres = Mage::getSingleton('core/resource');
2457
+ $dbw=$dbres->getConnection('core_write');
2458
+ $dbr=$dbres->getConnection("core_read");
2459
+
2460
+ $eavAttribute = Mage::getModel('eav/entity_attribute');
2461
+ $urlkey_attribute_id = $eavAttribute->getIdByCode('catalog_product', 'url_key');
2462
+
2463
+ $dst_store_id=$project['storeview_dst'];
2464
+
2465
+ $doc = new DOMDocument();
2466
+ $doc->load($full_filename);
2467
+ $articles=$doc->getElementsByTagName("article");
2468
+ foreach($articles as $article)
2469
+ {
2470
+ $fieldNodes=$article->childNodes;
2471
+
2472
+ $Id=0;
2473
+ $fields=array();
2474
+
2475
+ foreach($fieldNodes as $fieldNode)
2476
+ {
2477
+ $nodeName=trim($fieldNode->nodeName);
2478
+ $nodeContent=trim($fieldNode->textContent);
2479
+
2480
+ if ($nodeName!="")
2481
+ {
2482
+ if ($nodeName=="Id")
2483
+ {
2484
+ $Id=intval($nodeContent);
2485
+ }
2486
+ else if('custom_attributes' == $nodeName){
2487
+ $fields[$nodeName]=$fieldNode;
2488
+ }
2489
+ else
2490
+ {
2491
+ $fields[$nodeName]=$nodeContent;
2492
+ }
2493
+ }
2494
+ }
2495
+
2496
+ if ($Id>0)
2497
+ {
2498
+ /** @var Mage_Core_Model_Product $productDst */
2499
+ $productDst = Mage::getModel('catalog/product')->load($Id)->setStoreId($project['storeview_dst']);
2500
+ $this->getHelper()->log('Saving Product (ID '.$Id.') for StoreID: '.$project['storeview_dst']);
2501
+
2502
+ // Ignore fields:
2503
+ $ignoreFields=array("#text","Url");
2504
+
2505
+ $hasUrlKey=false;
2506
+
2507
+ foreach ($fields as $key => $value)
2508
+ {
2509
+ if ($key=="Title")
2510
+ {
2511
+ $productDst->setName($value);
2512
+ }
2513
+ else if ($key=="Longdesc")
2514
+ {
2515
+ $productDst->setDescription($value);
2516
+ }
2517
+ else if ($key == "UrlKey") {
2518
+ $urlProductCheck=Mage::getModel('catalog/product')->getCollection()->addAttributeToFilter('url_key', array('eq', $value));
2519
+ if ($urlProductCheck->count() > 0) {
2520
+ Mage::log($this->getHelper()->__('Changed duplicate URL Key'), Zend_Log::INFO, 'eurotext_urlkey_changes.log', true);
2521
+ Mage::log($this->getHelper()->__(' from "%s"', $value), Zend_Log::INFO, 'eurotext_urlkey_changes.log', true);
2522
+
2523
+ $value = $this->getHelper()->getUniqueUrl($value);
2524
+
2525
+ Mage::log($this->getHelper()->__(' to "%s"', $value), Zend_Log::INFO, 'eurotext_urlkey_changes.log', true);
2526
+
2527
+ }
2528
+ $productDst->setUrlKey($value);
2529
+ $hasUrlKey = true;
2530
+ }
2531
+ else if ($key=="Shortdesc")
2532
+ {
2533
+ $productDst->setShortDescription($value);
2534
+ }
2535
+ else if ($key=="SeoTitle")
2536
+ {
2537
+ $productDst->setMetaTitle($value);
2538
+ }
2539
+ else if ($key=="SeoDescription")
2540
+ {
2541
+ $productDst->setMetaDescription($value);
2542
+ }
2543
+ else if ($key=="SeoKeywords")
2544
+ {
2545
+ $productDst->setMetaKeyword($value);
2546
+ }
2547
+ else if ($key=="Images")
2548
+ {
2549
+ $imageNodes=$article->getElementsByTagName("Image");
2550
+ foreach($imageNodes as $imageNode)
2551
+ {
2552
+ $img_value_id=intval($imageNode->getAttributeNode("value_id")->value);
2553
+ $img_position=intval($imageNode->getAttributeNode("position")->value);
2554
+ $img_disabled=intval($imageNode->getAttributeNode("disabled")->value);
2555
+
2556
+ $labelNodes=$imageNode->getElementsByTagName("Label");
2557
+ foreach($labelNodes as $labelNode)
2558
+ {
2559
+ $img_label=trim($labelNode->textContent);
2560
+
2561
+ try
2562
+ {
2563
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('catalog_product_entity_media_gallery_value')."` (value_id,store_id,label,position,disabled) VALUES (?,?,?,?,?);",array($img_value_id,$dst_store_id,$img_label,$img_position,$img_disabled));
2564
+ $dbw->query("UPDATE `".$this->getTableName('catalog_product_entity_media_gallery_value')."` SET label=? WHERE value_id=? AND store_id=?;",array($img_label,$img_value_id,$dst_store_id));
2565
+ }
2566
+ catch(Exception $e)
2567
+ {
2568
+ // Exception might occur, if the image was deleted between export and import
2569
+ // due to foreign-key checks
2570
+ }
2571
+ }
2572
+ }
2573
+ }
2574
+ else if ($key=="Options")
2575
+ {
2576
+ $optionNodes=$article->getElementsByTagName("Option");
2577
+ foreach($optionNodes as $optionNode)
2578
+ {
2579
+ // Id:
2580
+ $option_id=intval($optionNode->getAttributeNode("Id")->value);
2581
+
2582
+ // Title:
2583
+ $option_title=trim($this->getXMLChildNodeText($optionNode,"Title",""));
2584
+ if (strlen($option_title)>0)
2585
+ {
2586
+ // Update Title:
2587
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('catalog_product_option_title')."` (option_id,store_id,title) VALUES (?,?,?);",array($option_id,$dst_store_id,$option_title));
2588
+ $dbw->query("UPDATE `".$this->getTableName('catalog_product_option_title')."` SET title=? WHERE option_id=? AND store_id=?;",array($option_title,$option_id,$dst_store_id));
2589
+ }
2590
+
2591
+ $OptionValueNodes=$article->getElementsByTagName("Value");
2592
+ foreach($OptionValueNodes as $OptionValueNode)
2593
+ {
2594
+ $option_value_id=intval($OptionValueNode->getAttributeNode("Id")->value);
2595
+ $option_value_title=trim($this->getXMLChildNodeText($OptionValueNode,"Title",""));
2596
+
2597
+ if (strlen($option_value_title)>0)
2598
+ {
2599
+ // Update Title:
2600
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('catalog_product_option_type_title')."` (option_type_id,store_id,title) VALUES (?,?,?);",array($option_value_id,$dst_store_id,$option_value_title));
2601
+ $dbw->query("UPDATE `".$this->getTableName('catalog_product_option_type_title')."` SET title=? WHERE option_type_id=? AND store_id=?;",array($option_value_title,$option_value_id,$dst_store_id));
2602
+ }
2603
+ }
2604
+ }
2605
+
2606
+ //echo "Options translated";
2607
+ //die();
2608
+ }
2609
+ else
2610
+ {
2611
+ if('custom_attributes' == $key){
2612
+ $custom_attributes = Mage::helper('eurotext_translationmanager')->getCustomProductAttributesForExport();
2613
+
2614
+ // value doesn't contain a text value as usual
2615
+ // only for custom_attributes key it's the node object
2616
+ $customAttributesNodes=$value->childNodes;
2617
+
2618
+ foreach ($customAttributesNodes as $customAttributesNode) {
2619
+ $custom_attribute_key = trim($customAttributesNode->nodeName);
2620
+ $custom_attribute_value = trim($customAttributesNode->textContent);
2621
+ if (array_key_exists($custom_attribute_key, $custom_attributes)) {
2622
+ $productDst->setDataUsingMethod($custom_attribute_key, $custom_attribute_value);
2623
+ }
2624
+ }
2625
+ }
2626
+ else if (!in_array($key,$ignoreFields))
2627
+ {
2628
+ $this->getHelper()->log('Unknown Field: '.$key, Zend_Log::EMERG);
2629
+ throw new Mage_Exception('Unknown Field: '.$key);
2630
+ }
2631
+ }
2632
+ }
2633
+
2634
+ if (!$hasUrlKey)
2635
+ {
2636
+ $productDst->setUrlKey(false);
2637
+ }
2638
+ $productDst->save();
2639
+ $this->getHelper()->log('== Product has been saved ==');
2640
+ }
2641
+ else
2642
+ {
2643
+ $this->getHelper()->log('Wrong Product (ID: '.$Id.')');
2644
+ Mage::throwException($this->__("Wrong Product ID '%s'", $Id));
2645
+ }
2646
+ }
2647
+ }
2648
+
2649
+ public function importstepAction_ImportCategory($item,$full_filename,$project)
2650
+ {
2651
+ $dbres = Mage::getSingleton('core/resource');
2652
+ $dbr=$dbres->getConnection("core_read");
2653
+
2654
+ $eavAttribute = Mage::getModel('eav/entity_attribute');
2655
+ $urlkey_attribute_id = $eavAttribute->getIdByCode('catalog_category', 'url_key');
2656
+
2657
+ $doc = new DOMDocument();
2658
+ $doc->load($full_filename);
2659
+ $categories=$doc->getElementsByTagName("category");
2660
+ foreach($categories as $category)
2661
+ {
2662
+ $fieldNodes=$category->childNodes;
2663
+
2664
+ $Id=0;
2665
+ $fields=array();
2666
+
2667
+ foreach($fieldNodes as $fieldNode)
2668
+ {
2669
+ $nodeName=trim($fieldNode->nodeName);
2670
+ $nodeContent=trim($fieldNode->textContent);
2671
+
2672
+ if ($nodeName!="")
2673
+ {
2674
+ if ($nodeName=="Id")
2675
+ {
2676
+ $Id=intval($nodeContent);
2677
+ }
2678
+ else
2679
+ {
2680
+ $fields[$nodeName]=$nodeContent;
2681
+ }
2682
+ }
2683
+ }
2684
+
2685
+ if ($Id>0)
2686
+ {
2687
+ $catDst = Mage::getModel('catalog/category')->load($Id)->setStoreId($project['storeview_dst']);
2688
+
2689
+ $hasUrlKey=false;
2690
+
2691
+ foreach ($fields as $key => $value)
2692
+ {
2693
+ if ($key=="Title")
2694
+ {
2695
+ $catDst->setName($value);
2696
+ }
2697
+ else if ($key=="Longdesc")
2698
+ {
2699
+ $catDst->setDescription($value);
2700
+ }
2701
+ else if ($key=="Shortdesc")
2702
+ {
2703
+ $catDst->setShortDescription($value);
2704
+ }
2705
+ else if ($key=="SeoTitle")
2706
+ {
2707
+ $catDst->setMetaTitle($value);
2708
+ }
2709
+ else if ($key=="SeoDescription")
2710
+ {
2711
+ $catDst->setMetaDescription($value);
2712
+ }
2713
+ else if ($key=="SeoKeywords")
2714
+ {
2715
+ $catDst->setMetaKeywords($value);
2716
+ }
2717
+ else if ($key=="UrlKey")
2718
+ {
2719
+ $catDst->setUrlKey($value);
2720
+ $hasUrlKey=true;
2721
+ }
2722
+ if('custom_attributes' == $key){
2723
+ $custom_attributes = Mage::helper('eurotext_translationmanager')->getCustomCategoryAttributesForExport();
2724
+
2725
+ // value doesn't contain a text value as usual
2726
+ // only for custom_attributes key it's the node object
2727
+ $customAttributesNodes=$value->childNodes;
2728
+
2729
+ foreach ($customAttributesNodes as $customAttributesNode) {
2730
+ $custom_attribute_key = trim($customAttributesNode->nodeName);
2731
+ $custom_attribute_value = trim($customAttributesNode->textContent);
2732
+ if (array_key_exists($custom_attribute_key, $custom_attributes)) {
2733
+ $catDst->setDataUsingMethod($custom_attribute_key, $custom_attribute_value);
2734
+ }
2735
+ }
2736
+ }
2737
+ }
2738
+
2739
+ if (!$hasUrlKey)
2740
+ {
2741
+ // Check if urlkey is already set:
2742
+ $res=$dbr->fetchAll("SELECT value FROM ".$this->getTableName('catalog_category_entity_varchar')." WHERE entity_id=".$Id." AND store_id=".$project['storeview_dst']." AND attribute_id=".$urlkey_attribute_id);
2743
+ if (count($res)==0) // Currently using default storeview value
2744
+ {
2745
+ // setting null will force magento to generate the urlkey using the product-name
2746
+ $catDst->setUrlKey(null);
2747
+ }
2748
+ }
2749
+
2750
+ $catDst->save();
2751
+ }
2752
+ else
2753
+ {
2754
+ Mage::throwException($this->__("Wrong Category ID '%s'", $Id));
2755
+ }
2756
+ }
2757
+ }
2758
+
2759
+ public function importstepAction_ImportCMS($item,$full_filename,$project,$dbr)
2760
+ {
2761
+ $doc = new DOMDocument();
2762
+ $doc->load($full_filename);
2763
+ $cmsSites=$doc->getElementsByTagName("cms-site");
2764
+ foreach($cmsSites as $cmsSite)
2765
+ {
2766
+ $fieldNodes=$cmsSite->childNodes;
2767
+
2768
+ $Id=0;
2769
+ $fields=array();
2770
+ $StoreviewSrc=-1;
2771
+ $StoreviewDst=-1;
2772
+
2773
+ foreach($fieldNodes as $fieldNode)
2774
+ {
2775
+ $nodeName=trim($fieldNode->nodeName);
2776
+ $nodeContent=trim($fieldNode->textContent);
2777
+
2778
+ if ($nodeName!="")
2779
+ {
2780
+ if ($nodeName=="Id")
2781
+ {
2782
+ $Id=intval($nodeContent);
2783
+ }
2784
+ elseif ($nodeName=="StoreviewSrc")
2785
+ {
2786
+ $StoreviewSrc=intval($nodeContent);
2787
+ }
2788
+ elseif ($nodeName=="StoreviewDst")
2789
+ {
2790
+ $StoreviewDst=intval($nodeContent);
2791
+ }
2792
+ else
2793
+ {
2794
+ $fields[$nodeName]=$nodeContent;
2795
+ }
2796
+ }
2797
+ }
2798
+
2799
+ if ($Id>0)
2800
+ {
2801
+ $pageDst = null;
2802
+ $pageSrc=Mage::getModel('cms/page')->load($Id);
2803
+
2804
+ // Remove dst-storeview from source cms-page:
2805
+ $srcStoreviewIds=$pageSrc->getStoreId();
2806
+ if (in_array(0,$srcStoreviewIds))
2807
+ {
2808
+ // Resolve store_id=0 ("all storeviews"):
2809
+ $srcStore=Mage::getModel("core/store")->load($StoreviewSrc);
2810
+ $srcWebsite=$srcStore->getWebsite();
2811
+ $srcWebsiteStoreIds=$srcWebsite->getStoreIds();
2812
+ foreach($srcWebsiteStoreIds as $srcWebsiteStoreId)
2813
+ {
2814
+ $srcStoreviewIds[]=$srcWebsiteStoreId;
2815
+ }
2816
+ }
2817
+ $srcStoreviewIds=array_unique($srcStoreviewIds);
2818
+ // Remove destination storeview:
2819
+ $srcStoreviewIds=array_diff($srcStoreviewIds,array(0,$StoreviewDst));
2820
+
2821
+ $pageSrc->setStoreId($srcStoreviewIds);
2822
+ $pageSrc->save();
2823
+
2824
+ $identifier=$pageSrc->getIdentifier();
2825
+ // Find matching page:
2826
+ $matchingPages=$dbr->fetchAll("SELECT p.page_id FROM `".$this->getTableName('cms_page')."` p, `".$this->getTableName('cms_page_store')."` s WHERE (p.page_id=s.page_id) AND UPPER(p.identifier)=? AND s.store_id>0 AND s.store_id=? ORDER BY p.page_id ASC",array($identifier,$project['storeview_dst']));
2827
+ if (count($matchingPages)>0)
2828
+ {
2829
+ $matchingPage=$matchingPages[0];
2830
+ $page_id_dst=$matchingPage['page_id'];
2831
+
2832
+ $pageDst = Mage::getModel('cms/page')->load($page_id_dst);
2833
+ }
2834
+ else
2835
+ {
2836
+ // Destination CMS-Page does not exist yet, and needs to be created (clone source cms page)
2837
+ $pageData = array(
2838
+ 'title' => $pageSrc->getTitle(),
2839
+ 'root_template' => $pageSrc->getRootTemplate(),
2840
+ 'meta_keywords' => $pageSrc->getMetaKeywords(),
2841
+ 'meta_description' => $pageSrc->getMetaDescription(),
2842
+ 'identifier' => $pageSrc->getIdentifier(),
2843
+ 'content_heading' => $pageSrc->getContentHeading(),
2844
+ 'stores' => array($StoreviewDst),
2845
+ 'content' => $pageSrc->getContent()
2846
+ );
2847
+ $pageDst=Mage::getModel('cms/page')->setData($pageData)->save();
2848
+ }
2849
+
2850
+ $pageDst->setStoreId(array($StoreviewDst));
2851
+
2852
+ foreach ($fields as $key => $value)
2853
+ {
2854
+ if ($key=="Title")
2855
+ {
2856
+ $pageDst->setTitle($value);
2857
+ }
2858
+ elseif($key=="Content")
2859
+ {
2860
+ $pageDst->setContent($value);
2861
+ }
2862
+ elseif($key=="ContentHeading")
2863
+ {
2864
+ $pageDst->setContentHeading($value);
2865
+ }
2866
+ elseif($key=="SeoKeywords")
2867
+ {
2868
+ $pageDst->setMetaKeywords($value);
2869
+ }
2870
+ elseif($key=="SeoDescription")
2871
+ {
2872
+ $pageDst->setMetaDescription($value);
2873
+ }
2874
+ }
2875
+
2876
+ $pageDst->save();
2877
+ }
2878
+ }
2879
+ }
2880
+
2881
+ public function importstepAction_ImportBlocks($item,$full_filename,$project,$dbr)
2882
+ {
2883
+ $doc = new DOMDocument();
2884
+ $doc->load($full_filename);
2885
+ $cmsSites=$doc->getElementsByTagName("cms-site");
2886
+ foreach($cmsSites as $cmsSite)
2887
+ {
2888
+ $fieldNodes=$cmsSite->childNodes;
2889
+
2890
+ $Id=0;
2891
+ $fields=array();
2892
+ $StoreviewSrc=-1;
2893
+ $StoreviewDst=-1;
2894
+
2895
+ foreach($fieldNodes as $fieldNode)
2896
+ {
2897
+ $nodeName=trim($fieldNode->nodeName);
2898
+ $nodeContent=trim($fieldNode->textContent);
2899
+
2900
+ if ($nodeName!="")
2901
+ {
2902
+ if ($nodeName=="Id")
2903
+ {
2904
+ $Id=intval($nodeContent);
2905
+ }
2906
+ elseif ($nodeName=="StoreviewSrc")
2907
+ {
2908
+ $StoreviewSrc=intval($nodeContent);
2909
+ }
2910
+ elseif ($nodeName=="StoreviewDst")
2911
+ {
2912
+ $StoreviewDst=intval($nodeContent);
2913
+ }
2914
+ else
2915
+ {
2916
+ $fields[$nodeName]=$nodeContent;
2917
+ }
2918
+ }
2919
+ }
2920
+
2921
+ if ($Id>0)
2922
+ {
2923
+ $blockDst = null;
2924
+ $blockSrc=Mage::getModel('cms/block')->load($Id);
2925
+
2926
+ // Remove dst-storeview from source cms-block:
2927
+ $srcStoreviewIds=$blockSrc->getStoreId();
2928
+ if (in_array(0,$srcStoreviewIds))
2929
+ {
2930
+ // Resolve store_id=0 ("all storeviews"):
2931
+ $srcStore=Mage::getModel("core/store")->load($StoreviewSrc);
2932
+ $srcWebsite=$srcStore->getWebsite();
2933
+ $srcWebsiteStoreIds=$srcWebsite->getStoreIds();
2934
+ foreach($srcWebsiteStoreIds as $srcWebsiteStoreId)
2935
+ {
2936
+ $srcStoreviewIds[]=$srcWebsiteStoreId;
2937
+ }
2938
+ }
2939
+ $srcStoreviewIds=array_unique($srcStoreviewIds);
2940
+ // Remove destination storeview:
2941
+ $srcStoreviewIds=array_diff($srcStoreviewIds,array(0,$StoreviewDst));
2942
+
2943
+ $blockSrc->setStoreId($srcStoreviewIds);
2944
+ $blockSrc->save();
2945
+
2946
+ $identifier=$blockSrc->getIdentifier();
2947
+ // Find matching block:
2948
+ $matchingPages=$dbr->fetchAll("SELECT p.block_id FROM `".$this->getTableName('cms_block')."` p, `".$this->getTableName('cms_block_store')."` s WHERE (p.block_id=s.block_id) AND UPPER(p.identifier)=? AND s.store_id>0 AND s.store_id=? ORDER BY p.block_id ASC",array($identifier,$project['storeview_dst']));
2949
+ if (count($matchingPages)>0)
2950
+ {
2951
+ $matchingPage=$matchingPages[0];
2952
+ $block_id_dst=$matchingPage['block_id'];
2953
+
2954
+ $blockDst = Mage::getModel('cms/block')->load($block_id_dst);
2955
+ }
2956
+ else
2957
+ {
2958
+ // Destination CMS-Page does not exist yet, and needs to be created (clone source cms block)
2959
+ $blockData = array(
2960
+ 'title' => $blockSrc->getTitle(),
2961
+ 'root_template' => $blockSrc->getRootTemplate(),
2962
+ 'meta_keywords' => $blockSrc->getMetaKeywords(),
2963
+ 'meta_description' => $blockSrc->getMetaDescription(),
2964
+ 'identifier' => $blockSrc->getIdentifier(),
2965
+ 'content_heading' => $blockSrc->getContentHeading(),
2966
+ 'stores' => array($StoreviewDst),
2967
+ 'content' => $blockSrc->getContent()
2968
+ );
2969
+ $blockDst=Mage::getModel('cms/block')->setData($blockData)->save();
2970
+ }
2971
+
2972
+ $blockDst->setStoreId(array($StoreviewDst));
2973
+
2974
+ foreach ($fields as $key => $value)
2975
+ {
2976
+ if ($key=="Title")
2977
+ {
2978
+ $blockDst->setTitle($value);
2979
+ }
2980
+ elseif($key=="Content")
2981
+ {
2982
+ $blockDst->setContent($value);
2983
+ }
2984
+ elseif($key=="ContentHeading")
2985
+ {
2986
+ $blockDst->setContentHeading($value);
2987
+ }
2988
+ elseif($key=="SeoKeywords")
2989
+ {
2990
+ $blockDst->setMetaKeywords($value);
2991
+ }
2992
+ elseif($key=="SeoDescription")
2993
+ {
2994
+ $blockDst->setMetaDescription($value);
2995
+ }
2996
+ }
2997
+
2998
+ $blockDst->save();
2999
+ }
3000
+ }
3001
+ }
3002
+
3003
+ public function importstepAction_ImportAttributes($item,$full_filename,$project,$dbr)
3004
+ {
3005
+ $doc = new DOMDocument();
3006
+ $doc->load($full_filename);
3007
+ $attributes=$doc->getElementsByTagName("attribute");
3008
+
3009
+ $dbres = Mage::getSingleton('core/resource');
3010
+ $dbw=$dbres->getConnection('core_write');
3011
+ $dbr=$dbres->getConnection('core_read');
3012
+
3013
+ foreach($attributes as $attribute)
3014
+ {
3015
+ $attribute_id=intval($attribute->getAttribute("id"));
3016
+ $attributeName="";
3017
+
3018
+ $attributeNameNodes=$attribute->getElementsByTagName("AttributeName");
3019
+ foreach($attributeNameNodes as $attributeNameNode)
3020
+ {
3021
+ $attributeName=$attributeNameNode->textContent;
3022
+ }
3023
+
3024
+ // Update attribute label:
3025
+ $qResult=$dbr->fetchAll("SELECT attribute_label_id FROM `".$this->getTableName('eav_attribute_label')."` WHERE attribute_id=".$attribute_id." AND store_id=".$project['storeview_dst']);
3026
+ if (count($qResult)>0)
3027
+ {
3028
+ // Update:
3029
+ $dbw->query("UPDATE `".$this->getTableName('eav_attribute_label')."` SET `value`=? WHERE attribute_id=".$attribute_id." AND store_id=".$project['storeview_dst'],array($attributeName));
3030
+ }
3031
+ else
3032
+ {
3033
+ // Insert:
3034
+ $dbw->query("INSERT INTO `".$this->getTableName('eav_attribute_label')."` (attribute_id,store_id,`value`) VALUES (?,?,?)",array($attribute_id,$project['storeview_dst'],$attributeName));
3035
+ }
3036
+
3037
+ $options=$attribute->getElementsByTagName("option");
3038
+ foreach($options as $option)
3039
+ {
3040
+ $option_id=intval($option->getAttribute("id"));
3041
+ $optionName="";
3042
+
3043
+ $optionNameNodes=$option->getElementsByTagName("OptionName");
3044
+ foreach($optionNameNodes as $optionNameNode)
3045
+ {
3046
+ $optionName=$optionNameNode->textContent;
3047
+ }
3048
+
3049
+ // Update attribute label:
3050
+ $qResult=$dbr->fetchAll("SELECT value_id FROM `".$this->getTableName('eav_attribute_option_value')."` WHERE option_id=".$option_id." AND store_id=".$project['storeview_dst']);
3051
+ if (count($qResult)>0)
3052
+ {
3053
+ // Update:
3054
+ $dbw->query("UPDATE `".$this->getTableName('eav_attribute_option_value')."` SET `value`=? WHERE option_id=".$option_id." AND store_id=".$project['storeview_dst'],array($optionName));
3055
+ }
3056
+ else
3057
+ {
3058
+ // Insert:
3059
+ $dbw->query("INSERT INTO `".$this->getTableName('eav_attribute_option_value')."` (option_id,store_id,`value`) VALUES (?,?,?)",array($option_id,$project['storeview_dst'],$optionName));
3060
+ }
3061
+ }
3062
+ }
3063
+ }
3064
+
3065
+ public function importstepAction_ImportTemplates($item,$full_filename,$project,$dbr)
3066
+ {
3067
+ $abc=array();
3068
+ $abc['item']=$item;
3069
+ $abc['full_filename']=$full_filename;
3070
+
3071
+ $filename=$item['filename'];
3072
+ $filename=substr($filename,strlen("emailtemplates"));
3073
+
3074
+ $baseLocaleFolder=Mage::getBaseDir('locale');
3075
+
3076
+ $dst_filename=$baseLocaleFolder.DS.$project['storeview_dst_locale'].DS."template".$filename;
3077
+ $dst_directory=$this->getEurotextHelper()->GetDirectoryFromPath($dst_filename);
3078
+ if (!is_dir($dst_directory))
3079
+ {
3080
+ mkdir($dst_directory,0777,true);
3081
+ }
3082
+
3083
+ copy($full_filename,$dst_filename);
3084
+ }
3085
+
3086
+ public function importstepAction_ImportTranslation($item,$full_filename,$project,$dbr)
3087
+ {
3088
+ $doc = new DOMDocument();
3089
+ $doc->load($full_filename);
3090
+ $translations=$doc->getElementsByTagName("translation");
3091
+ foreach($translations as $translation)
3092
+ {
3093
+ $src_filename=$translation->getAttribute("src_filename");
3094
+ $dst_filename=$translation->getAttribute("dst_filename");
3095
+
3096
+ $translationDict=array();
3097
+
3098
+ $idx=0;
3099
+ $foundRow=true;
3100
+ while($foundRow)
3101
+ {
3102
+ $idx++;
3103
+ $lineName="line".$idx;
3104
+
3105
+ $foundRow=false;
3106
+ $sublines=$translation->getElementsByTagName($lineName);
3107
+ foreach($sublines as $subline)
3108
+ {
3109
+ $foundRow=true;
3110
+
3111
+ $lines=$subline->getElementsByTagName("line");
3112
+ foreach($lines as $line)
3113
+ {
3114
+ $srchash=$line->getAttribute("srchash");
3115
+ $text_translated=$line->textContent;
3116
+
3117
+ // Get original line:
3118
+ $origLines=$dbr->fetchAll("SELECT text_src FROM `".$this->getTableName('eurotext_csv_data')."` WHERE text_src_hash=?",array($srchash));
3119
+ if (count($origLines)>0)
3120
+ {
3121
+ $origLine=$origLines[0];
3122
+ $text_original=$origLine['text_src'];
3123
+
3124
+ $translationDict[$text_original]=$text_translated;
3125
+ }
3126
+ }
3127
+ }
3128
+ }
3129
+
3130
+ $full_path=Mage::getBaseDir('app').$dst_filename;
3131
+
3132
+ // Create Backup:
3133
+ if (is_file($full_path))
3134
+ {
3135
+ $backup_path=$this->getProjectXMLPath($project,"backup").$dst_filename;
3136
+ $backup_dir=$this->getEurotextHelper()->GetDirectoryFromPath($backup_path);
3137
+ if (!is_dir($backup_dir))
3138
+ {
3139
+ mkdir($backup_dir,0777,true);
3140
+ }
3141
+ if (!is_file($backup_path))
3142
+ {
3143
+ copy($full_path,$backup_path);
3144
+ }
3145
+ }
3146
+
3147
+ // Read destination csv:
3148
+ if (is_file($full_path))
3149
+ {
3150
+ $fp=fopen($full_path,"r");
3151
+ $lineIndex=0;
3152
+ do
3153
+ {
3154
+ $fields=fgetcsv($fp,0,",","\"");
3155
+ if ($fields!==false)
3156
+ {
3157
+ if (count($fields)==2)
3158
+ {
3159
+ if (array_key_exists($fields[0],$translationDict))
3160
+ {
3161
+ // Skip
3162
+ }
3163
+ else
3164
+ {
3165
+ // Add item:
3166
+ $translationDict[$fields[0]]=$fields[1];
3167
+ }
3168
+ }
3169
+ }
3170
+
3171
+ $lineIndex++;
3172
+ }
3173
+ while($fields!==false);
3174
+ fclose($fp);
3175
+ }
3176
+
3177
+ // Create folder, if not exists:
3178
+ $full_path_dir=$this->getEurotextHelper()->GetDirectoryFromPath($full_path);
3179
+ if (!is_dir($full_path_dir))
3180
+ {
3181
+ mkdir($full_path_dir,0777,true);
3182
+ }
3183
+
3184
+ // Write new file:
3185
+ {
3186
+ $wp=fopen($full_path,"w");
3187
+ {
3188
+ foreach($translationDict as $key => $val)
3189
+ {
3190
+ $fields=array($key,$val);
3191
+ fputcsv($wp,$fields,",","\"");
3192
+ }
3193
+ }
3194
+ fclose($wp);
3195
+ }
3196
+ }
3197
+ }
3198
+
3199
+ public function uploadAction()
3200
+ {
3201
+
3202
+ // Clear tmp folder:
3203
+ $tmpdir=$this->getTempDir();
3204
+ $this->deleteDirectoryRecursive($tmpdir);
3205
+ $tmpdir=$this->getTempDir();
3206
+
3207
+ if(isset($_FILES['zipfile']['name']) && $_FILES['zipfile']['name'] != '')
3208
+ {
3209
+ try
3210
+ {
3211
+ $fname = $_FILES['zipfile']['name']; //file name
3212
+ $uploader = new Varien_File_Uploader('zipfile'); //load class
3213
+ $uploader->setAllowedExtensions(array('zip'));
3214
+ $uploader->setAllowCreateFolders(true); //for creating the directory if not exists
3215
+ $uploader->setAllowRenameFiles(false); //if true, uploaded file's name will be changed, if file with the same name already exists directory.
3216
+ $uploader->setFilesDispersion(false);
3217
+ $uploader->save($tmpdir,$fname); //save the file on the specified path
3218
+
3219
+ $import_url=Mage::helper('adminhtml')->getUrl('*/*/importextract');
3220
+ $this->_redirectUrl($import_url);
3221
+ }
3222
+ catch (Exception $e)
3223
+ {
3224
+ $this->loadLayout('adminhtml_eurotext_translationmanager_text');
3225
+ $block = $this->getLayout()->getBlock('et.tm.response.text');
3226
+
3227
+ $block->setText($this->__("Error Message").': '.$e->getMessage());
3228
+ $this->renderLayout();
3229
+ }
3230
+ }
3231
+ else
3232
+ {
3233
+ $this->loadLayout('adminhtml_eurotext_translationmanager_text');
3234
+ $block = $this->getLayout()->getBlock('et.tm.response.text');
3235
+
3236
+ $block->setText($this->__("No file given."));
3237
+ $this->renderLayout();
3238
+ }
3239
+ }
3240
+ }
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/RegisterController.php ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Adminhtml_Eurotext_Translationmanager_RegisterController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+
6
+ protected $helper = false;
7
+
8
+ protected function _isAllowed()
9
+ {
10
+ return Mage::getSingleton('admin/session')
11
+ ->isAllowed('eurotext_translationmanager/register');
12
+ }
13
+
14
+
15
+ protected function _construct(){
16
+ $this->helper = Mage::helper('eurotext_translationmanager');
17
+ }
18
+
19
+ protected function getHelper(){
20
+ return $this->helper;
21
+ }
22
+
23
+ public function indexAction()
24
+ {
25
+ $this->loadLayout();
26
+ $this->renderLayout();
27
+ }
28
+
29
+ public function addLine(&$str, $line)
30
+ {
31
+ $str=$str."".$line."\r\n";
32
+ return $str;
33
+ }
34
+
35
+ public function addLineHtml(&$str, $line)
36
+ {
37
+ $str2="<div>";
38
+ $str2.=$line;
39
+ $str2.="</div>";
40
+
41
+ return $this->addLine($str,$str2);
42
+ }
43
+
44
+ public function htmlencode($str)
45
+ {
46
+ return htmlentities($str,ENT_QUOTES,'UTF-8');
47
+ }
48
+
49
+ public function AnredeAufDeutsch($anrede)
50
+ {
51
+ if ($anrede=="MR") { return "Herr"; }
52
+ if ($anrede=="MRS") { return "Frau"; }
53
+
54
+ return $anrede;
55
+ }
56
+
57
+ public function saveAction()
58
+ {
59
+ /** @var Eurotext_TranslationManager_Helper_Data $helper */
60
+ $helper=$this->getHelper();
61
+ $request=$this->getRequest();
62
+
63
+ $prev_register_shopname = $helper->getSetting("register_shopname","");
64
+ $prev_register_url = $helper->getSetting("register_url","");
65
+ $prev_register_email = $helper->getSetting("register_email","");
66
+ $prev_register_sal = $helper->getSetting("register_sal","");
67
+ $prev_register_fname = $helper->getSetting("register_fname","");
68
+ $prev_register_lname = $helper->getSetting("register_lname","");
69
+ $prev_register_company = $helper->getSetting("register_company","");
70
+ $prev_register_street = $helper->getSetting("register_street","");
71
+ $prev_register_hnumber = $helper->getSetting("register_hnumber","");
72
+ $prev_register_zip = $helper->getSetting("register_zip","");
73
+ $prev_register_city = $helper->getSetting("register_city","");
74
+ $prev_register_country = $helper->getSetting("register_country","");
75
+ $prev_register_telefon = $helper->getSetting("register_telefon","");
76
+
77
+ $new_register_shopname = $request->getParam("register_shopname");
78
+ $new_register_url = $request->getParam("register_url");
79
+ $new_register_email = $request->getParam("register_email");
80
+ $new_register_sal = $request->getParam("register_sal");
81
+ $new_register_fname = $request->getParam("register_fname");
82
+ $new_register_lname = $request->getParam("register_lname");
83
+ $new_register_company = $request->getParam("register_company");
84
+ $new_register_street = $request->getParam("register_street");
85
+ $new_register_hnumber = $request->getParam("register_hnumber");
86
+ $new_register_zip = $request->getParam("register_zip");
87
+ $new_register_city = $request->getParam("register_city");
88
+ $new_register_country = $request->getParam("register_country");
89
+ $new_register_telefon = $request->getParam("register_telefon");
90
+
91
+ // Save settings:
92
+ $helper->saveSetting("register_shopname", $request->getParam("register_shopname"));
93
+ $helper->saveSetting("register_url", $request->getParam("register_url"));
94
+ $helper->saveSetting("register_email", $request->getParam("register_email"));
95
+ $helper->saveSetting("register_sal", $request->getParam("register_sal"));
96
+ $helper->saveSetting("register_fname", $request->getParam("register_fname"));
97
+ $helper->saveSetting("register_lname", $request->getParam("register_lname"));
98
+ $helper->saveSetting("register_shopname", $request->getParam("register_shopname"));
99
+ $helper->saveSetting("register_company", $request->getParam("register_company"));
100
+ $helper->saveSetting("register_street", $request->getParam("register_street"));
101
+ $helper->saveSetting("register_hnumber", $request->getParam("register_hnumber"));
102
+ $helper->saveSetting("register_zip", $request->getParam("register_zip"));
103
+ $helper->saveSetting("register_city", $request->getParam("register_city"));
104
+ $helper->saveSetting("register_country", $request->getParam("register_country"));
105
+ $helper->saveSetting("register_telefon", $request->getParam("register_telefon"));
106
+
107
+ $register_mailsent=$helper->getSetting("register_mailsent", "0");
108
+
109
+ $email_body="";
110
+ $this->addLine($email_body,"<div>Hallo,</div>");
111
+ if ($register_mailsent=="1")
112
+ {
113
+ $this->addLine($email_body,"<div>".$this->htmlencode("Ein Kunde hat im Magento-Modul seine Kontaktdaten geändert:")."</div>");
114
+ }
115
+ else
116
+ {
117
+ $this->addLine($email_body,"<div>".$this->htmlencode("Ein Kunde hat sich via Magento-Modul neu registriert")."</div>");
118
+ }
119
+
120
+ $this->addLine($email_body,"<div>Kundennummer: ".$this->htmlencode($helper->getSetting("eurotext_customerid",""))."</div>");
121
+ $this->addLine($email_body,"<div>Benutzername: ".$this->htmlencode($helper->getSetting("eurotext_username",""))."</div>");
122
+
123
+ $fieldItems=array();
124
+ $fieldItems[0]['title']='Shopname';
125
+ $fieldItems[0]['new']=$new_register_shopname;
126
+ $fieldItems[0]['prev']=$prev_register_shopname;
127
+
128
+ $fieldItems[1]['title']='Shop URL';
129
+ $fieldItems[1]['new']=$new_register_url;
130
+ $fieldItems[1]['prev']=$prev_register_url;
131
+
132
+ $fieldItems[2]['title']='eMail-Adresse';
133
+ $fieldItems[2]['new']=$new_register_email;
134
+ $fieldItems[2]['prev']=$prev_register_email;
135
+
136
+ $fieldItems[3]['title']='Anrede';
137
+ $fieldItems[3]['new']=$this->AnredeAufDeutsch($new_register_sal);
138
+ $fieldItems[3]['prev']=$this->AnredeAufDeutsch($prev_register_sal);
139
+
140
+ $fieldItems[4]['title']='Vorname';
141
+ $fieldItems[4]['new']=$new_register_fname;
142
+ $fieldItems[4]['prev']=$prev_register_fname;
143
+
144
+ $fieldItems[5]['title']='Nachname';
145
+ $fieldItems[5]['new']=$new_register_lname;
146
+ $fieldItems[5]['prev']=$prev_register_lname;
147
+
148
+ $fieldItems[6]['title']='Firma';
149
+ $fieldItems[6]['new']=$new_register_company;
150
+ $fieldItems[6]['prev']=$prev_register_company;
151
+
152
+ $fieldItems[7]['title']='Strasse';
153
+ $fieldItems[7]['new']=$new_register_street;
154
+ $fieldItems[7]['prev']=$prev_register_street;
155
+
156
+ $fieldItems[8]['title']='Hausnr';
157
+ $fieldItems[8]['new']=$new_register_hnumber;
158
+ $fieldItems[8]['prev']=$prev_register_hnumber;
159
+
160
+ $fieldItems[9]['title']='PLZ';
161
+ $fieldItems[9]['new']=$new_register_zip;
162
+ $fieldItems[9]['prev']=$prev_register_zip;
163
+
164
+ $fieldItems[10]['title']='Stadt';
165
+ $fieldItems[10]['new']=$new_register_city;
166
+ $fieldItems[10]['prev']=$prev_register_city;
167
+
168
+ $fieldItems[11]['title']='Land';
169
+ $fieldItems[11]['new']=$new_register_country;
170
+ $fieldItems[11]['prev']=$prev_register_country;
171
+
172
+ $fieldItems[12]['title']='Tel-Nr';
173
+ $fieldItems[12]['new']=$new_register_telefon;
174
+ $fieldItems[12]['prev']=$prev_register_telefon;
175
+
176
+ $this->addLine($email_body,"<hr size=1 color=black>");
177
+
178
+ for($i=0; $i<count($fieldItems); $i++)
179
+ {
180
+ $item=$fieldItems[$i];
181
+
182
+ $hasChanged=($item['new']!=$item['prev']);
183
+
184
+ $line=$this->htmlencode($item['title']);
185
+ $line.=": ";
186
+ $line.=$this->htmlencode($item['new']);
187
+ $line.=" (";
188
+ if ($hasChanged)
189
+ {
190
+ $line.="<span style='color:red;font-weight:bold;'>";
191
+ }
192
+ $line.=$this->htmlencode($item['prev']);
193
+ if ($hasChanged)
194
+ {
195
+ $line.="</span>";
196
+ }
197
+ $line.=")";
198
+
199
+ $this->addLineHtml($email_body,$line);
200
+ }
201
+
202
+ // Send email:
203
+ $helper=$this->getHelper();
204
+ $sender_email=Mage::getStoreConfig('trans_email/ident_general/email');
205
+ $sender_name=Mage::getStoreConfig('trans_email/ident_general/name');
206
+ $recipient=$helper->getRegistrationRecipient();
207
+
208
+ $mail = new Zend_Mail('UTF-8');
209
+ $mail->setBodyHtml($email_body);
210
+ $mail->setFrom($sender_email,$sender_name);
211
+ $mail->addTo($recipient['email'],$recipient['name']);
212
+
213
+ if ($register_mailsent=="1")
214
+ {
215
+ $mail->setSubject("Update - Ein Magento translationMANAGER-Kunde hat seine Registrierungsdaten geändert");
216
+ }
217
+ else
218
+ {
219
+ $mail->setSubject("Magento translationMANAGER: Registrierung");
220
+ }
221
+ $mail->send();
222
+
223
+ // Registration done:
224
+ $helper->saveSetting("register_mailsent_date", date("d.m.Y H:i:s")." (".date_default_timezone_get().")");
225
+ $helper->saveSetting("register_mailsent", "1");
226
+
227
+ $url=Mage::helper('adminhtml')->getUrl('*/*/index',array('saved' => true));
228
+ $this->_redirectUrl($url);
229
+ }
230
+ }
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectcategoriesController.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Adminhtml_Eurotext_Translationmanager_SelectcategoriesController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ private function getTableName($tblName)
6
+ {
7
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
8
+ }
9
+
10
+ public function indexAction()
11
+ {
12
+ $this->loadLayout();
13
+ $this->renderLayout();
14
+ }
15
+
16
+ private function getAllChildCategoryIds($cat_id)
17
+ {
18
+ $rv=array();
19
+
20
+ array_push($rv,$cat_id);
21
+
22
+ $category=Mage::getModel('catalog/category')->load($cat_id);
23
+ $childs=$category->getChildrenCategories();
24
+ if ($childs!=null)
25
+ {
26
+ foreach($childs as $child)
27
+ {
28
+ $rvSub=$this->getAllChildCategoryIds($child->getId());
29
+
30
+ $rv=array_merge($rv,$rvSub);
31
+ }
32
+ }
33
+
34
+ $rv=array_unique($rv);
35
+
36
+ return $rv;
37
+ }
38
+
39
+ public function saveAction()
40
+ {
41
+
42
+ $this->loadLayout('adminhtml_eurotext_translationmanager_text');
43
+ $block = $this->getLayout()->getBlock('et.tm.response.text');
44
+
45
+ $project_id=intval($this->getRequest()->getParam("project_id"));
46
+ $cnt=intval($this->getRequest()->getParam("cnt"));
47
+ $selectAction=$this->getRequest()->getParam("select");
48
+
49
+ $dbres = Mage::getSingleton('core/resource');
50
+ $dbr=$dbres->getConnection('core_read');
51
+ $dbw=$dbres->getConnection('core_write');
52
+
53
+ for($i=0; $i<$cnt; $i++)
54
+ {
55
+ $category_id=$this->getRequest()->getParam("category_id_".$i);
56
+ $set=$this->getRequest()->getParam("set_".$i);
57
+
58
+ if ($category_id>0)
59
+ {
60
+ $cat_ids=$this->getAllChildCategoryIds($category_id);
61
+
62
+ foreach($cat_ids as $cat_id)
63
+ {
64
+ if ($set=="enabled")
65
+ {
66
+ $time_added=time();
67
+
68
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_project_categories')."` (category_id,project_id) VALUES (?,?);",array($cat_id,$project_id));
69
+
70
+ // Update timestamp:
71
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project_categories')."` SET time_added=? WHERE category_id=? AND project_id=?;",array($time_added,$cat_id,$project_id));
72
+ }
73
+ else
74
+ {
75
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_project_categories')."` WHERE category_id=? AND project_id=?;",array($cat_id,$project_id));
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ if ($selectAction=="all")
82
+ {
83
+ $time_added=time();
84
+
85
+ $allCategoryIds=$this->getAllCategoryIds();
86
+
87
+ foreach($allCategoryIds as $category_id)
88
+ {
89
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_project_categories')."` (category_id,project_id) VALUES (?,?);",array($category_id,$project_id));
90
+ }
91
+
92
+ // Update timestamp:
93
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project_categories')."` SET time_added=? WHERE project_id=?;",array($time_added,$project_id));
94
+ }
95
+ else if ($selectAction=="none")
96
+ {
97
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_project_categories')."` WHERE project_id=?;",array($project_id));
98
+ }
99
+
100
+ // get result list:
101
+ $eavAttribute = Mage::getModel('eav/entity_attribute');
102
+ $name_id = $eavAttribute->getIdByCode('catalog_category', 'name');
103
+
104
+ $sql="SELECT e.entity_id FROM `".$this->getTableName('catalog_category_entity_varchar')."` a, `".$this->getTableName('catalog_category_entity')."` e, `".$this->getTableName('eurotext_project_categories')."` p WHERE (a.store_id=0) AND (a.entity_id=e.entity_id) AND (a.attribute_id=?) AND (p.category_id=e.entity_id) AND (p.project_id=?) ORDER BY time_added DESC, a.value ASC, e.entity_id ASC";
105
+ $categories=$dbr->fetchAll($sql,array($name_id,$project_id));
106
+
107
+ $rv = '';
108
+
109
+ // Results:
110
+ $rv .= "<table cellpadding=0 cellspacing=0>";
111
+ $rv .= "<tr>";
112
+ $rv .= " <td class='et_th'>".$this->__("Translate")."</td>";
113
+ $rv .= " <td class='et_th'>".$this->__("Designation")."</td>";
114
+ $rv .= " <td class='et_th'>&nbsp;</td>";
115
+ $rv .= "</tr>";
116
+
117
+ foreach($categories as $category_id)
118
+ {
119
+ $category=Mage::getModel("catalog/category")->load($category_id);
120
+
121
+ $rv .= "<tr>";
122
+ $rv .= " <td class='et_tc'><input type='checkbox' id='et_selcategory2_".$category->getId()."' class='et_selcategory et_selcategory_".$category->getId()."' checked='checked' onchange=\"eurotext_selectcategory('2','".$category->getId()."')\" /></td>";
123
+ $rv .= " <td class='et_tc'>".$category->getName()."</td>";
124
+ $rv .= " <td class='et_tc'>&nbsp;</td>";
125
+ $rv .= "</tr>";
126
+ }
127
+
128
+ $rv .= "</table>";
129
+
130
+
131
+ $block->setText($rv);
132
+ $this->renderLayout();
133
+ }
134
+ }
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectcmspagesController.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Adminhtml_Eurotext_Translationmanager_SelectcmspagesController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ private function getTableName($tblName)
6
+ {
7
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
8
+ }
9
+
10
+ public function indexAction()
11
+ {
12
+ $this->loadLayout();
13
+ $this->renderLayout();
14
+ }
15
+
16
+ public function saveAction()
17
+ {
18
+ $this->loadLayout('adminhtml_eurotext_translationmanager_text');
19
+ $block = $this->getLayout()->getBlock('et.tm.response.text');
20
+
21
+
22
+ $rv = '';
23
+
24
+ $project_id=intval($this->getRequest()->getParam("project_id"));
25
+ $cnt_pages=intval($this->getRequest()->getParam("cnt_pages"));
26
+ $cnt_blocks=intval($this->getRequest()->getParam("cnt_blocks"));
27
+ $selectAction=$this->getRequest()->getParam("select");
28
+
29
+ $dbres = Mage::getSingleton('core/resource');
30
+ $dbr=$dbres->getConnection('core_read');
31
+ $dbw=$dbres->getConnection('core_write');
32
+
33
+ for($i=0; $i<$cnt_pages; $i++)
34
+ {
35
+ $page_id=intval($this->getRequest()->getParam("page_id_".$i));
36
+ $set=$this->getRequest()->getParam("setpage_".$i);
37
+
38
+ if ($page_id>0)
39
+ {
40
+ if ($set=="enabled")
41
+ {
42
+ $time_added=time();
43
+
44
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_project_cmspages')."` (page_id,project_id) VALUES (?,?);",array($page_id,$project_id));
45
+
46
+ // Update timestamp:
47
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project_cmspages')."` SET time_added=? WHERE page_id=? AND project_id=?;",array($time_added,$page_id,$project_id));
48
+ }
49
+ else
50
+ {
51
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_project_cmspages')."` WHERE page_id=? AND project_id=?;",array($page_id,$project_id));
52
+ }
53
+ }
54
+ }
55
+
56
+ for($i=0; $i<$cnt_blocks; $i++)
57
+ {
58
+ $block_id=intval($this->getRequest()->getParam("block_id_".$i));
59
+ $set=$this->getRequest()->getParam("setblock_".$i);
60
+
61
+ if ($block_id>0)
62
+ {
63
+ if ($set=="enabled")
64
+ {
65
+ $time_added=time();
66
+
67
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_project_cmsblocks')."` (block_id,project_id) VALUES (?,?);",array($block_id,$project_id));
68
+
69
+ // Update timestamp:
70
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project_cmsblocks')."` SET time_added=? WHERE block_id=? AND project_id=?;",array($time_added,$block_id,$project_id));
71
+ }
72
+ else
73
+ {
74
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_project_cmsblocks')."` WHERE block_id=? AND project_id=?;",array($block_id,$project_id));
75
+ }
76
+ }
77
+ }
78
+
79
+ if ($selectAction=="none")
80
+ {
81
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_project_cmspages')."` WHERE project_id=?;",array($project_id));
82
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_project_cmsblocks')."` WHERE project_id=?;",array($project_id));
83
+ }
84
+
85
+ // Results:
86
+ $rv .= "<table cellpadding=0 cellspacing=0>";
87
+ $rv .= "<tr>";
88
+ $rv .= " <td class='et_th'>".$this->__("Translate")."</td>";
89
+ $rv .= " <td class='et_th'>".$this->__("Designation")."</td>";
90
+ $rv .= " <td class='et_th'>&nbsp;</td>";
91
+ $rv .= "</tr>";
92
+
93
+ // get result list:
94
+ $sql="SELECT c.* FROM `".$this->getTableName('cms_page')."` c, `".$this->getTableName('eurotext_project_cmspages')."` p WHERE (c.page_id=p.page_id) AND (p.project_id=?) ORDER BY time_added DESC, c.title ASC, c.page_id ASC";
95
+ $cms_pages=$dbr->fetchAll($sql,array($project_id));
96
+ foreach($cms_pages as $cms_page)
97
+ {
98
+ $rv .= "<tr>";
99
+ $rv .= " <td class='et_tc'><input type='checkbox' id='et_selpage2_".$cms_page['page_id']."' class='et_selcmspage et_selcmspage_".$cms_page['page_id']."' checked='checked' onchange=\"eurotext_selectcmspage('2','".$cms_page['page_id']."')\" /></td>";
100
+ $rv .= " <td class='et_tc'>".$cms_page['title']."</td>";
101
+ $rv .= " <td class='et_tc'>&nbsp;</td>";
102
+ $rv .= "</tr>";
103
+ }
104
+
105
+ // get result list:
106
+ $sql="SELECT c.* FROM `".$this->getTableName('cms_block')."` c, `".$this->getTableName('eurotext_project_cmsblocks')."` p WHERE (c.block_id=p.block_id) AND (p.project_id=?) ORDER BY time_added DESC, c.title ASC, c.block_id ASC";
107
+ $cms_blocks=$dbr->fetchAll($sql,array($project_id));
108
+ foreach($cms_blocks as $cms_block)
109
+ {
110
+ $rv .= "<tr>";
111
+ $rv .= " <td class='et_tc'><input type='checkbox' id='et_selblock2_".$cms_block['block_id']."' class='et_selcmsblock et_selcmsblock_".$cms_block['block_id']."' checked='checked' onchange=\"eurotext_selectcmsblock('2','".$cms_block['block_id']."')\" /></td>";
112
+ $rv .= " <td class='et_tc'>".$cms_block['title']."</td>";
113
+ $rv .= " <td class='et_tc'>&nbsp;</td>";
114
+ $rv .= "</tr>";
115
+ }
116
+
117
+ $rv .= "</table>";
118
+
119
+ $block->setText($rv);
120
+ $this->renderLayout();
121
+
122
+ }
123
+ }
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectemailsController.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Adminhtml_Eurotext_Translationmanager_SelectemailsController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ private function getTableName($tblName)
6
+ {
7
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
8
+ }
9
+
10
+ public function indexAction()
11
+ {
12
+ $this->loadLayout();
13
+ $this->renderLayout();
14
+ }
15
+
16
+ public function saveAction()
17
+ {
18
+
19
+ $this->loadLayout('adminhtml_eurotext_translationmanager_text');
20
+ $block = $this->getLayout()->getBlock('et.tm.response.text');
21
+
22
+
23
+ $project_id=intval($this->getRequest()->getParam("project_id"));
24
+ $cnt=intval($this->getRequest()->getParam("cnt"));
25
+ $selectAction=$this->getRequest()->getParam("select");
26
+
27
+ $dbres = Mage::getSingleton('core/resource');
28
+ $dbr=$dbres->getConnection('core_read');
29
+ $dbw=$dbres->getConnection('core_write');
30
+
31
+ for($i=0; $i<$cnt; $i++)
32
+ {
33
+ $file_hash=$this->getRequest()->getParam("file_hash_".$i);
34
+ $set=$this->getRequest()->getParam("set_".$i);
35
+
36
+ if ($file_hash!="")
37
+ {
38
+ $dbw=$dbres->getConnection('core_write');
39
+
40
+ $translate_flag=0;
41
+
42
+ if ($set=="enabled")
43
+ {
44
+ $translate_flag=1;
45
+ }
46
+
47
+ $time_added=time();
48
+
49
+ // Update:
50
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_emailtemplates')."` SET translate_flag=?, time_added=? WHERE file_hash=? AND project_id=?;",array($translate_flag,$time_added,$file_hash,$project_id));
51
+ }
52
+ }
53
+
54
+ if ($selectAction=="none")
55
+ {
56
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_emailtemplates')."` SET translate_flag=0 WHERE project_id=?;",array($project_id));
57
+ }
58
+
59
+ // get result list:
60
+ $sql="SELECT * FROM `".$this->getTableName('eurotext_emailtemplates')."` WHERE project_id=? AND translate_flag=1 ORDER BY time_added DESC, filename ASC";
61
+ $emails=$dbr->fetchAll($sql,array($project_id));
62
+
63
+ $rv = '';
64
+
65
+ // Results:
66
+ $rv .= "<table cellpadding=0 cellspacing=0>";
67
+ $rv .= "<tr>";
68
+ $rv .= " <td class='et_th'>".$this->__("Translate")."</td>";
69
+ $rv .= " <td class='et_th'>".$this->__("Filename")."</td>";
70
+ $rv .= " <td class='et_th'>&nbsp;</td>";
71
+ $rv .= "</tr>";
72
+
73
+ foreach($emails as $email)
74
+ {
75
+ $rv .= "<tr>";
76
+ $rv .= " <td class='et_tc'><input type='checkbox' id='et_selemail2_".$email['file_hash']."' class='et_selemail et_selemail_".$email['file_hash']."' checked='checked' onchange=\"eurotext_selectemail('2','".$email['file_hash']."')\" /></td>";
77
+ $rv .= " <td class='et_tc'>".$email['filename']."</td>";
78
+ $rv .= " <td class='et_tc'>&nbsp;</td>";
79
+ $rv .= "</tr>";
80
+ }
81
+
82
+ $rv .= "</table>";
83
+
84
+ $block->setText($rv);
85
+ $this->renderLayout();
86
+
87
+ }
88
+ }
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectlangfilesController.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Adminhtml_Eurotext_Translationmanager_SelectlangfilesController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ private function getTableName($tblName)
6
+ {
7
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
8
+ }
9
+
10
+ public function indexAction()
11
+ {
12
+ $this->loadLayout();
13
+ $this->renderLayout();
14
+ }
15
+
16
+ public function saveAction()
17
+ {
18
+
19
+ $this->loadLayout('adminhtml_eurotext_translationmanager_text');
20
+ $block = $this->getLayout()->getBlock('et.tm.response.text');
21
+
22
+
23
+ $project_id=intval($this->getRequest()->getParam("project_id"));
24
+ $cnt=intval($this->getRequest()->getParam("cnt"));
25
+ $selectAction=$this->getRequest()->getParam("select");
26
+
27
+ $dbres = Mage::getSingleton('core/resource');
28
+ $dbr=$dbres->getConnection('core_read');
29
+ $dbw=$dbres->getConnection('core_write');
30
+
31
+ for($i=0; $i<$cnt; $i++)
32
+ {
33
+ $line_hash=$this->getRequest()->getParam("langfile_linehash_".$i);
34
+ $set=$this->getRequest()->getParam("set_".$i);
35
+
36
+ if ($line_hash!="")
37
+ {
38
+ $translate_flag=0;
39
+
40
+ if ($set=="enabled")
41
+ {
42
+ $translate_flag=1;
43
+ }
44
+
45
+ $time_added=time();
46
+
47
+ // Update:
48
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_csv')."` SET translate_flag=?, time_added=? WHERE line_hash=? AND project_id=?;",array($translate_flag,$time_added,$line_hash,$project_id));
49
+ }
50
+ }
51
+
52
+ if ($selectAction=="all")
53
+ {
54
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_csv')."` SET translate_flag=1 WHERE project_id=?;",array($project_id));
55
+ }
56
+ else if ($selectAction=="none")
57
+ {
58
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_csv')."` SET translate_flag=0 WHERE project_id=?;",array($project_id));
59
+ }
60
+
61
+ // get result list:
62
+ $sql="SELECT * FROM `".$this->getTableName('eurotext_csv')."` WHERE project_id=? AND translate_flag=1 ORDER BY time_added DESC, filename ASC";
63
+ $langfiles=$dbr->fetchAll($sql,array($project_id));
64
+
65
+ $rv = '';
66
+
67
+ // Results:
68
+ $rv .= "<table cellpadding=0 cellspacing=0>";
69
+ $rv .= "<tr>";
70
+ $rv .= " <td class='et_th'>".$this->__("Translate")."</td>";
71
+ $rv .= " <td class='et_th'>".$this->__("Filename")."</td>";
72
+ $rv .= " <td class='et_th'>&nbsp;</td>";
73
+ $rv .= "</tr>";
74
+
75
+ foreach($langfiles as $langfile)
76
+ {
77
+ $rv .= "<tr>";
78
+ $rv .= " <td class='et_tc'><input type='checkbox' id='et_sellangfile2_".$langfile['line_hash']."' class='et_sellangfile et_sellangfile_".$langfile['line_hash']."' checked='checked' onchange=\"eurotext_selectlangfile('2','".$langfile['line_hash']."')\" /></td>";
79
+ $rv .= " <td class='et_tc'>".$langfile['filename']."</td>";
80
+ $rv .= " <td class='et_tc'>&nbsp;</td>";
81
+ $rv .= "</tr>";
82
+ }
83
+
84
+ $rv .= "</table>";
85
+
86
+ $block->setText($rv);
87
+ $this->renderLayout();
88
+ }
89
+ }
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SelectproductsController.php ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Adminhtml_Eurotext_Translationmanager_SelectproductsController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ public function indexAction()
6
+ {
7
+ $this->loadLayout();
8
+ $this->renderLayout();
9
+ }
10
+
11
+ private function getTableName($tblName)
12
+ {
13
+ return Mage::getSingleton('core/resource')->getTableName($tblName);
14
+ }
15
+
16
+ public function getAllProductIds()
17
+ {
18
+ $dbres = Mage::getSingleton('core/resource');
19
+ $dbr=$dbres->getConnection('core_read');
20
+
21
+ $eavAttribute = Mage::getModel('eav/entity_attribute');
22
+ $name_id = $eavAttribute->getIdByCode('catalog_product', 'name');
23
+
24
+ $open_catids=$this->getOpenCatIds();
25
+ $sql_categoryfilter="";
26
+ if (count($open_catids)>0)
27
+ {
28
+ $selected_catid=$open_catids[count($open_catids)-1]; // Last ID is the selected category
29
+ if ($selected_catid>1) // root-category has id 1
30
+ {
31
+ $search_catids=Mage::helper('eurotext_translationmanager')->getAllSubCategories($selected_catid); // Get the IDs of all children (direct+indirect)
32
+ array_push($search_catids,$selected_catid); // add selected category to list
33
+
34
+ // Filter to products which are assigned to any category in $search_catids:
35
+ $sql_categoryfilter=" AND e.entity_id IN (SELECT cat.product_id FROM ".$this->getTableName('catalog_category_product')." cat WHERE cat.category_id IN (".implode(",",$search_catids)."))";
36
+ }
37
+ }
38
+
39
+ $sql="SELECT e.entity_id FROM `".$this->getTableName('catalog_product_entity')."` e WHERE (1=1)".$sql_categoryfilter;
40
+ $allProducts=$dbr->fetchAll($sql);
41
+
42
+ $productIds=array();
43
+ for($i=0; $i<count($allProducts); $i++)
44
+ {
45
+ array_push($productIds,$allProducts[$i]['entity_id']);
46
+ }
47
+
48
+ return $productIds;
49
+ }
50
+
51
+ public function getOpenCatIds()
52
+ {
53
+ // Parse catids (list of open categories)
54
+ $open_catids_str=$this->getRequest()->getParam("catids");
55
+
56
+ $open_catids_list=explode(",",$open_catids_str);
57
+ $open_catids=array();
58
+ foreach($open_catids_list as $opencatid)
59
+ {
60
+ $cat_id=intval($opencatid); // prevent non-numeric ids
61
+ array_push($open_catids,$cat_id);
62
+ }
63
+ array_push($open_catids,1); // root-category is always open
64
+ $open_catids=array_unique($open_catids);
65
+
66
+ return $open_catids;
67
+ }
68
+
69
+ public function saveAction()
70
+ {
71
+
72
+ $this->loadLayout('adminhtml_eurotext_translationmanager_ajax');
73
+ $block = $this->getLayout()->getBlock('et.tm.response.ajax');
74
+
75
+
76
+ $helper=Mage::helper('eurotext_translationmanager');
77
+
78
+ $project_id=intval($this->getRequest()->getParam("project_id"));
79
+ $cnt=intval($this->getRequest()->getParam("cnt"));
80
+ $selectAction=$this->getRequest()->getParam("select");
81
+ $catid=$this->getRequest()->getParam("catid");
82
+
83
+ $dbres = Mage::getSingleton('core/resource');
84
+ $dbr=$dbres->getConnection('core_read');
85
+ $dbw=$dbres->getConnection('core_write');
86
+ $time_added=time();
87
+
88
+ for($i=0; $i<$cnt; $i++)
89
+ {
90
+ $product_id=$this->getRequest()->getParam("product_id_".$i);
91
+ $set=$this->getRequest()->getParam("set_".$i);
92
+ if ($product_id>0)
93
+ {
94
+ if ($set=="enabled")
95
+ {
96
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_project_products')."` (product_id,project_id) VALUES (?,?);",array($product_id,$project_id));
97
+
98
+ // Update timestamp:
99
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project_products')."` SET time_added=? WHERE product_id=? AND project_id=?;",array($time_added,$product_id,$project_id));
100
+ }
101
+ else
102
+ {
103
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_project_products')."` WHERE product_id=? AND project_id=?;",array($product_id,$project_id));
104
+ }
105
+ }
106
+ }
107
+
108
+ if ($selectAction=="setcat")
109
+ {
110
+ $touched_product_ids=$helper->getCategoryProducts($dbr,$catid);
111
+
112
+ foreach($touched_product_ids as $product_id)
113
+ {
114
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_project_products')."` (product_id,project_id) VALUES (?,?);",array($product_id,$project_id));
115
+ // Update timestamp:
116
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project_products')."` SET time_added=? WHERE product_id=? AND project_id=?;",array($time_added,$product_id,$project_id));
117
+ }
118
+ }
119
+ else if ($selectAction=="unsetcat")
120
+ {
121
+ $touched_product_ids=$helper->getCategoryProducts($dbr,$catid);
122
+
123
+ foreach($touched_product_ids as $product_id)
124
+ {
125
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_project_products')."` WHERE product_id=? AND project_id=?;",array($product_id,$project_id));
126
+ }
127
+ }
128
+
129
+ if ($selectAction=="all")
130
+ {
131
+ $time_added=time();
132
+
133
+ $allProductIds=$this->getAllProductIds();
134
+
135
+ foreach($allProductIds as $product_id)
136
+ {
137
+ $dbw->query("INSERT IGNORE INTO `".$this->getTableName('eurotext_project_products')."` (product_id,project_id) VALUES (?,?);",array($product_id,$project_id));
138
+ }
139
+
140
+ // Update timestamp:
141
+ $dbw->query("UPDATE `".$this->getTableName('eurotext_project_products')."` SET time_added=? WHERE project_id=?;",array($time_added,$project_id));
142
+ }
143
+ else if ($selectAction=="none")
144
+ {
145
+ $allProductIds=$this->getAllProductIds();
146
+
147
+ foreach($allProductIds as $product_id)
148
+ {
149
+ $dbw->query("DELETE FROM `".$this->getTableName('eurotext_project_products')."` WHERE project_id=? AND product_id=?;",array($project_id,$product_id));
150
+ }
151
+ }
152
+
153
+ // get result list:
154
+ $eavAttribute = Mage::getModel('eav/entity_attribute');
155
+ $name_id = $eavAttribute->getIdByCode('catalog_product', 'name');
156
+
157
+ $sql="SELECT a.store_id, a.value as article_name, e.sku, e.entity_id FROM `".$this->getTableName('catalog_product_entity_varchar')."` a, `".$this->getTableName('catalog_product_entity')."` e, `".$this->getTableName('eurotext_project_products')."` p WHERE (a.entity_id=e.entity_id) AND (a.attribute_id=?) AND a.store_id=0 AND (p.product_id=e.entity_id) AND (p.project_id=?) ORDER BY time_added DESC, article_name ASC, e.entity_id ASC";
158
+ $products=$dbr->fetchAll($sql,array($name_id,$project_id));
159
+
160
+ $product_ids=array();
161
+
162
+ $returnBody="";
163
+
164
+ // Results:
165
+ $returnBody.="<table cellpadding=0 cellspacing=0>";
166
+ $returnBody.="<tr>";
167
+ $returnBody.=" <td class='et_th'>".$this->__("Translate")."</td>";
168
+ $returnBody.=" <td class='et_th'>".$this->__("SKU")."</td>";
169
+ $returnBody.=" <td class='et_th'>".$this->__("Designation")."</td>";
170
+ $returnBody.=" <td class='et_th'>&nbsp;</td>";
171
+ $returnBody.="</tr>";
172
+
173
+ $alt=0;
174
+
175
+ foreach($products as $product)
176
+ {
177
+ array_push($product_ids,$product['entity_id']);
178
+
179
+ $alt=1-$alt;
180
+ $returnBody.="<tr>";
181
+ $returnBody.=" <td class='et_tc eurotext2_r".$alt."'><input type='checkbox' id='et_selproduct2_".$product['entity_id']."' class='et_selproduct et_selproduct_".$product['entity_id']."' checked='checked' onchange=\"eurotext_selectproduct('2','".$product['entity_id']."')\" /></td>";
182
+ $returnBody.=" <td class='et_tc eurotext2_r".$alt."'>".$product['sku']."</td>"; // (ID: ".$product['entity_id'].")
183
+ $returnBody.=" <td class='et_tc eurotext2_r".$alt."'>".$product['article_name']."</td>";
184
+ $returnBody.=" <td class='et_tc eurotext2_r".$alt."'>&nbsp;</td>";
185
+ $returnBody.="</tr>";
186
+ }
187
+
188
+ $returnBody.="</table>";
189
+
190
+ // Determine category state: $categories
191
+ $open_catids=$this->getOpenCatIds();
192
+
193
+ $rootcat=$helper->getCategoryTree(1,$open_catids);
194
+ $categories=array();
195
+ $this->checkCategoryState($dbr,$rootcat,$project_id,$categories);
196
+
197
+ $block->setHtmldata($returnBody);
198
+ $block->setProducts($product_ids);
199
+ $block->setCategories($categories);
200
+ $block->setDebug($open_catids);
201
+
202
+ $this->getResponse()->setHeader('Content-type', 'application/json', true);
203
+ $this->renderLayout();
204
+
205
+ }
206
+
207
+ private function checkCategoryState($dbr,$cat,$project_id, &$categoryDict)
208
+ {
209
+ $categoryState=Mage::helper('eurotext_translationmanager')->getTreeNodeTranslationState($dbr,$cat['id'],$project_id);
210
+
211
+
212
+ $catitem=array();
213
+ $catitem['id']=$cat['id'];
214
+ $catitem['checked']=($categoryState=="checked");
215
+ $catitem['indeterminate']=($categoryState=="indeterminate");
216
+
217
+ array_push($categoryDict,$catitem);
218
+
219
+ foreach($cat['childs'] as $child)
220
+ {
221
+ $this->checkCategoryState($dbr,$child,$project_id,$categoryDict);
222
+ }
223
+ }
224
+ }
app/code/community/Eurotext/TranslationManager/controllers/Adminhtml/Eurotext/Translationmanager/SettingsController.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Eurotext_TranslationManager_Adminhtml_Eurotext_Translationmanager_SettingsController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+
6
+
7
+ protected function _isAllowed()
8
+ {
9
+ return Mage::getSingleton('admin/session')
10
+ ->isAllowed('eurotext_translationmanager/settings');
11
+ }
12
+
13
+
14
+ public function indexAction()
15
+ {
16
+ $this->loadLayout();
17
+ $this->renderLayout();
18
+ }
19
+
20
+ public function upgradescopeAction()
21
+ {
22
+ $is_global=0;
23
+
24
+ $tbl_eav_attribute = Mage::getSingleton('core/resource')->getTableName('eav_attribute');
25
+ $tbl_catalog_eav_attribute = Mage::getSingleton('core/resource')->getTableName('catalog_eav_attribute');
26
+
27
+ $dbres = Mage::getSingleton('core/resource');
28
+ $dbr=$dbres->getConnection('core_read');
29
+ $dbw=$dbres->getConnection('core_write');
30
+
31
+ $result=$dbr->fetchAll("SELECT v.is_global, a.attribute_id, a.attribute_code FROM `".$tbl_eav_attribute."` a, `".$tbl_catalog_eav_attribute."` v WHERE (v.is_global!=".$is_global.") AND (a.attribute_id=v.attribute_id) AND ((a.attribute_code='url_key') OR (a.attribute_code='url_path'))");
32
+ foreach($result as $row)
33
+ {
34
+ $dbw->query("UPDATE `".$tbl_catalog_eav_attribute."` SET is_global=".$is_global." WHERE attribute_id=".$row['attribute_id']);
35
+ }
36
+
37
+ $url=Mage::helper('adminhtml')->getUrl('*/*/index');
38
+ $this->_redirectUrl($url);
39
+ }
40
+
41
+ public function saveAction()
42
+ {
43
+ $helper=Mage::helper('eurotext_translationmanager');
44
+ $request=$this->getRequest();
45
+
46
+ $helper->saveSetting("eurotext_username",$request->getParam("username"));
47
+ $helper->saveSetting("eurotext_password",Mage::helper('core')->encrypt($request->getParam("password")));
48
+ $helper->saveSetting("eurotext_customerid",$request->getParam("customerid"));
49
+
50
+ $et_products_per_file=intval($request->getParam("et_products_per_file"));
51
+ if ($et_products_per_file<$helper->getExportProductsMinPerFile())
52
+ {
53
+ $et_products_per_file=$helper->getExportProductsMinPerFile();
54
+ }
55
+
56
+ $et_categories_per_file=intval($request->getParam("et_categories_per_file"));
57
+ if ($et_categories_per_file<$helper->getExportCategoriesMinPerFile())
58
+ {
59
+ $et_categories_per_file=$helper->getExportCategoriesMinPerFile();
60
+ }
61
+
62
+ $et_cmspages_per_file=intval($request->getParam("et_cmspages_per_file"));
63
+ if ($et_cmspages_per_file<1)
64
+ {
65
+ $et_cmspages_per_file=20;
66
+ }
67
+
68
+ $helper->saveSetting("et_products_per_file",$et_products_per_file);
69
+ $helper->saveSetting("et_categories_per_file",$et_categories_per_file);
70
+ $helper->saveSetting("et_cmspages_per_file",$et_cmspages_per_file);
71
+ }
72
+ }
app/code/community/Eurotext/TranslationManager/etc/adminhtml.xml ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <menu>
4
+ <eurotext_translationmanager module="eurotext_translationmanager">
5
+ <title>translationMANAGER</title>
6
+ <sort_order>70</sort_order>
7
+ <children>
8
+ <register translate="title" module="eurotext_translationmanager">
9
+ <title>Registration</title>
10
+ <sort_order>10</sort_order>
11
+ <action>adminhtml/eurotext_translationmanager_register</action>
12
+ </register>
13
+ <settings translate="title" module="eurotext_translationmanager">
14
+ <title>Settings</title>
15
+ <sort_order>20</sort_order>
16
+ <action>adminhtml/eurotext_translationmanager_settings</action>
17
+ </settings>
18
+ <export translate="title" module="eurotext_translationmanager">
19
+ <title>Projects</title>
20
+ <sort_order>30</sort_order>
21
+ <action>adminhtml/eurotext_translationmanager_projects</action>
22
+ </export>
23
+ <help translate="title" module="eurotext_translationmanager">
24
+ <title>Help</title>
25
+ <sort_order>40</sort_order>
26
+ <action>adminhtml/eurotext_translationmanager_help</action>
27
+ </help>
28
+ </children>
29
+ </eurotext_translationmanager>
30
+ </menu>
31
+ <acl>
32
+ <resources>
33
+ <admin>
34
+ <children>
35
+ <eurotext_translationmanager module="eurotext_translationmanager">
36
+ <title>Eurotext - translationMANAGER</title>
37
+ <children>
38
+ <register translate="title" module="eurotext_translationmanager">
39
+ <title>Registration</title>
40
+ <sort_order>10</sort_order>
41
+ </register>
42
+ <settings translate="title" module="eurotext_translationmanager">
43
+ <title>Settings</title>
44
+ <sort_order>20</sort_order>
45
+ </settings>
46
+ <export translate="title" module="eurotext_translationmanager">
47
+ <title>Projects</title>
48
+ <sort_order>30</sort_order>
49
+ </export>
50
+ <help translate="title" module="eurotext_translationmanager">
51
+ <title>Help</title>
52
+ <sort_order>40</sort_order>
53
+ </help>
54
+ </children>
55
+ </eurotext_translationmanager>
56
+ </children>
57
+ </admin>
58
+ </resources>
59
+ </acl>
60
+ </config>
app/code/community/Eurotext/TranslationManager/etc/config.xml ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Eurotext_TranslationManager>
5
+ <version>1.1.2.2</version>
6
+ </Eurotext_TranslationManager>
7
+ </modules>
8
+ <global>
9
+ <models>
10
+ <eurotext_translationmanager>
11
+ <class>Eurotext_TranslationManager_Model</class>
12
+ </eurotext_translationmanager>
13
+ </models>
14
+ <helpers>
15
+ <eurotext_translationmanager>
16
+ <class>Eurotext_TranslationManager_Helper</class>
17
+ </eurotext_translationmanager>
18
+ </helpers>
19
+ <models>
20
+ <eurotext_translationmanager>
21
+ <class>Eurotext_TranslationManager_Model</class>
22
+ <resourceModel>eurotext_translationmanager_resource</resourceModel>
23
+ </eurotext_translationmanager>
24
+ <eurotext_translationmanager_resource>
25
+ <class>Eurotext_TranslationManager_Model_Resource</class>
26
+ <entities>
27
+ <project>
28
+ <table>eurotext_project</table>
29
+ </project>
30
+ </entities>
31
+ </eurotext_translationmanager_resource>
32
+ </models>
33
+ <resources>
34
+ <eurotext_translationmanager_setup>
35
+ <setup>
36
+ <module>Eurotext_TranslationManager</module>
37
+ <class>Eurotext_TranslationManager_Model_Resource_Setup</class>
38
+ </setup>
39
+ <connection>
40
+ <use>core_setup</use>
41
+ </connection>
42
+ </eurotext_translationmanager_setup>
43
+ </resources>
44
+ <blocks>
45
+ <eurotext_translationmanager>
46
+ <class>Eurotext_TranslationManager_Block</class>
47
+ </eurotext_translationmanager>
48
+ </blocks>
49
+ </global>
50
+ <admin>
51
+ <routers>
52
+ <adminhtml>
53
+ <args>
54
+ <modules>
55
+ <eurotext_translationmanager before="Mage_Adminhtml">Eurotext_TranslationManager_Adminhtml</eurotext_translationmanager>
56
+ </modules>
57
+ </args>
58
+ </adminhtml>
59
+ </routers>
60
+ </admin>
61
+ <adminhtml>
62
+ <translate>
63
+ <modules>
64
+ <Eurotext_TranslationManager>
65
+ <files>
66
+ <default>Eurotext_TranslationManager.csv</default>
67
+ </files>
68
+ </Eurotext_TranslationManager>
69
+ </modules>
70
+ </translate>
71
+ <layout>
72
+ <updates>
73
+ <eurotext_translationmanager>
74
+ <file>eurotext/translationmanager.xml</file>
75
+ </eurotext_translationmanager>
76
+ </updates>
77
+ </layout>
78
+ </adminhtml>
79
+ </config>
app/code/community/Eurotext/TranslationManager/etc/jstranslator.xml ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <jstranslator>
3
+ <msg1 translate="message" module="eurotext_translationmanager">
4
+ <message>Processing - please wait …</message>
5
+ </msg1>
6
+ <msg2 translate="message" module="eurotext_translationmanager">
7
+ <message>Loading - please wait …</message>
8
+ </msg2>
9
+ <msg3 translate="message" module="eurotext_translationmanager">
10
+ <message>Please wait - this may take a while</message>
11
+ </msg3>
12
+ <msg4 translate="message" module="eurotext_translationmanager">
13
+ <message>Please wait - saving project settings</message>
14
+ </msg4>
15
+ <msg5 translate="message" module="eurotext_translationmanager">
16
+ <message>Please wait</message>
17
+ </msg5>
18
+ <msg6 translate="message" module="eurotext_translationmanager">
19
+ <message>Unable to save project settings</message>
20
+ </msg6>
21
+ <msg7 translate="message" module="eurotext_translationmanager">
22
+ <message>Save project</message>
23
+ </msg7>
24
+ <msg8 translate="message" module="eurotext_translationmanager">
25
+ <message>Try again</message>
26
+ </msg8>
27
+ <msg9 translate="message" module="eurotext_translationmanager">
28
+ <message>No file was selected, or the file selected is not a ZIP file</message>
29
+ </msg9>
30
+ <msg10 translate="message" module="eurotext_translationmanager">
31
+ <message>Do you really want to delete this project?</message>
32
+ </msg10>
33
+ <msg11 translate="message" module="eurotext_translationmanager">
34
+ <message>Do you really want to reset this project?</message>
35
+ </msg11>
36
+ </jstranslator>
app/code/community/Eurotext/TranslationManager/etc/system.xml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <dev>
5
+ <groups>
6
+ <log>
7
+ <fields>
8
+ <ettm_head>
9
+ <frontend_model>adminhtml/system_config_form_field_heading</frontend_model>
10
+ <label>Eurotext translationMANAGER</label>
11
+ <show_in_default>1</show_in_default>
12
+ <show_in_website>0</show_in_website>
13
+ <show_in_store>0</show_in_store>
14
+ <sort_order>100</sort_order>
15
+ </ettm_head>
16
+ <ettm_debug translate="comment label">
17
+ <frontend_type>select</frontend_type>
18
+ <label>Debug Mode</label>
19
+ <comment>Enabling this option writes an eurotext.log file into {{base_dir}}/var/log</comment>
20
+ <source_model>adminhtml/system_config_source_enabledisable</source_model>
21
+ <show_in_default>1</show_in_default>
22
+ <show_in_website>0</show_in_website>
23
+ <show_in_store>0</show_in_store>
24
+ <sort_order>110</sort_order>
25
+ </ettm_debug>
26
+ </fields>
27
+ </log>
28
+
29
+ </groups>
30
+ </dev>
31
+ </sections>
32
+ </config>
app/code/community/Eurotext/TranslationManager/sql/eurotext_translationmanager_setup/mysql4-install-1.0.0.0.php ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /** @var Mage_Core_Model_Resource_Setup $installer */
4
+
5
+ $installer = $this;
6
+ $installer->startSetup();
7
+
8
+ try {
9
+ $sql = "";
10
+ $sql .= "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_config") . "` (";
11
+ $sql .= " `config_key` varchar(255) NOT NULL,";
12
+ $sql .= " `config_value` text NOT NULL,";
13
+ $sql .= " PRIMARY KEY (`config_key`)";
14
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
15
+ $installer->run($sql);
16
+ } catch (Exception $e) {
17
+ //void if table exists
18
+ }
19
+
20
+ try {
21
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_csv") . "` (";
22
+ $sql .= " `line_hash` varchar(60) NOT NULL,";
23
+ $sql .= " `project_id` bigint(20) NOT NULL,";
24
+ $sql .= " `filename` varchar(255) NOT NULL,";
25
+ $sql .= " `locale_dst` varchar(50) NOT NULL,";
26
+ $sql .= " `translate_flag` smallint(6) NOT NULL DEFAULT '0',";
27
+ $sql .= " `time_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,";
28
+ $sql .= " `deleteflag` smallint(6) NOT NULL DEFAULT '0',";
29
+ $sql .= " PRIMARY KEY (`line_hash`),";
30
+ $sql .= " KEY `idx_filename` (`filename`),";
31
+ $sql .= " KEY `idx_project_id` (`project_id`),";
32
+ $sql .= " KEY `idx_locale_dst` (`locale_dst`)";
33
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
34
+ $installer->run($sql);
35
+ } catch (Exception $e) {
36
+ //void if table exists
37
+ }
38
+
39
+ try {
40
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_csv_data") . "` (";
41
+ $sql .= " `line_hash` varchar(60) NOT NULL,";
42
+ $sql .= " `project_id` bigint(20) NOT NULL,";
43
+ $sql .= " `filename` varchar(255) NOT NULL,";
44
+ $sql .= " `csvline` int(11) NOT NULL DEFAULT '-1',";
45
+ $sql .= " `locale_dst` varchar(50) NOT NULL,";
46
+ $sql .= " `text_src` varchar(5000) NOT NULL,";
47
+ $sql .= " `text_src_hash` varchar(100) NOT NULL DEFAULT '',";
48
+ $sql .= " `text_dst` varchar(5000) NOT NULL,";
49
+ $sql .= " PRIMARY KEY (`line_hash`),";
50
+ $sql .= " KEY `project_id` (`project_id`),";
51
+ $sql .= " KEY `filename` (`filename`),";
52
+ $sql .= " KEY `text_src_hash` (`text_src_hash`),";
53
+ $sql .= " KEY `locale_dst` (`locale_dst`)";
54
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
55
+ $installer->run($sql);
56
+ } catch
57
+ (Exception $e) {
58
+ //void if table exists
59
+ }
60
+
61
+ try {
62
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_emailtemplates") . "` (";
63
+ $sql .= " `file_hash` varchar(150) NOT NULL,";
64
+ $sql .= " `filename` varchar(1500) NOT NULL,";
65
+ $sql .= " `translate_flag` smallint(6) NOT NULL DEFAULT '0',";
66
+ $sql .= " `project_id` bigint(20) NOT NULL DEFAULT '-1',";
67
+ $sql .= " `deleteflag` smallint(6) NOT NULL DEFAULT '0',";
68
+ $sql .= " `locale_dst` varchar(50) NOT NULL,";
69
+ $sql .= " `time_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,";
70
+ $sql .= " PRIMARY KEY (`file_hash`)";
71
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
72
+ $installer->run($sql);
73
+ } catch (Exception $e) {
74
+ //void if table exists
75
+ }
76
+
77
+ try {
78
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_import") . "` (";
79
+ $sql .= " `project_id` bigint(20) NOT NULL,";
80
+ $sql .= " `filename` varchar(255) NOT NULL,";
81
+ $sql .= " `storeview_dst` int(11) NOT NULL,";
82
+ $sql .= " `num` int(11) NOT NULL DEFAULT '-1',";
83
+ $sql .= " `is_imported` smallint(6) NOT NULL DEFAULT '0',";
84
+ $sql .= " UNIQUE KEY `pk` (`project_id`,`filename`)";
85
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
86
+ $installer->run($sql);
87
+ } catch (Exception $e) {
88
+ //void if table exists
89
+ }
90
+
91
+ try {
92
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_languages") . "` (";
93
+ $sql .= " `locale_magento` varchar(20) CHARACTER SET utf8 NOT NULL,";
94
+ $sql .= " `locale_eurotext` varchar(100) CHARACTER SET utf8 NOT NULL,";
95
+ $sql .= " `lang_name` varchar(200) CHARACTER SET utf8 NOT NULL,";
96
+ $sql .= " PRIMARY KEY (`locale_eurotext`)";
97
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
98
+ $installer->run($sql);
99
+ } catch (Exception $e) {
100
+ //void if table exists
101
+ }
102
+
103
+ try {
104
+ $sql = "INSERT IGNORE INTO `" . $installer->getTable("eurotext_languages") . "` (`locale_magento`, `locale_eurotext`, `lang_name`) VALUES";
105
+ $sql .= " ('af_ZA', 'afr', 'Afrikaans'),";
106
+ $sql .= " ('sq_AL', 'alb', 'Albanian'),";
107
+ $sql .= " ('ar_DZ', 'ar-dz', 'Arabic (Algeria)'),";
108
+ $sql .= " ('ar_EG', 'ar-eg', 'Arabic (Egypt)'),";
109
+ $sql .= " ('ar_KW', 'ar-kw', 'Arabic (Kuwait)'),";
110
+ $sql .= " ('ar_MA', 'ar-ma', 'Arabic (Morocco)'),";
111
+ $sql .= " ('ar_SA', 'ar-sa', 'Arabic (Saudi Arabia)'),";
112
+ $sql .= " ('az_AZ', 'aze', 'Azerbaijani (Latin)'),";
113
+ $sql .= " ('be_BY', 'bel', 'Byelorussian'),";
114
+ $sql .= " ('bg_BG', 'bg', 'Bulgarian'),";
115
+ $sql .= " ('bs_BA', 'bos', 'Bosnian'),";
116
+ $sql .= " ('ca_ES', 'cat', 'Catalan'),";
117
+ $sql .= " ('cs_CZ', 'cz-cz', 'Czech'),";
118
+ $sql .= " ('da_DK', 'da', 'Danish'),";
119
+ $sql .= " ('de_AT', 'de-at', 'German (AT)'),";
120
+ $sql .= " ('de_CH', 'de-ch', 'German (CH)'),";
121
+ $sql .= " ('de_DE', 'de-de', 'German (DE)'),";
122
+ $sql .= " ('el_GR', 'el', 'Greek'),";
123
+ $sql .= " ('en_AU', 'en-au', 'English (AU)'),";
124
+ $sql .= " ('en_CA', 'en-ca', 'English (CA)'),";
125
+ $sql .= " ('en_GB', 'en-gb', 'English (GB)'),";
126
+ $sql .= " ('en_IE', 'en-ie', 'English (IE)'),";
127
+ $sql .= " ('en_NZ', 'en-nz', 'English (NZ)'),";
128
+ $sql .= " ('en_US', 'en-us', 'English (US)'),";
129
+ $sql .= " ('es_AR', 'es-ar', 'Spanish (Argentina)'),";
130
+ $sql .= " ('es_CL', 'es-cl', 'Spanish (Chile)'),";
131
+ $sql .= " ('es_CO', 'es-co', 'Spanish (Colombia)'),";
132
+ $sql .= " ('es_CR', 'es-cr', 'Spanish (Costa Rica)'),";
133
+ $sql .= " ('es_ES', 'es-es', 'Spanish (ES)'),";
134
+ $sql .= " ('es_MX', 'es-mx', 'Spanish (Mexico)'),";
135
+ $sql .= " ('es_PA', 'es-pa', 'Spanish (Panama)'),";
136
+ $sql .= " ('es_PE', 'es-pe', 'Spanish (Peru)'),";
137
+ $sql .= " ('es_VE', 'es-ve', 'Spanish (Venezuela)'),";
138
+ $sql .= " ('et_EE', 'et', 'Estonian'),";
139
+ $sql .= " ('fi_FI', 'fi-fi', 'Finnish'),";
140
+ $sql .= " ('fr_CA', 'fr-ca', 'French (CA)'),";
141
+ $sql .= " ('fr_FR', 'fr-fr', 'French (FR)'),";
142
+ $sql .= " ('gl_ES', 'glg', 'Galician'),";
143
+ $sql .= " ('gu_IN', 'guj', 'Gujarati'),";
144
+ $sql .= " ('he_IL', 'he', 'Hebrew'),";
145
+ $sql .= " ('hi_IN', 'hin', 'Hindi'),";
146
+ $sql .= " ('hr_HR', 'hr', 'Croatian'),";
147
+ $sql .= " ('hu_HU', 'hu', 'Hungarian'),";
148
+ $sql .= " ('is_IS', 'ice', 'Icelandic'),";
149
+ $sql .= " ('id_ID', 'ind', 'Indonesian'),";
150
+ $sql .= " ('it_CH', 'it-ch', 'Italy (CH)'),";
151
+ $sql .= " ('it_IT', 'it-it', 'Italian (IT)'),";
152
+ $sql .= " ('ja_JP', 'ja', 'Japanese'),";
153
+ $sql .= " ('ko_KR', 'ko-kr', 'Korean'),";
154
+ $sql .= " ('lt_LT', 'lt-lt', 'Lithuanian'),";
155
+ $sql .= " ('lv_LV', 'lv', 'Latvian'),";
156
+ $sql .= " ('mk_MK', 'mk', 'Mecedonian'),";
157
+ $sql .= " ('ms_MY', 'msa', 'Malay'),";
158
+ $sql .= " ('nl_NL', 'nl-nl', 'Dutch (NL)'),";
159
+ $sql .= " ('nb_NO', 'no-nb', 'Norwegian (Bokmål)'),";
160
+ $sql .= " ('nn_NO', 'no-nn', 'Norwegian (Nynorsk)'),";
161
+ $sql .= " ('pl_PL', 'pl', 'Polish'),";
162
+ $sql .= " ('pt_BR', 'pt-br', 'Portuguese (BR)'),";
163
+ $sql .= " ('pt_PT', 'pt-pt', 'Portuguese (PT)'),";
164
+ $sql .= " ('ro_RO', 'ro-ro', 'Romanian'),";
165
+ $sql .= " ('ru_RU', 'ru-ru', 'Russian'),";
166
+ $sql .= " ('sk_SK', 'sk', 'Slovak'),";
167
+ $sql .= " ('sl_SI', 'sl', 'Slovenian'),";
168
+ $sql .= " ('sr_RS', 'sr', 'Serbian (Latin)'),";
169
+ $sql .= " ('sv_SE', 'sv-se', 'Swedish'),";
170
+ $sql .= " ('th_TH', 'th', 'Thai'),";
171
+ $sql .= " ('tr_TR', 'tr', 'Turkish'),";
172
+ $sql .= " ('uk_UA', 'uk', 'Ukrainian'),";
173
+ $sql .= " ('vi_VN', 'vn', 'Vietnamese'),";
174
+ $sql .= " ('cy_GB', 'wel', 'Welsh'),";
175
+ $sql .= " ('zh_CN', 'zh-cn', 'Chinese (PRC)'),";
176
+ $sql .= " ('zh_HK', 'zh-hk', 'Chinese (Hong Kong S.A.R.)'),";
177
+ $sql .= " ('zh_TW', 'zh-tw', 'Chinese (Taiwan)');";
178
+ $installer->run($sql);
179
+ } catch (Exception $e) {
180
+ //void if table exists
181
+ }
182
+
183
+ try {
184
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_project") . "` (";
185
+ $sql .= " `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',";
186
+ $sql .= " `deleted` smallint(6) NOT NULL DEFAULT '0',";
187
+ $sql .= " `create_id` varchar(100) DEFAULT NULL,";
188
+ $sql .= " `project_name` text NOT NULL COMMENT 'Project Name',";
189
+ $sql .= " `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Last Update',";
190
+ $sql .= " `storeview_src` int(11) NOT NULL DEFAULT '-1' COMMENT 'Source Storeview',";
191
+ $sql .= " `storeview_dst` int(11) NOT NULL DEFAULT '-1' COMMENT 'Destination Storeview',";
192
+ $sql .= " `project_status` int(11) NOT NULL DEFAULT '0' COMMENT 'Status',";
193
+ $sql .= " `langfilesmode` smallint(6) NOT NULL DEFAULT '0',";
194
+ $sql .= " `export_seo` smallint(6) NOT NULL DEFAULT '1',";
195
+ $sql .= " `productmode` smallint(6) NOT NULL DEFAULT '0' COMMENT '0=Selected Products, 1=Products without translation',";
196
+ $sql .= " `categorymode` smallint(6) NOT NULL DEFAULT '0' COMMENT '0=Selected Categories, 1=Categories without translation',";
197
+ $sql .= " `cmsmode` smallint(6) NOT NULL DEFAULT '0' COMMENT '0=Selected CMS-Pages, 1 = Missing CMS-Pages',";
198
+ $sql .= " `zip_filename` varchar(255) NOT NULL DEFAULT '',";
199
+ $sql .= " `export_attributes` smallint(6) NOT NULL DEFAULT '0',";
200
+ $sql .= " `templatemode` smallint(6) NOT NULL COMMENT '0',";
201
+ $sql .= " `export_urlkeys` smallint(6) NOT NULL DEFAULT '0',";
202
+ $sql .= " PRIMARY KEY (`id`)";
203
+ $sql .= ") ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='eurotext_project';";
204
+ $installer->run($sql);
205
+ } catch (Exception $e) {
206
+ //void if table exists
207
+ }
208
+
209
+ try {
210
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_project_categories") . "` (";
211
+ $sql .= " `category_id` bigint(20) NOT NULL,";
212
+ $sql .= " `project_id` bigint(20) NOT NULL,";
213
+ $sql .= " `time_added` bigint(20) NOT NULL DEFAULT '0',";
214
+ $sql .= " PRIMARY KEY (`category_id`,`project_id`)";
215
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
216
+ $installer->run($sql);
217
+ } catch (Exception $e) {
218
+ //void if table exists
219
+ }
220
+
221
+ try {
222
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_project_cmspages") . "` (";
223
+ $sql .= " `page_id` bigint(20) NOT NULL,";
224
+ $sql .= " `project_id` bigint(20) NOT NULL,";
225
+ $sql .= " `time_added` bigint(20) NOT NULL DEFAULT '0',";
226
+ $sql .= " PRIMARY KEY (`page_id`,`project_id`)";
227
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
228
+ $installer->run($sql);
229
+ } catch (Exception $e) {
230
+ //void if table exists
231
+ }
232
+
233
+ try {
234
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_project_products") . "` (";
235
+ $sql .= " `product_id` bigint(20) NOT NULL,";
236
+ $sql .= " `project_id` bigint(20) NOT NULL,";
237
+ $sql .= " `time_added` bigint(20) NOT NULL DEFAULT '0',";
238
+ $sql .= " PRIMARY KEY (`product_id`,`project_id`)";
239
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
240
+ $installer->run($sql);
241
+ } catch (Exception $e) {
242
+ //void if table exists
243
+ }
244
+
245
+ $installer->endSetup();
246
+
app/code/community/Eurotext/TranslationManager/sql/eurotext_translationmanager_setup/mysql4-upgrade-1.0.0.5-1.0.0.6.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ $installer->startSetup();
5
+
6
+ try {
7
+ $sql = "CREATE TABLE IF NOT EXISTS `" . $installer->getTable("eurotext_project_cmsblocks") . "` (";
8
+ $sql .= " `block_id` bigint(20) NOT NULL,";
9
+ $sql .= " `project_id` bigint(20) NOT NULL,";
10
+ $sql .= " `time_added` bigint(20) NOT NULL DEFAULT '0',";
11
+ $sql .= " PRIMARY KEY (`block_id`,`project_id`)";
12
+ $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
13
+
14
+ $installer->run($sql);
15
+ } catch (Exception $e) {
16
+
17
+ }
18
+
19
+ try {
20
+ $installer->run("ALTER TABLE `" . $installer->getTable("eurotext_project") . "` ADD `filter_status` INT NOT NULL DEFAULT '1' AFTER `export_urlkeys`");
21
+ } catch (Exception $e) {
22
+ // Column already exists
23
+ }
24
+
25
+ try {
26
+ $installer->run("ALTER TABLE `" . $installer->getTable("eurotext_project") . "` ADD `filter_stock` INT NOT NULL DEFAULT '1' AFTER `filter_status`");
27
+ } catch (Exception $e) {
28
+ // Column already exists
29
+ }
30
+
31
+ $installer->endSetup();
32
+
app/code/community/Eurotext/TranslationManager/sql/eurotext_translationmanager_setup/mysql4-upgrade-1.0.0.7-1.0.0.8.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $installer = $this;
4
+ $installer->startSetup();
5
+
6
+ try {
7
+ $sql = "ALTER TABLE `" . $installer->getTable("eurotext_project") . "` ADD `filter_product_type` VARCHAR( 150 ) NOT NULL DEFAULT '' AFTER `filter_stock`;";
8
+ $installer->run($sql);
9
+ } catch (Exception $ex) {
10
+ // Column already exists
11
+ }
12
+
13
+ $installer->endSetup();
14
+
app/design/adminhtml/base/default/layout/eurotext/translationmanager.xml ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout>
3
+
4
+ <eurotext_prepare_popup>
5
+ <reference name="root">
6
+ <action method="setTemplate">
7
+ <template>popup.phtml</template>
8
+ </action>
9
+ </reference>
10
+ <remove name="header"/>
11
+ <remove name="footer"/>
12
+ </eurotext_prepare_popup>
13
+
14
+ <eurotext_add_css>
15
+ <reference name="head">
16
+ <action method="addItem">
17
+ <type>skin_css</type>
18
+ <name>eurotext/translationmanager/css/styles.css</name>
19
+ <params/>
20
+ </action>
21
+ <action method="addItem">
22
+ <type>skin_js</type>
23
+ <name>eurotext/translationmanager/js/eurotext-jquery-1.9.1.js</name>
24
+ <params/>
25
+ </action>
26
+ <action method="addItem">
27
+ <type>skin_js</type>
28
+ <name>eurotext/translationmanager/js/eurotext.js</name>
29
+ <params/>
30
+ </action>
31
+ </reference>
32
+ </eurotext_add_css>
33
+
34
+ <!--
35
+
36
+ LAYOUT AREA FOR VISIBLE BACKEND PAGES
37
+
38
+ -->
39
+ <adminhtml_eurotext_translationmanager_register_index>
40
+ <update handle="eurotext_add_css"/>
41
+ <reference name="content">
42
+ <block type="eurotext_translationmanager/register" name="et.tm.register"
43
+ template="eurotext/translationmanager/register.phtml"/>
44
+ </reference>
45
+ </adminhtml_eurotext_translationmanager_register_index>
46
+
47
+ <adminhtml_eurotext_translationmanager_settings_index>
48
+ <update handle="eurotext_add_css"/>
49
+ <reference name="content">
50
+ <block type="eurotext_translationmanager/settings" name="et.tm.settings"
51
+ template="eurotext/translationmanager/settings.phtml"/>
52
+ </reference>
53
+ </adminhtml_eurotext_translationmanager_settings_index>
54
+
55
+ <adminhtml_eurotext_translationmanager_projects_index>
56
+ <update handle="eurotext_add_css"/>
57
+ <reference name="content">
58
+ <block type="eurotext_translationmanager/projects" name="et.tm.projects"
59
+ template="eurotext/translationmanager/projects.phtml"/>
60
+ </reference>
61
+ </adminhtml_eurotext_translationmanager_projects_index>
62
+
63
+ <adminhtml_eurotext_translationmanager_help_index>
64
+ <update handle="eurotext_add_css"/>
65
+ <reference name="content">
66
+ <block type="eurotext_translationmanager/help" name="et.tm.help"
67
+ template="eurotext/translationmanager/help.phtml"/>
68
+ </reference>
69
+ </adminhtml_eurotext_translationmanager_help_index>
70
+
71
+ <adminhtml_eurotext_translationmanager_selectproducts_index>
72
+ <update handle="eurotext_add_css"/>
73
+ <update handle="eurotext_prepare_popup"/>
74
+ <reference name="content">
75
+ <block type="eurotext_translationmanager/selectproducts" name="et.tm.selectproducts"
76
+ template="eurotext/translationmanager/selectproducts.phtml"/>
77
+ </reference>
78
+ </adminhtml_eurotext_translationmanager_selectproducts_index>
79
+
80
+ <adminhtml_eurotext_translationmanager_selectcategories_index>
81
+ <update handle="eurotext_add_css"/>
82
+ <update handle="eurotext_prepare_popup"/>
83
+ <reference name="content">
84
+ <block type="eurotext_translationmanager/selectcategories" name="et.tm.selectcategories"
85
+ template="eurotext/translationmanager/selectcategories.phtml"/>
86
+ </reference>
87
+ </adminhtml_eurotext_translationmanager_selectcategories_index>
88
+
89
+ <adminhtml_eurotext_translationmanager_selectcmspages_index>
90
+ <update handle="eurotext_add_css"/>
91
+ <update handle="eurotext_prepare_popup"/>
92
+ <reference name="content">
93
+ <block type="eurotext_translationmanager/selectcmspages" name="et.tm.selectcmspages"
94
+ template="eurotext/translationmanager/selectcmspages.phtml"/>
95
+ </reference>
96
+ </adminhtml_eurotext_translationmanager_selectcmspages_index>
97
+
98
+ <adminhtml_eurotext_translationmanager_selectlangfiles_index>
99
+ <update handle="eurotext_add_css"/>
100
+ <update handle="eurotext_prepare_popup"/>
101
+ <reference name="content">
102
+ <block type="eurotext_translationmanager/selectlangfiles" name="et.tm.selectlangfiles"
103
+ template="eurotext/translationmanager/selectlangfiles.phtml"/>
104
+ </reference>
105
+ </adminhtml_eurotext_translationmanager_selectlangfiles_index>
106
+
107
+ <adminhtml_eurotext_translationmanager_selectemails_index>
108
+ <update handle="eurotext_add_css"/>
109
+ <update handle="eurotext_prepare_popup"/>
110
+ <reference name="content">
111
+ <block type="eurotext_translationmanager/selectemails" name="et.tm.selectemails"
112
+ template="eurotext/translationmanager/selectemails.phtml"/>
113
+ </reference>
114
+ </adminhtml_eurotext_translationmanager_selectemails_index>
115
+
116
+
117
+ <!--
118
+
119
+ LAYOUT AREA FOR AJAX REQUESTS
120
+
121
+ -->
122
+ <adminhtml_eurotext_translationmanager_ajax>
123
+ <block type="eurotext_translationmanager/response_ajax" output="toJson" name="et.tm.response.ajax" />
124
+ </adminhtml_eurotext_translationmanager_ajax>
125
+ <adminhtml_eurotext_translationmanager_text>
126
+ <block type="core/text" output="toHtml" name="et.tm.response.text" />
127
+ </adminhtml_eurotext_translationmanager_text>
128
+ </layout>
app/design/adminhtml/base/default/template/eurotext/translationmanager/help.phtml ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // utf8-detection: öäü€
3
+ ?>
4
+
5
+ <table cellspacing="0" cellpadding="0" width="100%" height="100%" border="0">
6
+ <tr>
7
+ <td class="edittext">
8
+ <table width="900" border="0">
9
+ <tr>
10
+ <td width="900" class="edittext"><a href="http://www.eurotext.de/" target="_blank"><span class="eurotext_logo"></span></a></td>
11
+ </td>
12
+ </tr>
13
+ </table>
14
+
15
+ <div style="font-weight:bold;font-size:12pt; margin-top:25px;"><?php echo $this->__("translationMANAGER Quick Start Guide"); ?></div>
16
+ <div><?php echo $this->__("Here you will find a list of the settings required for operating the Eurotext interface"); ?></div>
17
+
18
+ <div style="margin-top:15px;margin-left:20px;">
19
+ <div style="font-weight:bold;font-size:11pt;"><?php echo $this->__("Registration"); ?></div>
20
+ <div><?php echo $this->__("Make sure to complete the registration process and enter your access information before submitting your first translation project. Fill out all fields of the registration form and click <b><i>Save and send</i>.</b> Eurotext will send you your login details within 24 hours (weekdays). Enter that information in the menu item <b><i>Settings</i></b>."); ?></div>
21
+ </div>
22
+
23
+ <div style="margin-top:15px;margin-left:20px;">
24
+ <div style="font-weight:bold;font-size:11pt;"><?php echo $this->__("Settings"); ?></div>
25
+ <div><?php echo $this->__('You will receive your login details from your customer service representative at Eurotext AG as soon as you have completed your registration. Please enter your user name, password and customer number in "Settings".<br/><span style="font-style:italic;"><span style="color:red;">Important:</span> Ensure that a storeview for the target language was created in the shop before exporting the text for translation, and that the storeview settings have the correct language settings</span>'); ?></div>
26
+ </div>
27
+
28
+ <div style="margin-top:15px;margin-left:20px;">
29
+ <div style="font-weight:bold;font-size:11pt;"><?php echo $this->__("Projects"); ?></div>
30
+ <div><?php echo $this->__("Create a new project for each translation order."); ?></div>
31
+ <ul style="margin-left:20px;">
32
+ <li style="list-style-type:disc;"><?php echo $this->__("Each project must have a name assigned (we recommend something unique and identifiable)."); ?></li>
33
+ <li style="list-style-type:disc;"><?php echo $this->__("Specify a <b>Source Storeview</b> and <b>Target Storeview</b> for the requested translation."); ?></li>
34
+ </ul>
35
+
36
+ <div style="margin-top:10px;"><?php echo $this->__("Select the products, categories, CMS pages, or email templates you need translated"); ?>:</div>
37
+ <ul style="margin-left:20px;">
38
+ <li style="list-style-type:disc;"><?php echo $this->__("Products, categories, CMS pages, or email templates can be selected individually (<b>Select […]</b>), or alternatively:"); ?></li>
39
+ <li style="list-style-type:disc;"><?php echo $this->__("<b>Select all [...] without existing translation:</b> The module will automatically select all Products, categories, CMS pages and email templates for which no translated text exists in the target language."); ?><br/><?php echo $this->__("<i><span style='color:red;'>Important:</span> All previous assignments will be deleted!</i>"); ?></li>
40
+ </ul>
41
+
42
+ <div style="margin-top:10px;"><?php echo $this->__("Clicking <b>Select language files</b> will allow you to manually select specific shop system texts in the selection window for translation."); ?></div>
43
+
44
+ <div style="margin-top:10px;"><?php echo $this->__("Clicking <b>Select all language files without existing translation</b> will automatically select all files and check for missing translations. All entries without an existing translation will be exported."); ?></div>
45
+ <div><?php echo $this->__("<i><span style='color:red;'>Important:</span> We recommend using this setting only for the initial translation of a new online shop, as it will trigger a translation of ALL language files for the selected target language!</i>"); ?></div>
46
+
47
+ <div style="margin-top:10px;"><?php echo $this->__("Select the check box <b>Export SEO content?</b> if you would like to have the SEO content (e.g. keywords, search terms, meta tags, etc.) translated."); ?></div>
48
+
49
+ <div style="margin-top:10px;"><?php echo $this->__("Select the check box <b>Export attributes and attribute options?</b> if you would like to have the product attributes and associated values translated."); ?></div>
50
+
51
+ <div style="margin-top:10px;"><?php echo $this->__("Select the check box <b>Export URL keys?</b> if you would like to have URLs of products and categories translated."); ?></div>
52
+
53
+ <div style="margin-top:20px;"><?php echo $this->__("Clicking the button <b>Request your free quote now</b> will transmit your texts for translation to the translation portal, and you will receive a detailed quote for your translation project within the next 24 hours (weekdays)."); ?>
54
+
55
+ <div style="margin-top:10px;"><?php echo $this->__("Your Eurotext customer service representative will send you your translations in a zip file. This zip file can be uploaded and imported to he relevant project."); ?></div>
56
+
57
+ <div style="margin-top:10px;"><?php echo $this->__("<b>Please note:</b> For safety reasons, we recommend importing to a staging system first. Make sure to backup all your data before importing."); ?></div>
58
+
59
+ <div style="margin-top:10px;"><?php echo $this->__("Please read the translationMANAGER documentation for more information."); ?></div>
60
+
61
+ <div>&nbsp;</div>
62
+ <div style="border:1px solid black;padding:20px;display:inline-block;">
63
+ <table width="900" border="0">
64
+ <tr>
65
+ <td width="445" class="edittext"><p align="left"><strong><?php echo $this->__("Contact"); ?> Eurotext AG </strong></td>
66
+ </tr>
67
+ <tr>
68
+ <td height="135" class="edittext">
69
+ <p class="edittext" align="left">
70
+ <span class="logo_eurotext"></span><br>
71
+ Eurotext AG<br>
72
+ Sch&uuml;rerstra&szlig;e 3<br>
73
+ 97080 W&uuml;rzburg<br>
74
+ Deutschland<br><br>
75
+ <?php echo $this->__("Phone"); ?>: +49 (0)931 35 40 50<br>
76
+ <?php echo $this->__("Telefax"); ?>: +49 (0)931 35 40 580<br>
77
+ E-Mail: <a href="mailto:>info@eurotext.de">info@eurotext.de</a><br>
78
+ Web: <a href="http://www.eurotext.de" target="_blank">www.eurotext.de</a>
79
+ </p>
80
+ <p class="edittext" align="left">&nbsp;</p>
81
+ </td>
82
+ </tr>
83
+ </table>
84
+ </div>
85
+
86
+ </td>
87
+ </tr>
88
+ </table>
app/design/adminhtml/base/default/template/eurotext/translationmanager/projects.phtml ADDED
@@ -0,0 +1,392 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // utf8-detection: öäü€
3
+
4
+ $helper=Mage::helper('eurotext_translationmanager');
5
+
6
+ ?>
7
+
8
+ <div class="content-header">
9
+ <table cellspacing="0">
10
+ <tbody><tr>
11
+ <td style="width:50%;"><h3 class="icon-head head-products">translationMANAGER <?php echo $this->__("Projects"); ?></h3></td>
12
+ <td class="a-right">
13
+ <button id="eurotext_newproject" title="New project" type="button" class="scalable add" onclick="setLocation('<?php echo $this->getNewProjectUrl(); ?>')" style=""><span><?php echo $this->__("New project"); ?></span></button>
14
+ </td>
15
+ </tr>
16
+ </tbody></table>
17
+ </div>
18
+
19
+ <div class="grid">
20
+ <div class="hor-scroll">
21
+ <table cellspacing="0" class="data" id="projectsGrid_table">
22
+ <colgroup>
23
+ <col width="140" class="a-center" />
24
+ <col width="60" />
25
+ </colgroup>
26
+ <thead>
27
+ <tr class="headings">
28
+ <th><a href="#" name="name" title="asc" class="not-sort"><span class="sort-title"><?php echo $this->__("Last update"); ?></span></a></th>
29
+ <th><span class="nobr"><a href="#" name="entity_id" title="asc" class="sort-title"><span class="sort-title"><?php echo $this->__("ID"); ?></span></a></th>
30
+ <th><a href="#" name="name" title="asc" class="not-sort"><span class="sort-title"><?php echo $this->__("Project name"); ?></span></a></th>
31
+ <th><a href="#" name="name" title="asc" class="not-sort"><span class="sort-title"><?php echo $this->__("Source storeview"); ?></span></a></th>
32
+ <th><a href="#" name="name" title="asc" class="not-sort"><span class="sort-title"><?php echo $this->__("Target storeview"); ?></span></a></th>
33
+ <th><a href="#" name="name" title="asc" class="not-sort"><span class="sort-title"><?php echo $this->__("Status"); ?></span></a></th>
34
+ <th class="no-link last"><span class="nobr"><?php echo $this->__("Action"); ?></span></th>
35
+ </tr>
36
+ <!--<tr class="filter">
37
+ <th><span class="timestamp"><input type="text" id="last_update" value="" class="input-text no-changes"></span></th>
38
+ <th><span class="number"><input type="text" id="id" value="" class="input-text no-changes"></span></th>
39
+ <th><span class="text"><input type="text" id="project_name" value="" class="input-text no-changes"></span></th>
40
+ <th><span class="text">
41
+ <select id="storeview_src" class="input-text no-changes">
42
+ <option value="-1">-- Choose Filter --</option>
43
+ <option value="0">xxx</option>
44
+ <option value="1">yyy</option>
45
+ </select>
46
+ </span></th>
47
+ <th><span class="text">
48
+ <select id="storeview_dst" class="input-text no-changes">
49
+ <option value="-1">-- Choose Filter --</option>
50
+ <option value="0">xxx</option>
51
+ <option value="1">yyy</option>
52
+ </select>
53
+ </span></th>
54
+ <th><span class="text">
55
+ <select id="project_status" class="input-text no-changes">
56
+ <option value="-1">Show All</option>
57
+ <option value="1">New</option>
58
+ <option value="2">In progress</option>
59
+ </select>
60
+ </span></th>
61
+ <th class=" no-link last">&nbsp;</th>
62
+ </tr>-->
63
+ </thead>
64
+ <tbody>
65
+ <?php
66
+ $projects=$this->getProjects();
67
+ if (count($projects)==0)
68
+ {
69
+ ?>
70
+ <tr class="even pointer">
71
+ <td colspan="7">- <?php echo $this->__("No projects created"); ?> -</td>
72
+ </tr>
73
+ <?php
74
+ }
75
+ else
76
+ {
77
+ $alt=0;
78
+ foreach($projects as $project)
79
+ {
80
+ $alt=1-$alt;
81
+ $trClass="even pointer";
82
+ if ($alt==1)
83
+ {
84
+ $trClass="odd pointer";
85
+ }
86
+
87
+ if ($project['id']==$this->getSelectedProjectId())
88
+ {
89
+ $trClass="selected_project pointer";
90
+ }
91
+
92
+ $row_url=$this->getProjectUrl($project['id']);
93
+
94
+ echo "<tr title='".$row_url."' class='".$trClass."'>";
95
+ {
96
+ echo "<td>".$project['last_update']."</td>";
97
+ echo "<td>".$project['id']."</td>";
98
+ echo "<td>".$this->htmlEscape($project['project_name'])."</td>";
99
+ echo "<td>".$this->getStoreviewTitle($project['storeview_src'])."</td>";
100
+ echo "<td>".$this->getStoreviewTitle($project['storeview_dst'])."</td>";
101
+ echo "<td>".$this->getStatusText($project['project_status'])."</td>";
102
+ echo "<td>";
103
+
104
+ if ($project['project_status']==0) // new
105
+ {
106
+ echo "<a href='".$row_url."'>".$this->__("Edit")."</a>";
107
+ }
108
+ else if ($project['project_status']==1) // in_progress
109
+ {
110
+ echo "<a href='".$row_url."'>".$this->__("Import")."</a>";
111
+ }
112
+ else if ($project['project_status']==2) // in_progress
113
+ {
114
+ echo "<a href='".$row_url."'>".$this->__("Import")."</a>";
115
+ }
116
+ else if ($project['project_status']==3) // done
117
+ {
118
+ echo "<a href='".$row_url."'>".$this->__("Loaded")."</a>";
119
+ }
120
+
121
+ $rowdel_url=$this->getProjectDeleteUrl($project['id']);
122
+ echo " &nbsp; <a href='".$rowdel_url."' onclick='return eurotext_confirmdelete()'>".$this->__("Delete")."</a>";
123
+
124
+ if ($project['project_status']>0)
125
+ {
126
+ $rowreset_url=$this->getProjectResetUrl($project['id']);
127
+ echo " &nbsp; <a href='".$rowreset_url."' onclick='return eurotext_confirmreset()'>".$this->__("Reset")."</a>";
128
+ }
129
+
130
+ echo "<input type='hidden' id='dummyinput_".$project['id']."' value='1' />"; // magento's grid needs this
131
+ echo "</td>";
132
+ }
133
+ echo "</tr>";
134
+ }
135
+ }
136
+ ?>
137
+ </tbody>
138
+ </table>
139
+ </div>
140
+ </div>
141
+
142
+ <script type="text/javascript">
143
+ eurotextGridJsObject = new varienGrid('projectsGrid', '', 'page', 'sort', 'dir', 'product_filter');
144
+ eurotextGridJsObject.useAjax = '1';
145
+ eurotextGridJsObject.rowClickCallback = openGridRow;
146
+ </script>
147
+
148
+ <hr size="1" color="black" style="margin:20px;" />
149
+
150
+ <?php
151
+ $project_id=$this->getSelectedProjectId();
152
+ if ($project_id>0)
153
+ {
154
+ $project=$this->getSelectedProject();
155
+
156
+ echo "<script>";
157
+ echo "var eurotext_project_saveurl='".$this->getSaveProjectUrl()."';";
158
+ echo "var eurotext_project_sendurl='".$this->getAjaxExportUrl()."';";
159
+ echo "var eurotext_project_importstepurl='".$this->getAjaxImportStepUrl()."';";
160
+ echo "var eurotext_project_id=".$project['id'].";";
161
+ echo "var eurotext_formkey='".Mage::getSingleton('core/session')->getFormKey()."'";
162
+ echo "</script>";
163
+
164
+ $uploadboxTitle=$this->__("Quote request");
165
+
166
+ $disabledStr="";
167
+ $cannotChangeInfo="";
168
+ if ($project['project_status']>0)
169
+ {
170
+ $uploadboxTitle=$this->__("Import translations");
171
+
172
+ $disabledStr=" disabled='disabled'";
173
+ $cannotChangeInfo="<span style='font-size:10pt;color:red;font-weight:bold;'>".$this->__("Already submitted. Project settings can no longer be edited.")."</span>";
174
+ }
175
+
176
+ $canExportUrlKeysStyle="";
177
+ if($helper->urlKeyScopeIsGlobal())
178
+ {
179
+ $canExportUrlKeysStyle="disabled='disabled' style='color:gray;'";
180
+ $project['export_urlkeys']=false;
181
+ }
182
+ ?>
183
+ <div class="project_settings">
184
+
185
+ <div id="seite_links" style="display:inline-block;margin-right:10px;">
186
+ <div class="grid eurotext_grid">
187
+ <table class="data" cellspacing="0" cellpadding="0" border="0" height="100%" width="100%">
188
+ <tr class="headings">
189
+ <td colspan="4" class="eurotext_head2"><?php echo $this->__("Project settings"); ?> '<?php echo $this->htmlEscape($project['project_name']); ?>' (Nr <?php echo $project_id; ?>) <?php echo $cannotChangeInfo; ?></td>
190
+ </tr>
191
+ <tr class="headings">
192
+ <td class="eurotext_head"><?php echo $this->__("Action"); ?></td>
193
+ <td class="eurotext_head">&nbsp;</td>
194
+ <td class="eurotext_head"><?php echo $this->__("Note"); ?></td>
195
+ <td class="eurotext_head"><?php echo $uploadboxTitle; ?></td>
196
+ </tr>
197
+ <tr>
198
+ <td class="eurotext_td"><?php echo $this->__("Project name"); ?></td>
199
+ <td>
200
+ <input <?php echo $disabledStr; ?> type="text" class="editinput" size="50" maxlength="255" autocomplete="off" id="form_project_name" value="<?php echo $this->htmlEscape($project['project_name']); ?>" >
201
+ </td>
202
+ <td><?php echo $this->__("Create a unique project name here. This will allow you better recognize your various projects later."); ?></td>
203
+ <td rowspan="10" style="background-color:#FAFAFA;">
204
+ <!-- Anfang rechte Seite -->
205
+ <div class="seite_rechts" style="display:inline-block;">
206
+
207
+ <?php
208
+ if ($project['project_status']==0)
209
+ {
210
+ ?>
211
+
212
+ <div style="margin:10px;border:2px dashed black; padding:10px;display:inline-block; background-color:#F7C27E;">
213
+ <table cellspacing="0" cellpadding="0" border="0" style="border:0px;">
214
+ <tr style="border:0px;">
215
+ <td style="border:0px;background-color:#F7C27E;"><?php echo $this->__("Select product texts, category texts, or CMS pages for translation."); ?><br><br>
216
+ <?php echo $this->__("Clicking the button below will transmit the selected texts to the translation portal, and you will receive a detailed quote for the translation of your texts within 24h (weekdays)."); ?><br><br>
217
+ <?php echo $this->__("This process may take a few minutes depending on project size. Please wait until a message is displayed that the import has been completed."); ?></td>
218
+ </tr>
219
+ <tr style="border:0px;">
220
+ <td style="border:0px;background-color:#F7C27E;">
221
+ <input id="eurotext_angebot_btt" class="et_btt" type="button" name="save" value="&nbsp;&nbsp;&nbsp;<?php echo $this->__("Request your free quote now"); ?>&nbsp;&nbsp;&nbsp;" onClick="return eurotext_sendproject()" /><br>
222
+ </td>
223
+ </tr>
224
+ <tr style="border:0px;">
225
+ <td style="border:0px;background-color:#F7C27E;"><span id="eurotext_sendprogress"></span></td>
226
+ </tr>
227
+ </table>
228
+ </div>
229
+
230
+ <?php
231
+ }
232
+
233
+ if ($project['project_status']==1)
234
+ {
235
+ ?>
236
+
237
+ <div style="margin:10px;border:2px dashed black; padding:10px;display:inline-block;background-color:#F7C27E;">
238
+ <form id="zipfile_form" action="<?php echo $this->getUploadUrl(); ?>" method="post" enctype="multipart/form-data">
239
+ <input type="hidden" name="form_key" value="<?php echo Mage::getSingleton('core/session')->getFormKey(); ?>" />
240
+ <table cellspacing="0" cellpadding="0" border="0" style="border:0px;">
241
+ <tr>
242
+ <td style="border:0px;background-color:#F7C27E;">
243
+ <div><?php echo $this->__("Import the *.zip file containing the finished translations here"); ?>:</div>
244
+ <div><input type="file" accept="application/zip" name="zipfile" id="zipfile" /></div>
245
+ </td>
246
+ </tr>
247
+ <tr height="10">
248
+ <td style="border:0px;background-color:#F7C27E;">&nbsp;</td>
249
+ </tr>
250
+ <tr>
251
+ <td style="border:0px;background-color:#F7C27E;text-align:center;">
252
+ <a class="et_btt" href="#" onclick="return eurotext_uploadzip()"><?php echo $this->__("Import translations now"); ?></a>
253
+ </td>
254
+ </tr>
255
+ </table>
256
+
257
+ <?php
258
+ if ($helper->getDebugMode())
259
+ {
260
+ $zip_url=Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB)."var/export/".$project['zip_filename'];
261
+ echo '<div>&nbsp;</div>';
262
+ echo '<div>(Debug) Vom Modul erstellte *.zip-Datei: <a href="'.$zip_url.'">'.$project['zip_filename'].'</a></div>';
263
+ }
264
+ ?>
265
+
266
+ </form>
267
+ </div>
268
+
269
+ <?php
270
+ }
271
+
272
+ if ($project['project_status']==2)
273
+ {
274
+ ?>
275
+
276
+ <div style="margin:10px;border:2px dashed black; padding:10px;display:inline-block;background-color:#F7C27E;">
277
+ <table cellspacing="0" cellpadding="0" border="0" style="border:0px;">
278
+ <tr>
279
+ <td style="border:0px;background-color:#F7C27E;">
280
+ <div><?php echo $this->__("The *.zip-File was extracted. Click “Begin import” to upload the translations."); ?></div>
281
+ <div><?php echo $this->__("We recommend creating a backup before importing."); ?></div>
282
+ </td>
283
+ </tr>
284
+ <tr height="10">
285
+ <td style="border:0px;background-color:#F7C27E;">&nbsp;</td>
286
+ </tr>
287
+ <tr>
288
+ <td style="border:0px;background-color:#F7C27E;text-align:center;">
289
+ <a class="et_btt" id="eurotext_importbtt" href="#" onclick="return eurotext_startimport()"><?php echo $this->__("Begin import"); ?></a>
290
+ </td>
291
+ </tr>
292
+ <tr>
293
+ <td style="border:0px;background-color:#F7C27E;"><span id="eurotext_importprogress"></span></td>
294
+ </tr>
295
+ </table>
296
+ </div>
297
+
298
+ <?php
299
+ }
300
+ ?>
301
+ </div>
302
+ <!-- Ende rechte Seite -->
303
+ </td>
304
+ </tr>
305
+ <tr>
306
+ <td>Storeview</td>
307
+ <td class="eurotext_td">
308
+ <div><?php echo $this->__("Source storeview"); ?></div>
309
+ <div><?php echo $this->getSpracheSelect('form_storeview_src',$project['storeview_src'],$disabledStr); ?></div>
310
+ <div>&nbsp;</div>
311
+ <div><?php echo $this->__("Target storeview"); ?></div>
312
+ <div><?php echo $this->getSpracheSelect('form_storeview_dst',$project['storeview_dst'],$disabledStr); ?></div>
313
+ </td>
314
+ <td><?php echo $this->__("Specify the source language (source storeview) and the target language (target storeview).<br/>The language of the current storeview is shown in brackets. You can amend the language setting in Magento if the language shown is incorrect:<br/>System -> Configuration, then select “Storeview” at the top left. Set the correct language via General -> Locale Options (here you can also double-check all other language/country settings for your shop)"); ?></td>
315
+ </tr>
316
+ <tr>
317
+ <td style="text-align:center;">
318
+ <input <?php echo $disabledStr; ?> class="et_btt" type="button" value="<?php echo $this->__("Select products"); ?>" class="eurotext_td" onclick="return eurotext_select('<?php echo $this->getSelectUrl('selectproducts',$project_id); ?>','<?php echo $project_id; ?>','form_productmode')">
319
+ </td>
320
+ <td><?php echo $this->__("or"); ?> <input <?php echo $disabledStr; ?> class="eurotext_td" type="checkbox" id="form_productmode" value='1' <?php echo $this->GetCheckedStr($project['productmode']); ?>> <?php echo $this->__("Select all products without existing translations"); ?></td>
321
+ <td><?php echo $this->__("Selecting this check box will assign all items for which no text has been created in the target language.<br><b>Important: All previous assignments will be cancelled!</b>"); ?></b></td>
322
+ </tr>
323
+ <tr>
324
+ <td style="text-align:center;">
325
+ <input <?php echo $disabledStr; ?> type="button" value="<?php echo $this->__("Select categories"); ?>" class="et_btt" onclick="return eurotext_select('<?php echo $this->getSelectUrl('selectcategories',$project_id); ?>','<?php echo $project_id; ?>','form_categorymode')" >
326
+ </td>
327
+ <td><?php echo $this->__("or"); ?> <input <?php echo $disabledStr; ?> class="eurotext_td" type="checkbox" id="form_categorymode" autocomplete="off" value='1' <?php echo $this->GetCheckedStr($project['categorymode']); ?>> <?php echo $this->__("Select all categories without existing translations"); ?></td>
328
+ <td><?php echo $this->__("Selecting this check box will assign all categories for which no texts have been created in the target language.<br/><b>Important: All previous assignments will be cancelled!</b>"); ?></td>
329
+ </tr>
330
+ <tr>
331
+ <td style="text-align:center;">
332
+ <input <?php echo $disabledStr; ?> type="button" value="<?php echo $this->__("Select CMS pages"); ?>" class="et_btt" onclick="return eurotext_select('<?php echo $this->getSelectUrl('selectcmspages',$project_id); ?>','<?php echo $project_id; ?>','form_cmsmode')" >
333
+ </td>
334
+ <td><?php echo $this->__("or"); ?> <input <?php echo $disabledStr; ?> class="eurotext_td" type="checkbox" id="form_cmsmode" autocomplete="off" value='1' <?php echo $this->GetCheckedStr($project['cmsmode']); ?>> <?php echo $this->__("Select all CMS pages without existing translations"); ?></td>
335
+ <td><?php echo $this->__("Selecting this check box will assign all CMS pages without existing target language translations.<br/><b>Important: All previous assignments will be cancelled!</b>"); ?></td>
336
+ </tr>
337
+ <tr>
338
+ <td style="text-align:center;">
339
+ <input <?php echo $disabledStr; ?> type="button" value="<?php echo $this->__("Select email templates"); ?>" class="et_btt" onclick="return eurotext_select('<?php echo $this->getSelectUrl('selectemails',$project_id); ?>','<?php echo $project_id; ?>','form_templatemode')" >
340
+ </td>
341
+ <td><?php echo $this->__("or"); ?> <input <?php echo $disabledStr; ?> class="eurotext_td" type="checkbox" id="form_templatemode" autocomplete="off" value='1' <?php echo $this->GetCheckedStr($project['templatemode']); ?>> <?php echo $this->__("Select all email templates without existing translations"); ?></td>
342
+ <td><?php echo $this->__("Selecting this check box will export all email templates without a counterpart in the target language.<br><b>Important: All previous assignments will be cancelled!</b>"); ?></td>
343
+ </tr>
344
+ <tr>
345
+ <td style="text-align:center;">
346
+ <input <?php echo $disabledStr; ?> type="button" value="<?php echo $this->__("Select language files"); ?>" class="et_btt" onclick="return eurotext_select('<?php echo $this->getSelectUrl('selectlangfiles',$project_id); ?>','<?php echo $project_id; ?>','form_langfilesmode')" >
347
+ </td>
348
+ <td><?php echo $this->__("or"); ?> <input <?php echo $disabledStr; ?> class="eurotext_td" type="checkbox" id="form_langfilesmode" autocomplete="off" value='1' <?php echo $this->GetCheckedStr($project['langfilesmode']); ?>> <?php echo $this->__("Select all entries in language files without existing translations"); ?></td>
349
+ <td><?php echo $this->__("Selecting this check box will trigger a check of all language files for missing translations. All entries without an existing translation will be exported.<br><b>Important: All previous assignments will be cancelled!</b>"); ?></td>
350
+ </tr>
351
+ <tr>
352
+ <td><?php echo $this->__("Additional settings"); ?></td>
353
+ <td>
354
+ <div><input <?php echo $disabledStr; ?> class="eurotext_td" type="checkbox" id="form_export_seo" autocomplete="off" value='1' <?php echo $this->GetCheckedStr($project['export_seo']); ?>> 1) <?php echo $this->__("Export SEO content?"); ?></div>
355
+ <div>&nbsp;</div>
356
+ <div><input <?php echo $disabledStr; ?> class="eurotext_td" type="checkbox" id="form_export_attributes" autocomplete="off" value='1' <?php echo $this->GetCheckedStr($project['export_attributes']); ?>> 2) <?php echo $this->__("Export attributes und attribute options?"); ?></div>
357
+ <div>&nbsp;</div>
358
+ <div <?php echo $canExportUrlKeysStyle; ?>><input <?php echo $canExportUrlKeysStyle; ?> <?php echo $disabledStr; ?> class="eurotext_td" type="checkbox" id="form_export_urlkeys" autocomplete="off" value='1' <?php echo $this->GetCheckedStr($project['export_urlkeys']); ?>> 3) <?php echo $this->__("Export URL keys?"); ?></div>
359
+ </td>
360
+ <td>
361
+ <div>1) <?php echo $this->__("Select this check box if you wish to translate your SEO content (keywords, meta tags, etc.)."); ?></div>
362
+ <div>&nbsp;</div>
363
+ <div>2) <?php echo $this->__("Select this check box if you wish to translate product attributes and associated values."); ?></div>
364
+ <div>&nbsp;</div>
365
+ <div <?php echo $canExportUrlKeysStyle; ?>>3) <?php echo $this->__("Select this check box if you wish to translate product and category URLs"); ?></div>
366
+ </td>
367
+ </tr>
368
+ </table>
369
+ </div>
370
+ </div>
371
+
372
+ <div style="margin-top:20px;">
373
+ <input <?php echo $disabledStr; ?> type="submit" class="et_btt" id="btt_saveproject" value="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<?php echo $this->__("Save"); ?>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" onclick="return eurotext_saveproject(eurotext_reloadpage)">
374
+ <br><span id="eurotext_saving"></span>
375
+ </div>
376
+
377
+ <!-- Ende linke Seite -->
378
+ </td>
379
+ <td>
380
+
381
+ </td>
382
+ </tr>
383
+ </table>
384
+ </div>
385
+ <?php
386
+ }
387
+ ?>
388
+
389
+ <div>&nbsp;</div>
390
+ <div class="actions">
391
+ <ul><li><a class="firstitem" id="btn.help" href="<?php echo Mage::helper('adminhtml')->getUrl('*/eurotext_translationmanager_help/index'); ?>" target="_blank"><?php echo $this->__("Open help"); ?></a></li></ul>
392
+ </div>
app/design/adminhtml/base/default/template/eurotext/translationmanager/register.phtml ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // utf8-detection: öäü€
3
+
4
+ ?>
5
+
6
+ <form id="et_form" action="<?php echo $this->getPostBackUrl(); ?>" method="get">
7
+ <div class="eurotext_confighead"><?php echo $this->__("Please enter your contact information here"); ?></div>
8
+ <div class="eurotext_config">
9
+ <table width="98%" class="form">
10
+ <tr>
11
+ <td colspan="2" class="eurotext_edittext"><?php echo $this->__("This information is your personal access data for the Eurotext AG translation portal.<br/>Registration is free of charge. You will receive your personal access information within 24 hours (weekdays)."); ?></td>
12
+ </tr>
13
+ </table>
14
+ <table class="form">
15
+ <tr>
16
+ <td class="eurotext_edittext"><label><?php echo $this->__("Shop Name"); ?>:&nbsp;&nbsp;</label></td>
17
+ <td class="eurotext_edittext"><input disabled="disabled" id="et_register_shopname" type="text" name="register_shopname" size="95"
18
+ class="defaultcontent"
19
+ value="<?php echo $this->getSetting("register_shopname"); ?>" >
20
+ </td>
21
+ </tr>
22
+ <tr>
23
+ <td class="eurotext_edittext"><label><?php echo $this->__("Shop URL"); ?>:&nbsp;&nbsp;</label></td>
24
+ <td class="eurotext_edittext"><input disabled="disabled" id="et_register_shopurl" type="text" name="register_url" size="95"
25
+ class="defaultcontent"
26
+ value="<?php echo $this->getSetting("register_url"); ?>" >
27
+ </td>
28
+ </tr>
29
+ <tr>
30
+ <td class="eurotext_edittext"><label><?php echo $this->__("eMail Address"); ?>:&nbsp;&nbsp;</label></td>
31
+ <td class="eurotext_edittext"><input disabled="disabled" id="et_register_email" type="text" name="register_email" size="95"
32
+ class="defaultcontent"
33
+ value="<?php echo $this->getSetting("register_email"); ?>" >
34
+ </td>
35
+ </tr>
36
+ <tr>
37
+ <td class="eurotext_edittext"><label><?php echo $this->__("Salutation"); ?>:&nbsp;&nbsp;</label></td>
38
+ <td class="eurotext_edittext">
39
+ <select disabled="disabled" name="register_sal" id="register_sal" >
40
+ <option value="MR" <?php if ($this->getSetting("register_sal")=="MR") { echo " selected='selected'"; } ?>><?php echo $this->__("Mr."); ?></option>
41
+ <option value="MRS" <?php if ($this->getSetting("register_sal")=="MRS") { echo " selected='selected'"; } ?>><?php echo $this->__("Ms."); ?></option>
42
+ </select>
43
+ </td>
44
+ </tr>
45
+ <tr>
46
+ <td class="eurotext_edittext"><label><?php echo $this->__("First Name"); ?>:&nbsp;&nbsp;</label></td>
47
+ <td class="eurotext_edittext"><input disabled="disabled" id="et_register_firstname" type="text" name="register_fname" size="95"
48
+ class="defaultcontent"
49
+ value="<?php echo $this->getSetting("register_fname"); ?>" >
50
+ </td>
51
+ </tr>
52
+ <tr>
53
+ <td class="eurotext_edittext"><label><?php echo $this->__("Last Name"); ?>:&nbsp;&nbsp;</label></td>
54
+ <td class="eurotext_edittext"><input disabled="disabled" id="et_register_lastname" type="text" name="register_lname" size="95"
55
+ class="defaultcontent"
56
+ value="<?php echo $this->getSetting("register_lname"); ?>" >
57
+ </td>
58
+ </tr>
59
+ <tr>
60
+ <td class="eurotext_edittext"><label><?php echo $this->__("Company"); ?>:&nbsp;&nbsp;</label></td>
61
+ <td class="eurotext_edittext"><input disabled="disabled" id="et_register_company" type="text" name="register_company" size="95"
62
+ class="defaultcontent"
63
+ value="<?php echo $this->getSetting("register_company"); ?>" >
64
+ </td>
65
+ </tr>
66
+ <tr>
67
+ <td class="eurotext_edittext"><label><?php echo $this->__("Street"); ?> / <?php echo $this->__("House No"); ?>:&nbsp;&nbsp;</label></td>
68
+ <td class="eurotext_edittext"><input disabled="disabled" id="et_register_street" type="text" name="register_street" size="83"
69
+ class="defaultcontent" value="<?php echo $this->getSetting("register_street"); ?>" >
70
+ <input disabled="disabled" id="et_register_hnumber" type="text" name="register_hnumber" size="8"
71
+ class="defaultcontent" value="<?php echo $this->getSetting("register_hnumber"); ?>" >
72
+ </td>
73
+ </tr>
74
+ <tr>
75
+ <td class="eurotext_edittext"><label><?php echo $this->__("Zip"); ?> / <?php echo $this->__("City"); ?>:&nbsp;&nbsp;</label></td>
76
+ <td class="eurotext_edittext"><input disabled="disabled" id="et_register_zip" type="text" name="register_zip" size="8"
77
+ class="defaultcontent" value="<?php echo $this->getSetting("register_zip"); ?>" >
78
+ <input disabled="disabled" id="et_register_city" type="text" name="register_city" size="83"
79
+ class="defaultcontent" value="<?php echo $this->getSetting("register_city"); ?>">
80
+ </td>
81
+ </tr>
82
+ <tr>
83
+ <td class="eurotext_edittext"><label><?php echo $this->__("Country"); ?>&nbsp;&nbsp;</label></td>
84
+ <td class="eurotext_edittext">
85
+ <select disabled="disabled" name="register_country" id="register_country" >
86
+ <option value="Deutschland">Deutschland</option>
87
+ <option value="&Ouml;sterreich">&Ouml;sterreich</option>
88
+ <option value="Schweiz">Schweiz</option>
89
+ <option value="Afghanistan">Afghanistan</option>
90
+ <option value="&Auml;gypten">&Auml;gypten</option>
91
+ <option value="Albanien">Albanien</option>
92
+ <option value="Algerien">Algerien</option>
93
+ <option value="Andorra">Andorra</option>
94
+ <option value="Angola">Angola</option>
95
+ <option value="Antigua und Barbuda">Antigua und Barbuda</option>
96
+ <option value="&Auml;quatorialguinea">&Auml;quatorialguinea</option>
97
+ <option value="Argentinien">Argentinien</option>
98
+ <option value="Armenien">Armenien</option>
99
+ <option value="Aserbaidschan">Aserbaidschan</option>
100
+ <option value="&Auml;thiopien">&Auml;thiopien</option>
101
+ <option value="Australien">Australien</option>
102
+ <option value="Bahamas">Bahamas</option>
103
+ <option value="Bahrain">Bahrain</option>
104
+ <option value="Bangladesch">Bangladesch</option>
105
+ <option value="Barbados">Barbados</option>
106
+ <option value="Wei&szlig;russland">Wei&szlig;russland</option>
107
+ <option value="Belgien">Belgien</option>
108
+ <option value="Belize">Belize</option>
109
+ <option value="Benin">Benin</option>
110
+ <option value="Bhutan">Bhutan</option>
111
+ <option value="Bolivien">Bolivien</option>
112
+ <option value="Bosnien-Herzegowina">Bosnien-Herzegowina</option>
113
+ <option value="Botsuana">Botsuana</option>
114
+ <option value="Brasilien">Brasilien</option>
115
+ <option value="Brunei Darussalam">Brunei Darussalam</option>
116
+ <option value="Bulgarien">Bulgarien</option>
117
+ <option value="Burkina Faso">Burkina Faso</option>
118
+ <option value="Burundi">Burundi</option>
119
+ <option value="Cayman Islands">Cayman Islands</option>
120
+ <option value="Chile">Chile</option>
121
+ <option value="China">China</option>
122
+ <option value="Cookinseln">Cookinseln</option>
123
+ <option value="Costa Rica">Costa Rica</option>
124
+ <option value="Elfenbeink&uuml;ste">Elfenbeink&uuml;ste</option>
125
+ <option value="D&auml;nemark">D&auml;nemark</option>
126
+ <option value="Dominica">Dominica</option>
127
+ <option value="Dominikanische Republik">Dominikanische Republik</option>
128
+ <option value="Dschamahirija">Dschamahirija</option>
129
+ <option value="Dschibuti">Dschibuti</option>
130
+ <option value="Ecuador">Ecuador</option>
131
+ <option value="El Salvador">El Salvador</option>
132
+ <option value="England">England</option>
133
+ <option value="Eritrea">Eritrea</option>
134
+ <option value="Estland">Estland</option>
135
+ <option value="Fidschi">Fidschi</option>
136
+ <option value="Finnland">Finnland</option>
137
+ <option value="Frankreich">Frankreich</option>
138
+ <option value="Gabon">Gabon</option>
139
+ <option value="Gambia">Gambia</option>
140
+ <option value="Georgien">Georgien</option>
141
+ <option value="Ghana">Ghana</option>
142
+ <option value="Grenada">Grenada</option>
143
+ <option value="Griechenland">Griechenland</option>
144
+ <option value="Guatemala">Guatemala</option>
145
+ <option value="Guinea">Guinea</option>
146
+ <option value="Guinea-Bissau">Guinea-Bissau</option>
147
+ <option value="Guyana">Guyana</option>
148
+ <option value="Haiti">Haiti</option>
149
+ <option value="Heiliger Stuhl">Heiliger Stuhl</option>
150
+ <option value="Honduras">Honduras</option>
151
+ <option value="Indien">Indien</option>
152
+ <option value="Indonesien">Indonesien</option>
153
+ <option value="Irak">Irak</option>
154
+ <option value="Iran">Iran</option>
155
+ <option value="Irland">Irland</option>
156
+ <option value="Island">Island</option>
157
+ <option value="Israel">Israel</option>
158
+ <option value="Italien">Italien</option>
159
+ <option value="Jamaika">Jamaika</option>
160
+ <option value="Japan">Japan</option>
161
+ <option value="Jemen">Jemen</option>
162
+ <option value="Jordanien">Jordanien</option>
163
+ <option value="Jugoslawien">Jugoslawien</option>
164
+ <option value="Kambodscha">Kambodscha</option>
165
+ <option value="Kamerun">Kamerun</option>
166
+ <option value="Kanada">Kanada</option>
167
+ <option value="Kap Verde">Kap Verde</option>
168
+ <option value="Kasachstan">Kasachstan</option>
169
+ <option value="Katar">Katar</option>
170
+ <option value="Kenia">Kenia</option>
171
+ <option value="Kirgisistan">Kirgisistan</option>
172
+ <option value="Kiribati">Kiribati</option>
173
+ <option value="Kolumbien">Kolumbien</option>
174
+ <option value="Komoren">Komoren</option>
175
+ <option value="Kongo">Kongo</option>
176
+ <option value="Kongo, Dem. Republik">Kongo, Dem. Republik</option>
177
+ <option value="Korea, Dem. Volksrep.">Korea, Dem. Volksrep.</option>
178
+ <option value="Korea, Republik">Korea, Republik</option>
179
+ <option value="Kosovo">Kosovo</option>
180
+ <option value="Kroatien">Kroatien</option>
181
+ <option value="Kuba">Kuba</option>
182
+ <option value="Kuwait">Kuwait</option>
183
+ <option value="Laos">Laos</option>
184
+ <option value="Lesotho">Lesotho</option>
185
+ <option value="Lettland">Lettland</option>
186
+ <option value="Libanon">Libanon</option>
187
+ <option value="Liberia">Liberia</option>
188
+ <option value="Libysch-Arabische">Libysch-Arabische</option>
189
+ <option value="Liechtenstein">Liechtenstein</option>
190
+ <option value="Litauen">Litauen</option>
191
+ <option value="Luxemburg">Luxemburg</option>
192
+ <option value="Madagaskar">Madagaskar</option>
193
+ <option value="Malawi">Malawi</option>
194
+ <option value="Malaysia">Malaysia</option>
195
+ <option value="Malediven">Malediven</option>
196
+ <option value="Mali">Mali</option>
197
+ <option value="Malta">Malta</option>
198
+ <option value="Marokko">Marokko</option>
199
+ <option value="Marshallinseln">Marshallinseln</option>
200
+ <option value="Mauretanien">Mauretanien</option>
201
+ <option value="Mauritius">Mauritius</option>
202
+ <option value="Mazedonien">Mazedonien</option>
203
+ <option value="Mexiko">Mexiko</option>
204
+ <option value="Mikronesien">Mikronesien</option>
205
+ <option value="Moldau">Moldau</option>
206
+ <option value="Monaco">Monaco</option>
207
+ <option value="Mongolei">Mongolei</option>
208
+ <option value="Mosambik">Mosambik</option>
209
+ <option value="Myanmar">Myanmar</option>
210
+ <option value="Namibia">Namibia</option>
211
+ <option value="Nauru">Nauru</option>
212
+ <option value="Nepal">Nepal</option>
213
+ <option value="Neuseeland">Neuseeland</option>
214
+ <option value="Nicaragua">Nicaragua</option>
215
+ <option value="Niederlande">Niederlande</option>
216
+ <option value="Niger">Niger</option>
217
+ <option value="Nigeria">Nigeria</option>
218
+ <option value="Niue">Niue</option>
219
+ <option value="Norwegen">Norwegen</option>
220
+ <option value="Oman">Oman</option>
221
+ <option value="Osttimor">Osttimor</option>
222
+ <option value="Pakistan">Pakistan</option>
223
+ <option value="Palau">Palau</option>
224
+ <option value="Palestina">Palestina</option>
225
+ <option value="Panama">Panama</option>
226
+ <option value="Papua-Neuguinea">Papua-Neuguinea</option>
227
+ <option value="Paraguay">Paraguay</option>
228
+ <option value="Peru">Peru</option>
229
+ <option value="Philippinen">Philippinen</option>
230
+ <option value="Polen">Polen</option>
231
+ <option value="Portugal">Portugal</option>
232
+ <option value="Ruanda">Ruanda</option>
233
+ <option value="Rum&auml;nien">Rum&auml;nien</option>
234
+ <option value="Russische F&ouml;deration">Russische F&ouml;deration</option>
235
+ <option value="Salomonen">Salomonen</option>
236
+ <option value="Sambia">Sambia</option>
237
+ <option value="Samoa">Samoa</option>
238
+ <option value="San Marino">San Marino</option>
239
+ <option value="São Tomé und Príncipe">São Tomé und Príncipe</option>
240
+ <option value="Saudi-Arabien">Saudi-Arabien</option>
241
+ <option value="Schweden">Schweden</option>
242
+ <option value="Senegal">Senegal</option>
243
+ <option value="Serbien">Serbien</option>
244
+ <option value="Seychellen">Seychellen</option>
245
+ <option value="Sierra Leone">Sierra Leone</option>
246
+ <option value="Simbabwe">Simbabwe</option>
247
+ <option value="Singapur">Singapur</option>
248
+ <option value="Slowakei">Slowakei</option>
249
+ <option value="Slowenien">Slowenien</option>
250
+ <option value="Somalia">Somalia</option>
251
+ <option value="Spanien">Spanien</option>
252
+ <option value="Sri Lanka">Sri Lanka</option>
253
+ <option value="St. Kitts und Nevis">St. Kitts und Nevis</option>
254
+ <option value="St. Lucia">St. Lucia</option>
255
+ <option value="St. Vincent / Grenadinen">St. Vincent / Grenadinen</option>
256
+ <option value="S&uuml;dafrika">S&uuml;dafrika</option>
257
+ <option value="Sudan">Sudan</option>
258
+ <option value="Suriname">Suriname</option>
259
+ <option value="Swasiland">Swasiland</option>
260
+ <option value="Syrien">Syrien</option>
261
+ <option value="Tadschikistan">Tadschikistan</option>
262
+ <option value="Taiwan">Taiwan</option>
263
+ <option value="Tansania">Tansania</option>
264
+ <option value="Thailand">Thailand</option>
265
+ <option value="Togo">Togo</option>
266
+ <option value="Tonga">Tonga</option>
267
+ <option value="Trinidad und Tobago">Trinidad und Tobago</option>
268
+ <option value="Tschad">Tschad</option>
269
+ <option value="Tschechische Republik">Tschechische Republik</option>
270
+ <option value="Tunesien">Tunesien</option>
271
+ <option value="T&uuml;rkei">T&uuml;rkei</option>
272
+ <option value="Turkmenistan">Turkmenistan</option>
273
+ <option value="Tuvalu">Tuvalu</option>
274
+ <option value="Uganda">Uganda</option>
275
+ <option value="Ukraine">Ukraine</option>
276
+ <option value="Ungarn">Ungarn</option>
277
+ <option value="Uruguay">Uruguay</option>
278
+ <option value="USA">USA</option>
279
+ <option value="Usbekistan">Usbekistan</option>
280
+ <option value="Vanuatu">Vanuatu</option>
281
+ <option value="Vatikanstadt">Vatikanstadt</option>
282
+ <option value="Venezuela">Venezuela</option>
283
+ <option value="Vereinigte Arabische Emirate">Vereinigte Arabische Emirate</option>
284
+ <option value="Vereinigtes K&ouml;nigreich">Vereinigtes K&ouml;nigreich</option>
285
+ <option value="Vietnam">Vietnam</option>
286
+ <option value="Zentralafrikanische Republik">Zentralafrikanische Republik</option>
287
+ <option value="Zypern">Zypern</option>
288
+ </select>
289
+
290
+ <script>
291
+ jQuery(document).ready(function()
292
+ {
293
+ var selCountry="<?php echo $this->getSetting("register_country"); ?>";
294
+ jQuery("#register_country").val(selCountry);
295
+ });
296
+ </script>
297
+ </td>
298
+ </tr>
299
+ <tr>
300
+ <td class="eurotext_edittext"><label><?php echo $this->__("Phone"); ?>:&nbsp;&nbsp;</label></td>
301
+ <td class="eurotext_edittext"><input disabled="disabled" id="et_register_tel" type="text" name="register_telefon" size=95
302
+ class="defaultcontent"
303
+ value="<?php echo $this->getSetting("register_telefon"); ?>">
304
+ </td>
305
+ </tr>
306
+ <tr>
307
+ <td class="eurotext_edittext" colspan="2" style="padding-left:8px;"><br><?php echo $this->__("Please enter all requested information!"); ?><br><br><b><?php echo $this->__("Important"); ?>:</b><br><?php echo $this->__("Any information you provide is voluntary. Your data will be used for the purpose of logging in to the translation portal and for project processing in your dealings with Eurotext AG only. Your information will not be forwarded to third parties. You can request a deletion of your information at any time by sending an email to <a href='mailto:datenschutz@eurotext.de'>privacy@eurotext.de</a>."); ?></td>
308
+ </tr>
309
+ </table>
310
+ </div>
311
+ </div>
312
+
313
+ <div style="margin-top:20px;">
314
+ <table cellpadding="0" cellspacing="0" style="width:100%;">
315
+ <tr>
316
+ <td class="eurotext_edittext">
317
+ <span class="btn">
318
+ <input class="et_btt" disabled="disabled" type="submit" class="btn" id="register_submit" value="&nbsp;&nbsp;&nbsp;<?php echo $this->__("Save and send"); ?>&nbsp;&nbsp;&nbsp;">
319
+ </span>
320
+ <span class="btn" style="margin-left:230px;">
321
+ <input class="et_btt" style="display:none;" id="register_change" type="submit" onclick="return eurotext_unlock2()" value="&nbsp;&nbsp;&nbsp;<?php echo $this->__("Edit information"); ?>&nbsp;&nbsp;&nbsp;">
322
+ </span>
323
+ </td>
324
+ <td style="text-align:right;"><?php echo $this->__("Module-Version"); ?>: <?php echo $this->getModuleVersion(); ?></td>
325
+ </tr>
326
+ <tr>
327
+ <td colspan="2" class="eurotext_edittext" id="et_statusmsg"><?php echo $this->getMessage(); ?></td>
328
+ </tr>
329
+ </table>
330
+ </div>
331
+
332
+ <?php
333
+ if ($this->isSaved()==false)
334
+ {
335
+ // eMail was never send, unlock fields:
336
+ echo "<script>eurotext_unlock();</script>";
337
+ }
338
+ else
339
+ {
340
+ // Enable 'Edit information' button
341
+ echo "<script>jQuery('#register_change').show();</script>";
342
+ }
343
+ ?>
344
+ </form>
app/design/adminhtml/base/default/template/eurotext/translationmanager/selectcategories.phtml ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // allow utf8-detection: öäü€
3
+
4
+ global $visibleCatIds;
5
+ $visibleCatIds=array();
6
+
7
+ ?>
8
+
9
+ <div class="eurotext_layertitle"><?php echo $this->__("Select categories"); ?></div>
10
+ <div class="eurotext_layerstrip"></div>
11
+
12
+ <table cellpadding="0" cellspacing="0" style="width:100%;border:1px solid #E6E6E6;">
13
+ <tr>
14
+ <td class="eurotext_layercellheader"><?php echo $this->__("Select categories"); ?></td>
15
+ <td class="eurotext_layercellheader" style="border-left:1px solid #E6E6E6;"><?php echo $this->__("Selected categories"); ?></td>
16
+ </tr>
17
+ <tr>
18
+ <td style="width:50%;vertical-align:top;">
19
+ <div style="margin:20px;">
20
+ <?php
21
+
22
+ function drawCategory($category, $openedPathIds, $selectedCategoryIds, $level)
23
+ {
24
+ global $visibleCatIds;
25
+
26
+ $category_id=$category->getId();
27
+
28
+ $checkStr="";
29
+ if (in_array($category_id,$selectedCategoryIds))
30
+ {
31
+ $checkStr="checked='checked'";
32
+ }
33
+
34
+ array_push($visibleCatIds,$category_id);
35
+
36
+ echo "<table cellpadding=0 cellspacing=0>";
37
+ echo "<tr>";
38
+ echo " <td class='et_tc'>&nbsp;</td>";
39
+ echo " <td class='et_tc'><input ".$checkStr." type='checkbox' id='et_selcategory1_".$category_id."' class='et_selcategoryitem et_selcategory_".$category_id."' onchange=\"eurotext_selectcategory('1','".$category_id."')\" /></td>";
40
+ echo " <td class='et_tc'>";
41
+ echo " <div>".$category->getName()."</div>";
42
+
43
+ {
44
+ echo " <div>";
45
+ $childs=$category->getChildrenCategories();
46
+ if ($childs!=null)
47
+ {
48
+ foreach($childs as $child)
49
+ {
50
+ drawCategory($child, $openedPathIds, $selectedCategoryIds, ($level+1));
51
+ }
52
+ }
53
+ echo " </div>";
54
+ }
55
+
56
+ echo " </td>";
57
+ echo "</tr>";
58
+ echo "</table>";
59
+
60
+ }
61
+
62
+ $root=$this->getRootCategory();
63
+ $openedPathIds=$this->getOpenedPathIds();
64
+ $selectedCategoryIds=$this->getSelectedCategoryIds();
65
+
66
+ drawCategory($root,$openedPathIds,$selectedCategoryIds,0);
67
+ ?>
68
+ </div>
69
+
70
+ <?php
71
+ echo "<div style='margin:20px;'>";
72
+ //echo "<div><a href='#' class='eurotext_selectcategories_allvisible'>".$this->__("Select all categories displayed")."</a></div>";
73
+ echo "<div><a href='#' class='eurotext_selectcategories_all'>".$this->__("Select all categories")."</a></div>";
74
+ echo "<div><a href='#' class='eurotext_selectcategories_none'>".$this->__("No selection")."</a></div>";
75
+ echo "</div>";
76
+ ?>
77
+
78
+ </td>
79
+ <td style="vertical-align:top;background-color:#F2F2F2;">
80
+ <div style="margin:20px;">
81
+ <div id="et_saveinfo"><?php $this->__("Loading - please wait..."); ?></div>
82
+ <div id="selectedcategories_result"></div>
83
+ </div>
84
+ </td>
85
+ </tr>
86
+ </table>
87
+ <div style="text-align:center;margin-top:10px;"><a href="#" onclick="return eurotext_closeme()" class="et_btt"><?php echo $this->__("Save"); ?></a></div>
88
+ <div>&nbsp;</div>
89
+
90
+ <script>
91
+ var eurotext_selectcategories_url="<?php echo $this->getSelectCategoriesUrl(); ?>";
92
+ var eurotext_selectcategories_saveurl="<?php echo $this->getSelectCategoriesSaveUrl(); ?>";
93
+ var eurotext_formkey="<?php echo Mage::getSingleton('core/session')->getFormKey(); ?>";
94
+ var eurotext_projectid="<?php echo $this->getRequest()->getParam("id"); ?>";
95
+
96
+ jQuery(document).ready(function()
97
+ {
98
+ eurotext_selectedcategories_send(new Array(),true,null);
99
+
100
+ jQuery(".eurotext_selectcategories_all").click(function()
101
+ {
102
+ var postdata=new Array();
103
+
104
+ <?php
105
+ echo "postdata['cnt']=".count($visibleCatIds).";";
106
+ for($i=0; $i<count($visibleCatIds); $i++)
107
+ {
108
+ echo "postdata['category_id_".$i."']=".$visibleCatIds[$i].";";
109
+ echo "postdata['set_".$i."']='enabled';";
110
+ }
111
+ ?>
112
+
113
+ jQuery(".et_selcategoryitem").attr("checked","checked");
114
+
115
+ eurotext_selectedcategories_send(postdata,true,null);
116
+ });
117
+
118
+ jQuery(".eurotext_selectcategories_none").click(function()
119
+ {
120
+ var postdata=new Array();
121
+ postdata['cnt']=0;
122
+ postdata['select']='none';
123
+
124
+ jQuery(".et_selcategoryitem").attr("checked",false);
125
+
126
+ eurotext_selectedcategories_send(postdata,true,null);
127
+ });
128
+
129
+ });
130
+ </script>
app/design/adminhtml/base/default/template/eurotext/translationmanager/selectcmspages.phtml ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // allow utf8-detection: öäü€
3
+
4
+ $visibleCMSPages=array();
5
+ $visibleCMSBlocks=array();
6
+ ?>
7
+
8
+ <div class="eurotext_layertitle"><?php echo $this->__("Select CMS pages/blocks"); ?></div>
9
+ <div class="eurotext_layerstrip"></div>
10
+
11
+ <table cellpadding="0" cellspacing="0" style="width:100%;border:1px solid #E6E6E6;">
12
+ <tr>
13
+ <td class="eurotext_layercellheader"><?php echo $this->__("Select CMS pages/blocks"); ?></td>
14
+ <td class="eurotext_layercellheader" style="border-left:1px solid #E6E6E6;"><?php echo $this->__("Selected CMS pages/blocks"); ?></td>
15
+ </tr>
16
+ <tr>
17
+ <td style="width:50%;vertical-align:top;">
18
+ <div style="margin:20px;">
19
+ <?php
20
+ // Results:
21
+ echo "<table cellpadding=0 cellspacing=0>";
22
+ echo "<tr>";
23
+ echo " <td class='et_th'>".$this->__("Translate")."</td>";
24
+ echo " <td class='et_th'>".$this->__("Designation")."</td>";
25
+ echo " <td class='et_th'>".$this->__("Identifier")."</td>";
26
+ echo " <td class='et_th'>".$this->__("CMS-Type")."</td>";
27
+ echo " <td class='et_th'>&nbsp;</td>";
28
+ echo "</tr>";
29
+
30
+ $pages=$this->getCMSPages();
31
+ foreach($pages as $page)
32
+ {
33
+ $checkStr="";
34
+ if ($page['checked'])
35
+ {
36
+ $checkStr="checked='checked'";
37
+ }
38
+
39
+ array_push($visibleCMSPages,$page['page_id']);
40
+
41
+ echo "<tr>";
42
+ echo " <td class='et_tc'><input ".$checkStr." type='checkbox' id='et_selcmspage1_".$page['page_id']."' class='et_selcmspage et_selcmspage_".$page['page_id']."' onchange=\"eurotext_selectcmspage('1','".$page['page_id']."')\" /></td>";
43
+ echo " <td class='et_tc'>".$page['title']."</td>";
44
+ echo " <td class='et_tc'>".$page['identifier']."</td>";
45
+ echo " <td class='et_tc' style='white-space:pre;'>".$page['type']."</td>";
46
+ echo " <td class='et_tc'>&nbsp;</td>";
47
+ echo "</tr>";
48
+ }
49
+
50
+ $blocks=$this->getCMSBlocks();
51
+ foreach($blocks as $block)
52
+ {
53
+ $checkStr="";
54
+ if ($block['checked'])
55
+ {
56
+ $checkStr="checked='checked'";
57
+ }
58
+
59
+ array_push($visibleCMSBlocks,$block['block_id']);
60
+
61
+ echo "<tr>";
62
+ echo " <td class='et_tc'><input ".$checkStr." type='checkbox' id='et_selcmsblock1_".$block['block_id']."' class='et_selcmsblock et_selcmsblock_".$block['block_id']."' onchange=\"eurotext_selectcmsblock('1','".$block['block_id']."')\" /></td>";
63
+ echo " <td class='et_tc'>".$block['title']."</td>";
64
+ echo " <td class='et_tc'>".$block['identifier']."</td>";
65
+ echo " <td class='et_tc' style='white-space:pre;'>".$block['type']."</td>";
66
+ echo " <td class='et_tc'>&nbsp;</td>";
67
+ echo "</tr>";
68
+ }
69
+
70
+ echo "</table>";
71
+
72
+
73
+
74
+ ?>
75
+ </div>
76
+ <?php
77
+ echo "<div style='margin:20px;'>";
78
+ //echo "<div><a href='#' class='eurotext_selectcmspages_allvisible'>".$this->__("Select all CMS pages displayed")."</a></div>";
79
+ echo "<div><a href='#' class='eurotext_selectcmspages_all'>".$this->__("Select all CMS pages/blocks")."</a></div>";
80
+ echo "<div><a href='#' class='eurotext_selectcmspages_none'>".$this->__("No selection")."</a></div>";
81
+ echo "</div>";
82
+ ?>
83
+ </td>
84
+ <td style="vertical-align:top;background-color:#F2F2F2;">
85
+ <div style="margin:20px;">
86
+ <div id="et_saveinfo"><?php $this->__("Loading - please wait..."); ?></div>
87
+ <div id="selectedcmspages_result"></div>
88
+ </div>
89
+ </td>
90
+ </tr>
91
+ </table>
92
+ <div style="text-align:center;margin-top:10px;"><a href="#" onclick="return eurotext_closeme()" class="et_btt"><?php echo $this->__("Save"); ?></a></div>
93
+ <div>&nbsp;</div>
94
+
95
+ <script>
96
+ var eurotext_selectcmspages_url="<?php echo $this->getSelectCmsPagesUrl(); ?>";
97
+ var eurotext_selectcmspages_saveurl="<?php echo $this->getSelectCmsPagesSaveUrl(); ?>";
98
+ var eurotext_formkey="<?php echo Mage::getSingleton('core/session')->getFormKey(); ?>";
99
+ var eurotext_projectid="<?php echo $this->getRequest()->getParam("id"); ?>";
100
+
101
+ jQuery(document).ready(function()
102
+ {
103
+ eurotext_selectedcmspages_send(new Array(),null);
104
+
105
+ jQuery(".eurotext_selectcmspages_allvisible").click(function()
106
+ {
107
+ var postdata=new Array();
108
+
109
+ <?php
110
+ echo "postdata['cnt_pages']=".count($visibleCMSPages).";";
111
+ for($i=0; $i<count($visibleCMSPages); $i++)
112
+ {
113
+ echo "postdata['page_id_".$i."']=".$visibleCMSPages[$i].";";
114
+ echo "postdata['setpage_".$i."']='enabled';";
115
+ }
116
+
117
+ echo "postdata['cnt_blocks']=".count($visibleCMSBlocks).";";
118
+ for($i=0; $i<count($visibleCMSBlocks); $i++)
119
+ {
120
+ echo "postdata['block_id_".$i."']=".$visibleCMSBlocks[$i].";";
121
+ echo "postdata['setblock_".$i."']='enabled';";
122
+ }
123
+ ?>
124
+
125
+ jQuery(".et_selcmspage").setPropAndAttr("checked","checked");
126
+ jQuery(".et_selcmsblock").setPropAndAttr("checked","checked");
127
+
128
+ eurotext_selectedcmspages_send(postdata,null);
129
+ });
130
+
131
+ jQuery(".eurotext_selectcmspages_all").click(function()
132
+ {
133
+ var postdata=new Array();
134
+
135
+ <?php
136
+ echo "postdata['cnt_pages']=".count($visibleCMSPages).";";
137
+ for($i=0; $i<count($visibleCMSPages); $i++)
138
+ {
139
+ echo "postdata['page_id_".$i."']=".$visibleCMSPages[$i].";";
140
+ echo "postdata['setpage_".$i."']='enabled';";
141
+ }
142
+
143
+ echo "postdata['cnt_blocks']=".count($visibleCMSBlocks).";";
144
+ for($i=0; $i<count($visibleCMSBlocks); $i++)
145
+ {
146
+ echo "postdata['block_id_".$i."']=".$visibleCMSBlocks[$i].";";
147
+ echo "postdata['setblock_".$i."']='enabled';";
148
+ }
149
+ ?>
150
+
151
+ jQuery(".et_selcmspage").setPropAndAttr("checked","checked");
152
+ jQuery(".et_selcmsblock").setPropAndAttr("checked","checked");
153
+ eurotext_selectedcmspages_send(postdata,null);
154
+ });
155
+
156
+ jQuery(".eurotext_selectcmspages_none").click(function()
157
+ {
158
+ var postdata=new Array();
159
+ postdata['cnt_pages']=0;
160
+ postdata['cnt_blocks']=0;
161
+ postdata['select']='none';
162
+
163
+ jQuery(".et_selcmspage").setPropAndAttr("checked",false);
164
+ jQuery(".et_selcmsblock").setPropAndAttr("checked",false);
165
+
166
+ eurotext_selectedcmspages_send(postdata,null);
167
+ });
168
+ });
169
+ </script>
app/design/adminhtml/base/default/template/eurotext/translationmanager/selectemails.phtml ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // allow utf8-detection: öäü€
3
+
4
+ $visibleEmailTemplates=array();
5
+ ?>
6
+
7
+ <div class="eurotext_layertitle"><?php echo $this->__("Add eMail-Template"); ?></div>
8
+ <div class="eurotext_layerstrip"></div>
9
+
10
+ <table cellpadding="0" cellspacing="0" style="width:100%;border:1px solid #E6E6E6;">
11
+ <tr>
12
+ <td class="eurotext_layercellheader"><?php echo $this->__("Select email templates"); ?></td>
13
+ <td class="eurotext_layercellheader" style="border-left:1px solid #E6E6E6;"><?php echo $this->__("Selected eMail-Templates"); ?></td>
14
+ </tr>
15
+ <tr>
16
+ <td style="width:50%;vertical-align:top;">
17
+ <div style="margin:20px;">
18
+ <?php
19
+ $emailTemplates=$this->getEMailTemplates();
20
+
21
+ // Results:
22
+ echo "<table cellpadding=0 cellspacing=0>";
23
+ echo "<tr>";
24
+ echo " <td class='et_th'>".$this->__("Translate")."</td>";
25
+ echo " <td class='et_th'>".$this->__("Filename")."</td>";
26
+ echo " <td class='et_th'>&nbsp;</td>";
27
+ echo "</tr>";
28
+
29
+ foreach($emailTemplates as $emailTemplate)
30
+ {
31
+ $checkStr="";
32
+ if ($emailTemplate['checked'])
33
+ {
34
+ $checkStr="checked='checked'";
35
+ }
36
+
37
+ array_push($visibleEmailTemplates,$emailTemplate['file_hash']);
38
+
39
+ echo "<tr>";
40
+ echo " <td class='et_tc'><input ".$checkStr." type='checkbox' id='et_selemail1_".$emailTemplate['file_hash']."' class='et_selemail et_selemail_".$emailTemplate['file_hash']."' onchange=\"eurotext_selectemail('1','".$emailTemplate['file_hash']."')\" /></td>";
41
+ echo " <td class='et_tc'>".$emailTemplate['filename']."</td>";
42
+ echo " <td class='et_tc'>&nbsp;</td>";
43
+ echo "</tr>";
44
+ }
45
+ echo "</table>";
46
+ ?>
47
+ </div>
48
+ <?php
49
+ echo "<div style='margin:20px;'>";
50
+ //echo "<div><a href='#' class='eurotext_selectemails_allvisible'>".$this->__("Select all email templates displayed")."</a></div>";
51
+ echo "<div><a href='#' class='eurotext_selectemails_all'>".$this->__("Select all email templates")."</a></div>";
52
+ echo "<div><a href='#' class='eurotext_selectemails_none'>".$this->__("No selection")."</a></div>";
53
+ echo "</div>";
54
+ ?>
55
+ </td>
56
+ <td style="vertical-align:top;background-color:#F2F2F2;">
57
+ <div style="margin:20px;">
58
+ <div id="et_saveinfo"><?php $this->__("Loading - please wait..."); ?></div>
59
+ <div id="selectedemails_result"></div>
60
+ </div>
61
+ </td>
62
+ </tr>
63
+ </table>
64
+ <div style="text-align:center;margin-top:10px;"><a href="#" onclick="return eurotext_closeme()" class="et_btt"><?php echo $this->__("Save"); ?></a></div>
65
+ <div>&nbsp;</div>
66
+
67
+ <script>
68
+ var eurotext_selectemails_url="<?php echo $this->getSelectEMailsUrl(); ?>";
69
+ var eurotext_selectemails_saveurl="<?php echo $this->getSelectEMailsSaveUrl(); ?>";
70
+ var eurotext_formkey="<?php echo Mage::getSingleton('core/session')->getFormKey(); ?>";
71
+ var eurotext_projectid="<?php echo $this->getRequest()->getParam("id"); ?>";
72
+
73
+ jQuery(document).ready(function()
74
+ {
75
+ eurotext_selectedemails_send(new Array(),null);
76
+
77
+ jQuery(".eurotext_selectemails_all").click(function()
78
+ {
79
+ var postdata=new Array();
80
+
81
+ <?php
82
+ echo "postdata['cnt']=".count($visibleEmailTemplates).";";
83
+ for($i=0; $i<count($visibleEmailTemplates); $i++)
84
+ {
85
+ echo "postdata['file_hash_".$i."']='".$visibleEmailTemplates[$i]."';\r\n";
86
+ echo "postdata['set_".$i."']='enabled';\r\n";
87
+ }
88
+ ?>
89
+
90
+ jQuery(".et_selemail").attr("checked","checked");
91
+ jQuery(".et_selemail").prop("checked",true); // for some reason Chrome ignores the line above (but works on every other page) ;/
92
+
93
+ eurotext_selectedemails_send(postdata,null);
94
+
95
+ return false;
96
+ });
97
+
98
+ jQuery(".eurotext_selectemails_none").click(function()
99
+ {
100
+ var postdata=new Array();
101
+ postdata['cnt']=0;
102
+ postdata['select']='none';
103
+
104
+ eurotext_selectemails_ignore=true;
105
+
106
+ jQuery(".et_selemail").attr("checked",false);
107
+
108
+ eurotext_selectedemails_send(postdata,null);
109
+
110
+ return false;
111
+ });
112
+ });
113
+ </script>
app/design/adminhtml/base/default/template/eurotext/translationmanager/selectlangfiles.phtml ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // allow utf8-detection: öäü€
3
+
4
+ $visibleLangfiles=array();
5
+ ?>
6
+
7
+ <div class="eurotext_layertitle"><?php echo $this->__("Select language files"); ?></div>
8
+ <div class="eurotext_layerstrip"></div>
9
+
10
+ <table cellpadding="0" cellspacing="0" style="width:100%;border:1px solid #E6E6E6;">
11
+ <tr>
12
+ <td class="eurotext_layercellheader"><?php echo $this->__("Select language files"); ?></td>
13
+ <td class="eurotext_layercellheader" style="border-left:1px solid #E6E6E6;"><?php echo $this->__("Selected language files"); ?></td>
14
+ </tr>
15
+ <tr>
16
+ <td style="width:50%;vertical-align:top;">
17
+ <div style="margin:20px;">
18
+ <?php
19
+ $langfiles=$this->getLangfiles();
20
+
21
+ // Results:
22
+ echo "<table cellpadding=0 cellspacing=0>";
23
+ echo "<tr>";
24
+ echo " <td class='et_th'>".$this->__("Translate")."</td>";
25
+ echo " <td class='et_th'>".$this->__("Filename")."</td>";
26
+ echo " <td class='et_th'>&nbsp;</td>";
27
+ echo "</tr>";
28
+
29
+ foreach($langfiles as $langfile)
30
+ {
31
+ $checkStr="";
32
+ if ($langfile['checked'])
33
+ {
34
+ $checkStr="checked='checked'";
35
+ }
36
+
37
+ array_push($visibleLangfiles,$langfile['line_hash']);
38
+
39
+ echo "<tr>";
40
+ echo " <td class='et_tc'><input ".$checkStr." type='checkbox' id='et_sellangfile1_".$langfile['line_hash']."' class='et_sellangfile et_sellangfile_".$langfile['line_hash']."' onchange=\"eurotext_selectlangfile('1','".$langfile['line_hash']."')\" /></td>";
41
+ echo " <td class='et_tc'>".$langfile['filename']."</td>";
42
+ echo " <td class='et_tc'>&nbsp;</td>";
43
+ echo "</tr>";
44
+ }
45
+ echo "</table>";
46
+
47
+ ?>
48
+ </div>
49
+ <?php
50
+ echo "<div style='margin:20px;'>";
51
+ echo "<div><a href='#' class='eurotext_selectlangfiles_allvisible'>".$this->__("Select all language files displayed")."</a></div>";
52
+ echo "<div><a href='#' class='eurotext_selectlangfiles_all'>".$this->__("Select all language files")."</a></div>";
53
+ echo "<div><a href='#' class='eurotext_selectlangfiles_none'>".$this->__("No selection")."</a></div>";
54
+ echo "</div>";
55
+ ?>
56
+ </td>
57
+ <td style="vertical-align:top;background-color:#F2F2F2;">
58
+ <div style="margin:20px;">
59
+ <div id="et_saveinfo"><?php $this->__("Loading - please wait..."); ?></div>
60
+ <div id="selectedlangfiles_result"></div>
61
+ </div>
62
+ </td>
63
+ </tr>
64
+ </table>
65
+ <div style="text-align:center;margin-top:10px;"><a href="#" onclick="return eurotext_closeme()" class="et_btt"><?php echo $this->__("Save"); ?></a></div>
66
+ <div>&nbsp;</div>
67
+
68
+ <script>
69
+ var eurotext_selectlangfiles_url="<?php echo $this->getSelectLangfilesUrl(); ?>";
70
+ var eurotext_selectlangfiles_saveurl="<?php echo $this->getSelectLangfilesSaveUrl(); ?>";
71
+ var eurotext_formkey="<?php echo Mage::getSingleton('core/session')->getFormKey(); ?>";
72
+ var eurotext_projectid="<?php echo $this->getRequest()->getParam("id"); ?>";
73
+
74
+ jQuery(document).ready(function()
75
+ {
76
+ eurotext_selectedlangfiles_send(new Array(),null);
77
+
78
+ jQuery(".eurotext_selectlangfiles_allvisible").click(function()
79
+ {
80
+ var postdata=new Array();
81
+
82
+ <?php
83
+ echo "postdata['cnt']=".count($visibleLangfiles).";";
84
+ for($i=0; $i<count($visibleLangfiles); $i++)
85
+ {
86
+ echo "postdata['langfile_linehash_".$i."']='".$visibleLangfiles[$i]."';\r\n";
87
+ echo "postdata['set_".$i."']='enabled';\r\n";
88
+ }
89
+ ?>
90
+
91
+ jQuery(".et_sellangfile").attr("checked","checked");
92
+
93
+ eurotext_selectedlangfiles_send(postdata,null);
94
+
95
+ return false;
96
+ });
97
+
98
+ jQuery(".eurotext_selectlangfiles_all").click(function()
99
+ {
100
+ var postdata=new Array();
101
+
102
+ <?php
103
+ echo "postdata['cnt']=".count($visibleLangfiles).";";
104
+ for($i=0; $i<count($visibleLangfiles); $i++)
105
+ {
106
+ echo "postdata['langfile_linehash_".$i."']='".$visibleLangfiles[$i]."';\r\n";
107
+ echo "postdata['set_".$i."']='enabled';\r\n";
108
+ }
109
+ ?>
110
+
111
+ jQuery(".et_sellangfile").attr("checked","checked");
112
+
113
+ eurotext_selectedlangfiles_send(postdata,null);
114
+
115
+ return false;
116
+ });
117
+
118
+ jQuery(".eurotext_selectlangfiles_none").click(function()
119
+ {
120
+ var postdata=new Array();
121
+ postdata['cnt']=0;
122
+ postdata['select']='none';
123
+
124
+ jQuery(".et_selproductitem").attr("checked",false);
125
+
126
+ eurotext_selectedlangfiles_send(postdata,null);
127
+ });
128
+ });
129
+ </script>
app/design/adminhtml/base/default/template/eurotext/translationmanager/selectproducts.phtml ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // allow utf8-detection: öäü€
3
+
4
+ function selected_str($curval,$checkval)
5
+ {
6
+ if ($curval==$checkval)
7
+ {
8
+ return "selected='selected'";
9
+ }
10
+
11
+ return "";
12
+ }
13
+
14
+ $visibleProductIds=array();
15
+ $page_size=20;
16
+ $current_page=1;
17
+
18
+ $this->updateFilterData();
19
+
20
+ $val_filter_status=$this->getFilterStatus();
21
+ $val_filter_stock=$this->getFilterStock();
22
+ $val_filter_product_type=$this->getFilterProductType();
23
+
24
+ ?>
25
+
26
+ <div class="eurotext_layertitle"><?php echo $this->__("Select products"); ?></div>
27
+ <div class="eurotext_layerstrip"></div>
28
+
29
+ <table cellpadding="0" cellspacing="0" style="width:100%;border:1px solid #E6E6E6;">
30
+ <tr>
31
+ <td class="eurotext_layercellheader"><?php echo $this->__("Filter"); ?></td>
32
+ <td class="eurotext_layercellheader" style="border-left:1px solid #E6E6E6;"><?php echo $this->__("Select products"); ?></td>
33
+ <td class="eurotext_layercellheader" style="border-left:2px solid #E6E6E6;"><?php echo $this->__("Selected products"); ?></td>
34
+ </tr>
35
+ <tr>
36
+ <td style="vertical-align:top;min-width:250px;">
37
+
38
+ <div style="margin:10px;">
39
+ <table>
40
+ <tr>
41
+ <td><?php echo $this->__("Status"); ?>: &nbsp;</td>
42
+ <td><select autocomplete="off" id="filter_status">
43
+ <option value="1" <?php if ($val_filter_status==1) { echo "selected='selected'"; } ?>><?php echo $this->__("Enabled"); ?></option>
44
+ <option value="2" <?php if ($val_filter_status==2) { echo "selected='selected'"; } ?>><?php echo $this->__("Disabled"); ?></option>
45
+ </select></td>
46
+ </tr>
47
+ <tr>
48
+ <td><?php echo $this->__("Stock Availability"); ?>: &nbsp;</td>
49
+ <td><select autocomplete="off" id="filter_stock">
50
+ <option value="1" <?php if ($val_filter_stock==1) { echo "selected='selected'"; } ?>><?php echo $this->__("In Stock"); ?></option>
51
+ <option value="0" <?php if ($val_filter_stock==0) { echo "selected='selected'"; } ?>><?php echo $this->__("Out of Stock"); ?></option>
52
+ </select></td>
53
+ </tr>
54
+ <tr>
55
+ <td><?php echo $this->__("Product Type"); ?>: &nbsp;</td>
56
+ <td><select autocomplete="off" id="filter_product_type">
57
+ <option value="" <?php if ($val_filter_product_type=='') { echo "selected='selected'"; } ?>><?php echo $this->__("Show all"); ?></option>
58
+ <option value="simple" <?php if ($val_filter_product_type=='simple') { echo "selected='selected'"; } ?>><?php echo $this->__("Simple Product"); ?></option>
59
+ <option value="grouped" <?php if ($val_filter_product_type=='grouped') { echo "selected='selected'"; } ?>><?php echo $this->__("Grouped Product"); ?></option>
60
+ <option value="configurable" <?php if ($val_filter_product_type=='configurable') { echo "selected='selected'"; } ?>><?php echo $this->__("Configurable Product"); ?></option>
61
+ <option value="virtual" <?php if ($val_filter_product_type=='virtual') { echo "selected='selected'"; } ?>><?php echo $this->__("Virtual Product"); ?></option>
62
+ <option value="bundle" <?php if ($val_filter_product_type=='bundle') { echo "selected='selected'"; } ?>><?php echo $this->__("Bundle Product"); ?></option>
63
+ <option value="downloadable" <?php if ($val_filter_product_type=='downloadable') { echo "selected='selected'"; } ?>><?php echo $this->__("Downloadable Product"); ?></option>
64
+ </select></td>
65
+ </tr>
66
+ </table>
67
+ </div>
68
+
69
+
70
+ <table style="border-spacing:0px;margin-top:15px;width:100%;">
71
+ <tr>
72
+ <td class="eurotext_layercellheader"><?php echo $this->__("Category filter"); ?></td>
73
+ </tr>
74
+ </table>
75
+ <div style="margin:10px;">
76
+ <?php echo $this->getCategoryTreeHTML(); ?>
77
+ </div>
78
+ </td>
79
+ <td style="vertical-align:top;">
80
+
81
+ <div style="margin:15px;">
82
+
83
+ <?php
84
+ $result=$this->getSearchResult();
85
+ ?>
86
+
87
+ <div style="margin:10px;padding:10px;">
88
+ <table cellpadding="0" cellspacing="0">
89
+ <tr>
90
+ <td><input type="text" id="find_products" style="min-width:250px;" value="<?php echo htmlentities($result['find'],ENT_COMPAT,'UTF-8'); ?>" placeholder="<?php echo $this->__("Enter designation or SKU"); ?>" /></td>
91
+ <td>&nbsp; <a href="#" onclick="return eurotext_findproducts()"><span class="eurotext_findbtt"></span></a></td>
92
+ </tr>
93
+ </table>
94
+ </div>
95
+
96
+ <!-- Search result -->
97
+ <?php
98
+ if ($result['result_count']>0)
99
+ {
100
+ $pager="";
101
+
102
+ $current_page=$result['page_current'];
103
+ $page_size=$result['page_size'];
104
+
105
+ $prev_page=($current_page-1);
106
+ $pager_prevpage="";
107
+ if ($prev_page<1)
108
+ {
109
+ $pager_prevpage="<span class='eurotext_back_disabled'></span>";
110
+ }
111
+ else
112
+ {
113
+ $page_url=$this->getSelectProductsUrl()."page/".$prev_page."/find/".urlencode($result['find'])."/pagesize/".$page_size."/catids/".implode(",",$this->getOpenCatIds());
114
+ $pager_prevpage="<a href='".$page_url."'><span class='eurotext_back_enabled'></span></a>";
115
+ }
116
+
117
+ $next_page=($current_page+1);
118
+ $pager_nextpage="";
119
+ if ($next_page>$result['page_last'])
120
+ {
121
+ $pager_nextpage="<span class='eurotext_next_disabled'></span>";
122
+ }
123
+ else
124
+ {
125
+ $page_url=$this->getSelectProductsUrl()."page/".$next_page."/find/".urlencode($result['find'])."/pagesize/".$page_size."/catids/".implode(",",$this->getOpenCatIds());
126
+ $pager_nextpage="<a href='".$page_url."'><span class='eurotext_next_enabled'></span></a>";
127
+ }
128
+
129
+ $maxSeiten="";
130
+ if ($result['page_last']==1)
131
+ {
132
+ $maxSeiten="von 1 Seite";
133
+ }
134
+ else
135
+ {
136
+ $maxSeiten="von ".$result['page_last']." Seiten";
137
+ }
138
+
139
+ $pager="<table cellpadding=0 cellspacing=0 style='margin:15px;'>";
140
+ $pager.=" <tr>";
141
+ $pager.=" <td class='eurotext_vcenter'>".$this->__("Page")." &nbsp;</td>";
142
+ $pager.=" <td>&nbsp;</td>";
143
+ $pager.=" <td class='eurotext_vcenter'>$pager_prevpage </td>";
144
+ $pager.=" <td>&nbsp;</td>";
145
+ $pager.=" <td class='eurotext_vcenter'><input style='text-align:center;width:35px;' type='text' class='eurotext_page' value='$current_page' autocomplete='off' /> </td>";
146
+ $pager.=" <td>&nbsp;</td>";
147
+ $pager.=" <td class='eurotext_vcenter'>$pager_nextpage </td>";
148
+ $pager.=" <td>&nbsp;</td>";
149
+ $pager.=" <td class='eurotext_vcenter'>&nbsp; $maxSeiten</td>";
150
+ $pager.=" <td style='width:30px;'>&nbsp;</td>";
151
+ $pager.=" <td class='eurotext_vcenter'>";
152
+ $pager.=" <select class='eurotext_pagesize'>";
153
+ $pager.=" <option value='20' ".selected_str($page_size,20).">20</option>";
154
+ $pager.=" <option value='30' ".selected_str($page_size,30).">30</option>";
155
+ $pager.=" <option value='50' ".selected_str($page_size,50).">50</option>";
156
+ $pager.=" <option value='100' ".selected_str($page_size,100).">100</option>";
157
+ $pager.=" <option value='200' ".selected_str($page_size,200).">200</option>";
158
+ $pager.=" </select>";
159
+ $pager.=" </td>";
160
+ $pager.=" <td>&nbsp;</td>";
161
+ $pager.=" <td class='eurotext_vcenter'>".$this->__('Per Page')."</td>";
162
+ $pager.=" </tr>";
163
+ $pager.="</table>";
164
+
165
+ echo $pager;
166
+
167
+ // Results:
168
+ echo "<table cellpadding=0 cellspacing=0>";
169
+ echo "<tr>";
170
+ echo " <td class='et_th'>".$this->__("Translate")."</td>";
171
+ echo " <td class='et_th'>".$this->__("SKU")."</td>";
172
+ echo " <td class='et_th'>".$this->__("Designation")."</td>";
173
+ echo " <td class='et_th'>&nbsp;</td>";
174
+ echo "</tr>";
175
+
176
+ $products=$result['products'];
177
+
178
+ $alt=0;
179
+ foreach($products as $product)
180
+ {
181
+ $checkStr="";
182
+ if ($product['checked'])
183
+ {
184
+ $checkStr="checked='checked'";
185
+ }
186
+
187
+ $alt=1-$alt;
188
+
189
+ // Remember visible products:
190
+ array_push($visibleProductIds,$product['entity_id']);
191
+
192
+ echo "<tr>";
193
+ echo " <td class='et_tc eurotext_r".$alt."'><input autocomplete='off' ".$checkStr." type='checkbox' id='et_selproduct1_".$product['entity_id']."' class='et_selproductitem et_selproduct_".$product['entity_id']."' onchange=\"eurotext_selectproduct('1','".$product['entity_id']."')\" /></td>";
194
+ echo " <td class='et_tc eurotext_r".$alt."'>".$product['sku']."</td>"; // (ID: ".$product['entity_id'].")
195
+ echo " <td class='et_tc eurotext_r".$alt."'>".$product['article_name']."</td>";
196
+ echo " <td class='et_tc eurotext_r".$alt."'>&nbsp;</td>";
197
+ echo "</tr>";
198
+ }
199
+
200
+ echo "</table>";
201
+
202
+ echo $pager;
203
+
204
+ echo "<div>";
205
+ echo "<div><a href='#' class='eurotext_selectproducts_allvisible'>".$this->__("Select all products displayed")."</a></div>";
206
+ echo "<div><a href='#' class='eurotext_selectproducts_all'>".$this->__("Select all products in this category")."</a></div>";
207
+ echo "<div><a href='#' class='eurotext_selectproducts_none'>".$this->__("No selection")."</a></div>";
208
+ echo "</div>";
209
+ }
210
+ else
211
+ {
212
+ echo $this->__("No result");
213
+ }
214
+
215
+ ?>
216
+ </div>
217
+
218
+ </td>
219
+ <td style="vertical-align:top;background-color:#F2F2F2;">
220
+ <div style="margin:20px;">
221
+ <div id="et_saveinfo"><?php $this->__("Loading - Please wait")."..."; ?></div>
222
+ <div id="selectedproducts_result"></div>
223
+ </div>
224
+ </td>
225
+ </tr>
226
+ </table>
227
+ <div style="text-align:center;margin-top:10px;"><a href="#" onclick="return eurotext_closeme()" class="et_btt"><?php echo $this->__("Save"); ?></a></div>
228
+ <div>&nbsp;</div>
229
+
230
+ <script>
231
+ var eurotext_selectproducts_url="<?php echo $this->getSelectProductsUrl(); ?>";
232
+ var eurotext_selectproducts_saveurl="<?php echo $this->getSelectProductsSaveUrl(); ?>";
233
+ var eurotext_pagesize="<?php echo $page_size; ?>";
234
+ var eurotext_page="<?php echo $current_page; ?>";
235
+ var eurotext_catids="<?php echo implode(",",$this->getOpenCatIds()); ?>";
236
+ var eurotext_formkey="<?php echo Mage::getSingleton('core/session')->getFormKey(); ?>";
237
+ var eurotext_projectid="<?php echo $this->getRequest()->getParam("id"); ?>";
238
+ jQuery(document).ready(function()
239
+ {
240
+ eurotext_selectedproducts_send(new Array(),null);
241
+
242
+ jQuery("#filter_status").change(function()
243
+ {
244
+ eurotext_findproducts();
245
+
246
+ return false;
247
+ });
248
+
249
+ jQuery("#filter_stock").change(function()
250
+ {
251
+ eurotext_findproducts();
252
+
253
+ return false;
254
+ });
255
+
256
+ jQuery("#filter_product_type").change(function()
257
+ {
258
+ eurotext_findproducts();
259
+
260
+ return false;
261
+ });
262
+
263
+
264
+ jQuery(".eurotext_catsel").click(function()
265
+ {
266
+ var catid=jQuery(this).attr("x-catid");
267
+ var catstate=jQuery(this).attr("x-state");
268
+
269
+ postdata=new Array();
270
+ postdata['catid']=catid;
271
+
272
+ if (catstate=="checked")
273
+ {
274
+ postdata['select']='unsetcat';
275
+ }
276
+ else
277
+ {
278
+ postdata['select']='setcat';
279
+ }
280
+
281
+ eurotext_selectedproducts_send(postdata,null);
282
+ });
283
+
284
+ jQuery(".eurotext_selectproducts_allvisible").click(function()
285
+ {
286
+ var postdata=new Array();
287
+
288
+ <?php
289
+ echo "postdata['cnt']=".count($visibleProductIds).";";
290
+ for($i=0; $i<count($visibleProductIds); $i++)
291
+ {
292
+ echo "postdata['product_id_".$i."']=".$visibleProductIds[$i].";";
293
+ echo "postdata['set_".$i."']='enabled';";
294
+ }
295
+ ?>
296
+
297
+ jQuery(".et_selproductitem").attr("checked","checked");
298
+
299
+ eurotext_selectedproducts_send(postdata,null);
300
+ });
301
+
302
+ jQuery(".eurotext_selectproducts_all").click(function()
303
+ {
304
+ var postdata=new Array();
305
+
306
+ var postdata=new Array();
307
+ postdata['cnt']=0;
308
+ postdata['select']='all';
309
+
310
+ jQuery(".et_selproductitem").attr("checked","checked");
311
+
312
+ eurotext_selectedproducts_send(postdata,null);
313
+ });
314
+
315
+ jQuery(".eurotext_selectproducts_none").click(function()
316
+ {
317
+ var postdata=new Array();
318
+ postdata['cnt']=0;
319
+ postdata['select']='none';
320
+
321
+ jQuery(".et_selproductitem").attr("checked",false);
322
+
323
+ eurotext_selectedproducts_send(postdata,null);
324
+ });
325
+
326
+ jQuery('#find_products').keypress(function (e)
327
+ {
328
+ if (e.which == 13)
329
+ {
330
+ e.preventDefault();
331
+ eurotext_findproducts();
332
+ }
333
+ });
334
+
335
+ jQuery('.eurotext_page').keypress(function (e)
336
+ {
337
+ eurotext_page=jQuery(this).val();
338
+ if (e.which == 13)
339
+ {
340
+ e.preventDefault();
341
+ eurotext_findproducts();
342
+ }
343
+ });
344
+
345
+ jQuery('.eurotext_pagesize').change(function (e)
346
+ {
347
+ eurotext_pagesize=jQuery(this).val();
348
+ eurotext_findproducts();
349
+ });
350
+ });
351
+ </script>
app/design/adminhtml/base/default/template/eurotext/translationmanager/settings.phtml ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // allow utf8-detection: öäü€
3
+
4
+ // Read Settings:
5
+ $helper=Mage::helper('eurotext_translationmanager');;
6
+ $et_username=$helper->getSetting("eurotext_username");
7
+ $et_password=Mage::helper('core')->decrypt($helper->getSetting("eurotext_password"));
8
+ $et_customerid=$helper->getSetting("eurotext_customerid");
9
+
10
+ $et_products_per_file=$helper->getSetting("et_products_per_file","20");
11
+ $et_categories_per_file=$helper->getSetting("et_categories_per_file","20");
12
+ $et_cmspages_per_file=$helper->getSetting("et_cmspages_per_file","20");
13
+
14
+ // Test ftp login:
15
+ $statusMessage="";
16
+ if (trim($et_username)!="")
17
+ {
18
+ $rv=$helper->testFtpConnection();
19
+ $statusMessage=$rv['statusmessage'];
20
+ }
21
+
22
+ if ($helper->getDebugMode())
23
+ {
24
+ echo '<div style="background-color:red;padding:3px;color:white;">Debug-Mode is active!</div>';
25
+ }
26
+
27
+
28
+ echo "<script type='text/javascript'>";
29
+ echo "var eurotext_saveurl='".$this->getSaveUrl()."';";
30
+ echo "var eurotext_formkey='".Mage::getSingleton('core/session')->getFormKey()."';";
31
+ echo "</script>";
32
+
33
+ ?>
34
+
35
+ <div class="eurotext_confighead"><?php echo $this->__("Settings"); ?></div>
36
+ <div class="eurotext_config">
37
+ <div style="padding:10px;">
38
+ <table cellpadding="0" cellspacing="0">
39
+ <tr>
40
+ <td class="eurotext_edittext"><?php echo $this->__("User name"); ?>:</td>
41
+ <td class="eurotext_edittext">&nbsp;</td>
42
+ <td class="eurotext_edittext"><input type="text" id="et_username" value="<?php echo $et_username; ?>" autocomplete="off" /></td>
43
+ <td class="eurotext_edittext"></td>
44
+ </tr>
45
+ <tr>
46
+ <td class="eurotext_edittext"><?php echo $this->__("Password"); ?>:</td>
47
+ <td class="eurotext_edittext">&nbsp;</td>
48
+ <td class="eurotext_edittext"><input type="password" id="et_password" value="<?php echo $et_password; ?>" autocomplete="off" /></td>
49
+ <td class="eurotext_edittext"></td>
50
+ </tr>
51
+ <tr>
52
+ <td class="eurotext_edittext"><?php echo $this->__("Customer ID"); ?>:</td>
53
+ <td class="eurotext_edittext">&nbsp;</td>
54
+ <td class="eurotext_edittext"><input type="text" id="et_customerid" value="<?php echo $et_customerid; ?>" autocomplete="off" /></td>
55
+ <td class="eurotext_edittext"></td>
56
+ </tr>
57
+ <tr>
58
+ <td class="eurotext_edittext" colspan="4">&nbsp;</td>
59
+ </tr>
60
+ <tr>
61
+ <td class="eurotext_edittext"><?php echo $this->__("Products per file"); ?>:</td>
62
+ <td class="eurotext_edittext">&nbsp;</td>
63
+ <td class="eurotext_edittext"><input type="number" id="et_products_per_file" value="<?php echo $et_products_per_file; ?>" autocomplete="off" /></td>
64
+ <td class="eurotext_edittext">(<?php echo $this->__("Minimum"); ?>: <?php echo $helper->getExportProductsMinPerFile(); ?>)</td>
65
+ </tr>
66
+ <tr>
67
+ <td class="eurotext_edittext"><?php echo $this->__("Categories per file"); ?>:</td>
68
+ <td class="eurotext_edittext">&nbsp;</td>
69
+ <td class="eurotext_edittext"><input type="number" id="et_categories_per_file" value="<?php echo $et_categories_per_file; ?>" autocomplete="off" /></td>
70
+ <td class="eurotext_edittext">(<?php echo $this->__("Minimum"); ?>: <?php echo $helper->getExportCategoriesMinPerFile(); ?>)</td>
71
+ </tr>
72
+ <!-- <tr>
73
+ <td class="eurotext_edittext"><?php echo $this->__("CMS pages per file"); ?>:</td>
74
+ <td class="eurotext_edittext">&nbsp;</td>
75
+ <td class="eurotext_edittext"><input type="number" id="et_cmspages_per_file" value="<?php echo $et_cmspages_per_file; ?>" autocomplete="off" /></td>
76
+ <td class="eurotext_edittext"><img title="" src="" /></td>
77
+ </tr>-->
78
+ <tr>
79
+ <td class="eurotext_edittext" colspan="4">&nbsp;</td>
80
+ </tr>
81
+ <tr>
82
+ <td class="eurotext_edittext" colspan="4" style="text-align:center;"><a id="eurotext_savebtt" href="#" onclick="return eurotext_savesettings()" class="et_btt"><?php echo $this->__("Save"); ?></a></td>
83
+ </tr>
84
+ </table>
85
+
86
+ <div>&nbsp;</div>
87
+ <div><?php echo $statusMessage; ?></div>
88
+
89
+ <?php if ($helper->urlKeyScopeIsGlobal())
90
+ {
91
+ ?>
92
+ <div style="margin-top:20px;">
93
+ <?php echo $this->__('Currently your Magento installation is not capable to save translated url keys for products and categories.'); ?><br>
94
+ <a href="<?php echo $this->getUpgradeScopeUrl(); ?>"><?php echo $this->__('Click here to change the scope of url keys to enable translation of url keys for products and categories. (Attention: This can not be undone!)'); ?></a>
95
+ </div>
96
+ <?php
97
+ }
98
+ else
99
+ {
100
+ ?>
101
+ <div style="margin-top:20px;">
102
+ <?php echo $this->__("Your Magento installation allows the translation of URLs of categories and products."); ?></a>
103
+ </div>
104
+ <?php
105
+ }
106
+ ?>
107
+ </div>
108
+ </div>
109
+
110
+ <div>&nbsp;</div>
111
+
112
+ <div class="eurotext_confighead"><?php echo $this->__("Language settings"); ?></div>
113
+ <div class="eurotext_config">
114
+ <div style="padding:10px;">
115
+ <table cellpadding="0" cellspacing="0">
116
+ <tr>
117
+ <td class='et_th' colspan="2"><?php echo $this->__("Your Magento languages"); ?>:</td>
118
+ <td class='et_th'>&nbsp;</td>
119
+ <td class='et_th'>&nbsp;</td>
120
+ <td class='et_th'>&nbsp;</td>
121
+ <td class='et_th' colspan="2"><?php echo $this->__("Language of the translation portal"); ?>:</td>
122
+ </tr>
123
+ <?php
124
+ $stores=$this->getStoreviews();
125
+ foreach($stores as $store)
126
+ {
127
+ $localeInfo=$helper->getLocaleInfoByMagentoLocale($store['locale']);
128
+
129
+ echo "<tr>";
130
+ echo "<td class='et_tc'>".$store['name']."</td>";
131
+ echo "<td class='et_tc'>".$store['locale']."</td>";
132
+ echo "<td class='et_tc'>&nbsp;</td>";
133
+ echo "<td class='et_tc'>&lt;----&gt;</td>";
134
+ echo "<td class='et_tc'>&nbsp;</td>";
135
+ echo "<td class='et_tc'>".$localeInfo['lang_name']."</td>";
136
+ echo "<td class='et_tc'>".$localeInfo['locale_eurotext']."</td>";
137
+ echo "</tr>";
138
+ }
139
+ ?>
140
+ </table>
141
+ </div>
142
+ </div>
143
+
144
+ <div>&nbsp;</div>
145
+
146
+ <div class="eurotext_confighead"><?php echo $this->__("System requirements"); ?>:</div>
147
+ <div class="eurotext_config">
148
+ <div style="padding:10px;">
149
+ <?php
150
+
151
+ $items=array();
152
+
153
+ {
154
+ $item=array();
155
+ $item['name']="PHP-Version";
156
+ $item['ok']=(version_compare(PHP_VERSION, '5.2.0') >= 0); // min 5.2.0
157
+ $item['value_now']=PHP_VERSION;
158
+ $item['value_required']="min 5.2.0";
159
+ array_push($items,$item);
160
+ }
161
+
162
+ {
163
+ $item=array();
164
+ $item['name']="FTP-Extension";
165
+ $item['ok']=function_exists("ftp_connect");
166
+ $item['value_now']=function_exists("ftp_connect") ? $this->__("Installed") : $this->__("Missing");
167
+ $item['value_required']=$this->__("Installed");
168
+ array_push($items,$item);
169
+ }
170
+
171
+ {
172
+ $item=array();
173
+ $item['name']="ZIP-Extension";
174
+ $item['ok']=class_exists("ZIPArchive");
175
+ $item['value_now']=class_exists("ZIPArchive") ? $this->__("Installed") : $this->__("Missing");
176
+ $item['value_required']=$this->__("Installed");
177
+ array_push($items,$item);
178
+ }
179
+
180
+ echo "<table cellpadding=0 cellspacing=0>";
181
+ {
182
+ echo "<tr>";
183
+ echo " <td class='et_th'>".$this->__("Designation")."</td>";
184
+ echo " <td class='et_th'>".$this->__("Current value")."</td>";
185
+ echo " <td class='et_th'>".$this->__("Required value")."</td>";
186
+ echo "</tr>";
187
+ }
188
+ foreach($items as $item)
189
+ {
190
+ echo "<tr>";
191
+ echo " <td class='et_tc'>".$item['name']."</td>";
192
+ echo " <td class='et_tc'>".$item['value_now']."</td>";
193
+ echo " <td class='et_tc'>".$item['value_required']."</td>";
194
+ echo "</tr>";
195
+ }
196
+ echo "</table>";
197
+
198
+ echo "<div style='margin-top:20px;'>".$this->__("Current value for 'max_execution_time'").": ".ini_get("max_execution_time")."s</div>";
199
+
200
+ ?>
201
+ </div>
202
+ </div>
app/etc/modules/Eurotext_TranslationManager.xml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ -->
4
+ <config>
5
+ <modules>
6
+ <Eurotext_TranslationManager>
7
+ <active>true</active>
8
+ <codePool>community</codePool>
9
+ </Eurotext_TranslationManager>
10
+ </modules>
11
+ </config>
app/locale/de_DE/Eurotext_TranslationManager.csv ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (All products),(Alle Artikel)
2
+ -- Select storeview --,-- Bitte Storeview wählen --
3
+ "<b>Please note:</b> For safety reasons, we recommend importing to a staging system first. Make sure to backup all your data before importing.",<b>Bitte beachten Sie:</b> Aus Sicherheitsgründen sollte der erste Import in einem Testsystem (Staging) durchgeführt werden. Erstellen Sie vor dem Import ein Backup Ihrer Daten.
4
+ "<b>Select all [...] without existing translation:</b> The module will automatically select all Products, categories, CMS pages and email templates for which no translated text exists in the target language.","<b>Alle [...] ohne vorhandene Übersetzung auswählen:</b> Das Modul wählt für Sie automatisch alle Artikel, Kategorien, CMS-Seiten und/oder E-Mail-Vorlagen aus, für die noch keine Texte in der Zielsprache existieren."
5
+ <i><span style='color:red;'>Important:</span> All previous assignments will be deleted!</i>,<i><span style='color:red;'>Achtung:</span> Alle vorherigen Zuordnungen werden dadurch gelöscht!</i>
6
+ "<i><span style='color:red;'>Important:</span> We recommend using this setting only for the initial translation of a new online shop, as it will trigger a translation of ALL language files for the selected target language!</i>","<i><span style='color:red;'>Achtung:</span> Diese Einstellung empfiehlt sich nur bei der ersten Übersetzung neuer Onlineshops, denn hier werden ALLE Sprachdateien der gewählten Zielsprache neu übersetzt!</i>"
7
+ A storeview does not exist (anymore),Der Storeview existiert nicht (mehr)
8
+ Action,Aktion
9
+ Add eMail-Template,E-Mail-Vorlage hinzufügen
10
+ Additional settings,Weitere Einstellungen
11
+ Already submitted. Project settings can no longer be edited.,Bereits abgeschickt. Projekteinstellungen können nicht mehr geändert werden.
12
+ Analysing,Analysiere
13
+ Analyzing,Analysiere
14
+ Any information you provide is voluntary. Your data will be used for the purpose of logging in to the translation portal and for project processing in your dealings with Eurotext AG only. Your information will not be forwarded to third parties. You can request a deletion of your information at any time by sending an email to <a href='mailto:datenschutz@eurotext.de'>privacy@eurotext.de</a>.,Alle Angaben erfolgen freiwillig. Die von Ihnen übermittelten Informationen werden ausschließlich für die Anmeldung am Übersetzungsportal und die Projektabwicklung mit der Eurotext AG genutzt. Die Weitergabe der Daten an Dritte ist ausgeschlossen. Sie können jederzeit eine Löschung Ihrer Daten unter <a href='mailto:datenschutz@eurotext.de'>privacy@eurotext.de</a>veranlassen.
15
+ Begin import,Import starten
16
+ Bundle Product,Bündelartikel
17
+ CMS pages per file,CMS-Seiten pro Datei
18
+ CMS-Block,CMS-Block
19
+ CMS-Page,CMS-Seite
20
+ CMS-Type,CMS-Typ
21
+ Categories per file,Kategorien pro Datei
22
+ Category filter,Kategorienfilter
23
+ Checking CMS-Block,Prüfe CMS-Block
24
+ Checking CMS-Blocks,Prüfe CMS-Blöcke
25
+ Checking CMS-Page,Prüfe CMS-Seite
26
+ Checking CMS-Pages,Prüfe CMS-Seiten
27
+ Checking Product,Prüfe Artikel
28
+ Checking attributes,Prüfe Attribute
29
+ Checking category,Prüfe Kategorie
30
+ Checking eMail-Template,Prüfe E-Mail-Vorlage
31
+ Checking eMail-Templates,Prüfe E-Mail-Vorlagen
32
+ City,Ort
33
+ Click here to change the scope of url keys to enable translation of url keys for products and categories. (Attention: This can not be undone!),"Klicken Sie hier, um den Umfang der URL-Keys zu ändern, damit die URL-Keys für Artikel und Kategorien übersetzt werden können. (Achtung: Dies kann nicht mehr rückgängig gemacht werden!)"
34
+ Clicking <b>Select all language files without existing translation</b> will automatically select all files and check for missing translations. All entries without an existing translation will be exported.,Mit Klick auf <b>Alle Einträge in Sprachdateien ohne vorhandene Übersetzung auswählen</b> werden alle Sprachdateien eingelesen und nach fehlenden Übersetzungen überprüft. Alle Einträge ohne Übersetzung werden exportiert.
35
+ Clicking <b>Select language files</b> will allow you to manually select specific shop system texts in the selection window for translation.,Mit Klick auf <b>Sprachdateien auswählen</b> können Sie die für die Übersetzung bestimmten Shop-Systemtexte im Auswahlfenster manuell zuordnen.
36
+ "Clicking the button <b>Request your free quote now</b> will transmit your texts for translation to the translation portal, and you will receive a detailed quote for your translation project within the next 24 hours (weekdays).",Durch Klick auf den Button <b>Jetzt kostenloses Angebot anfordern</b> werden die zu übersetzenden Texte an das Übersetzungsportal übermittelt und Sie erhalten innerhalb von 24 Stunden (werktags) ein detailliertes Angebot für die Übersetzung Ihrer Texte.
37
+ "Clicking the button below will transmit the selected texts to the translation portal, and you will receive a detailed quote for the translation of your texts within 24h (weekdays).",Mit einem Klick auf den nachfolgenden Button werden die ausgewählten Texte an das Übersetzungsportal übermittelt und Sie erhalten innerhalb von 24 Stunden (werktags) ein detailliertes Angebot für die Übersetzung Ihrer Texte.
38
+ Company,Firma
39
+ Configurable Product,Konfigurierbarer Artikel
40
+ Contact,Kontakt
41
+ Could not connect to server. Could be a temporary error or firewall problem. You could also check for a new module version.,"Es konnte keine Server-Verbindung hergestellt werden. Dies kann auf einen vorübergehenden Fehler oder ein Problem mit der Firewall zurückzuführen sein. Sie sollten außerdem prüfen, ob eine neue Modul-Version verfügbar ist."
42
+ Country,Land
43
+ Create a new project for each translation order.,Legen Sie für jeden Übersetzungsauftrag ein Projekt an.
44
+ Create a unique project name here. This will allow you better recognize your various projects later.,"Setzen Sie hier einen eindeutigen Projektnamen ein. Das erlaubt Ihnen, die Projekte besser zuzuordnen bzw. sie wiederzuerkennen."
45
+ Current value,Aktueller Wert
46
+ Current value for 'max_execution_time',Aktueller Wert von 'max_execution_time'
47
+ Currently your Magento installation is not capable to save translated url keys for products and categories.,"Ihre aktuelle Magento-Installation ist nicht in der Lage, übersetzte URL-Keys für Artikel und Kategorien zu speichern."
48
+ Customer ID,Kunden-Nummer
49
+ Delete,Löschen
50
+ Designation,Bezeichnung
51
+ Disabled,Deaktiviert
52
+ Do you really want to delete this project?,Wollen Sie dieses Projekt wirklich löschen?
53
+ Do you really want to reset this project?,Wollen Sie dieses Projekt wirklich zurücksetzen?
54
+ Done,Fertig
55
+ Downloadable Product,Downloadartikel
56
+ Each project must have a name assigned (we recommend something unique and identifiable).,Vergeben Sie dem Projekt einen Namen (es empfiehlt sich hier einen aussagekräftigen Namen zu wählen)
57
+ Edit,Bearbeiten
58
+ Edit information,Daten ändern
59
+ Enabled,Aktiviert
60
+ Enter designation or SKU,Bezeichnung oder Artikelnummer eingeben
61
+ Error Message,Fehlermeldung
62
+ Export,Export
63
+ Export SEO content?,SEO-Inhalte exportieren?
64
+ Export URL keys?,URL-Keys exportieren?
65
+ Export attributes und attribute options?,Attribute und Attribut-Optionen exportieren?
66
+ Filename,Dateiname
67
+ Filter,Filter
68
+ Finished loading language files.,Das Laden der Sprachdateien ist abgeschlossen.
69
+ First Name,Vorname
70
+ Generating Language files,Erzeuge Sprachdateien
71
+ Generating ZIP-Archive,Erzeuge ZIP-Archiv
72
+ Generating language files...,Erzeuge Sprachdateien ...
73
+ Grouped Product,Gruppen Artikel
74
+ Help,Hilfe
75
+ Here you will find a list of the settings required for operating the Eurotext interface,Hier finden Sie eine Auflistung der für den Betrieb der Eurotext-Schnittstelle notwendigen Einstellungen
76
+ House No,Hausnummer
77
+ ID,ID
78
+ Identifier,Identifizierer
79
+ Import,Importieren
80
+ Import the *.zip file containing the finished translations here,Die *.zip-Datei mit den fertigen Übersetzungen können Sie hier wieder importieren
81
+ Import translations,Übersetzungen importieren
82
+ Import translations now,Jetzt Übersetzungen importieren
83
+ Important,Wichtig
84
+ In Stock,Auf Lager
85
+ In progress,In Bearbeitung
86
+ Installed,Installiert
87
+ Language of the translation portal,Sprache im Übersetzungsportal
88
+ Language settings,Spracheinstellungen
89
+ Last Name,Nachname
90
+ Last save,Zuletzt gespeichert
91
+ Last update,Letzte Aktualisierung
92
+ Loaded,Geladen
93
+ Loading - Please wait,Lade - Einen Moment
94
+ Loading - please wait …,Lade - Einen Moment...
95
+ Loading - please wait...,Lade - Einen Moment...
96
+ Login data could not be validated. FTP-Extension is missing!,Die Login-Daten konnten nicht bestätigt werden. FTP-Extension fehlt!
97
+ Make sure to complete the registration process and enter your access information before submitting your first translation project. Fill out all fields of the registration form and click <b><i>Save and send</i>.</b> Eurotext will send you your login details within 24 hours (weekdays). Enter that information in the menu item <b><i>Settings</i></b>.,Vor dem ersten Übersetzungsprojekt müssen Sie den Registrierungsprozess abgeschlossen und die Zugangsdaten eingetragen haben. Füllen Sie bitte alle Felder des Registrierungsformulars aus und klicken Sie auf <b><i>Speichern und Senden</i></b>. Eurotext wird Ihnen innerhalb von 24 Stunden (werktags) Ihre Zugangsdaten zusenden. Tragen Sie diese Daten im Menüpunkt <b><i>Einstellungen</i></b> ein.
98
+ Minimum,Minimum
99
+ Missing,Fehlend
100
+ Module-Version,Modul-Version
101
+ Mr.,Herr
102
+ Ms.,Frau
103
+ New,Neu
104
+ New Project,Neues Projekt
105
+ New project,Neues Projekt
106
+ No file given!,Keine Datei angegeben!
107
+ "No file was selected, or the file selected is not a ZIP file",Es wurde keine Datei ausgewählt oder die Datei ist keine ZIP-Datei
108
+ No projects created,Noch keine Projekte angelegt
109
+ No result,Nichts gefunden
110
+ No selection,Nichts ausgewählt
111
+ Not yet saved,Noch nicht gespeichert
112
+ Not yet selected,Noch nicht ausgewählt
113
+ Note,Hinweis
114
+ Open help,Hilfe starten
115
+ Out of Stock,Nicht auf Lager
116
+ Page,Seite
117
+ Password,Passwort
118
+ Per Page,Pro Seite
119
+ Phone,Telefon
120
+ Please enter all requested information!,Bitte füllen Sie alle Felder aus!
121
+ Please enter project name!,Bitte vergeben Sie einen Projektnamen!
122
+ Please enter your contact information here,Bitte tragen Sie hier Ihre Kontaktdaten ein
123
+ Please read the translationManager documentation for more information.,"Für weitere Informationen, lesen Sie bitte die translationMANAGER Dokumentation."
124
+ Please select Destination Storeview!,Bitte Ziel-Storeview wählen!
125
+ Please select Source Storeview!,Bitte Ausgangs-Storeview wählen!
126
+ Please select different Storeviews for Source and Destination!,Wählen Sie bitte verschiedene Storeviews für Ausgang und Ziel aus!
127
+ Please wait,Einen Moment
128
+ Please wait - saving project settings,Einen Moment - Speichere Projekteinstellungen
129
+ Please wait - this may take a while,Einen Moment - Kann etwas dauern
130
+ Processing - please wait …,Arbeite - Einen Moment...
131
+ Processing File,Verarbeite Datei
132
+ Product Type,Artikel-Art
133
+ Products per file,Artikel pro Datei
134
+ "Products, categories, CMS pages, or email templates can be selected individually (<b>Select […]</b>), or alternatively:","Die Artikel, Kategorien, CMS-Seiten und/oder E-Mail-Vorlagen können individuell ausgewählt werden (Klick auf <b>[…] auswählen</b>) oder"
135
+ Project name,Projektname
136
+ Project settings,Projekt-Einstellungen
137
+ Projects,Projekte
138
+ Quote request,Angebot anfordern
139
+ Registration,Registrierung
140
+ Request your free quote now,Jetzt kostenloses Angebot anfordern
141
+ Required value,Benötigter Wert
142
+ Reset,Zurücksetzen
143
+ SKU,Artikelnr
144
+ Salutation,Anrede
145
+ Save,Speichern
146
+ Save and send,Senden und Speichern
147
+ Save project,Projekt speichern
148
+ Saved Project!,Projekt gespeichert!
149
+ Select CMS pages,CMS-Seiten auswählen
150
+ Select CMS pages/blocks,CMS-Seiten/-Blöcke auswählen
151
+ Select all CMS pages displayed,Alle angezeigten CMS-Seiten auswählen
152
+ Select all CMS pages without existing translations,Alle CMS-Seiten ohne vorhandene Übersetzung auswählen
153
+ Select all CMS pages/blocks,Alle CMS-Seiten/-Blöcke auswählen
154
+ Select all categories,Alle Kategorien auswählen
155
+ Select all categories displayed,Alle angezeigten Kategorien auswählen
156
+ Select all categories without existing translations,Alle Kategorien ohne vorhandene Übersetzung auswählen
157
+ Select all email templates,Alle E-Mail-Vorlagen auswählen
158
+ Select all email templates displayed,Alle angezeigten E-Mail-Vorlagen auswählen
159
+ Select all email templates without existing translations,Alle E-Mail-Templates ohne vorhandene Übersetzung auswählen
160
+ Select all entries in language files without existing translations,Alle Einträge in Sprachdateien ohne vorhandene Übersetzung auswählen
161
+ Select all language files,Alle Sprachdateien auswählen
162
+ Select all language files displayed,Alle angezeigten Sprachdateien auswählen
163
+ Select all products displayed,Alle angezeigten Artikel auswählen
164
+ Select all products in this category,Alle Artikel dieser Kategorie auswählen
165
+ Select all products without existing translations,Alle Artikel ohne vorhandene Übersetzung auswählen
166
+ Select categories,Kategorien auswählen
167
+ Select email templates,E-Mail-Vorlagen auswählen
168
+ Select language files,Sprachdateien wählen
169
+ "Select product texts, category texts, or CMS pages for translation.","Wählen Sie Artikeltexte, Kategorietexte oder CMS-Seiten für die Übersetzung aus."
170
+ Select products,Artikel auswählen
171
+ "Select the check box <b>Export SEO content?</b> if you would like to have the SEO content (e.g. keywords, search terms, meta tags, etc.) translated.","Aktivieren Sie die Checkbox <b>SEO-Inhalte exportieren?</b>, falls Sie auch die SEO-Inhalte (z.B. Keywords, Suchbegriffe, Meta-Tags, etc.) übersetzen lassen wollen."
172
+ Select the check box <b>Export URL keys?</b> if you would like to have URLs of products and categories translated.,Aktivieren Sie die Checkbox <b>URL-Keys exportieren?</b> falls Sie die URLs von Artikeln und Kategorien übersetzen lassen möchten.
173
+ Select the check box <b>Export attributes and attribute options?</b> if you would like to have the product attributes and associated values translated.,Aktivieren Sie die Checkbox <b>Attribute und Attribut-Optionen exportieren?</b> falls Sie Artikelattribute und die dazugehörigen Werte übersetzen lassen möchten.
174
+ "Select the products, categories, CMS pages, or email templates you need translated","Wählen Sie die Artikel, Kategorien, CMS-Seiten und/oder E-Mail-Vorlagen aus, die Sie übersetzen lassen wollen"
175
+ Select this check box if you wish to translate product and category URLs,"Aktivieren Sie diese Checkbox, falls Sie die URLs von Artikeln und Kategorien übersetzen lassen möchten."
176
+ Select this check box if you wish to translate product attributes and associated values.,"Aktivieren Sie diese Checkbox, falls Sie Artikelattribute und die dazugehörigen Werte übersetzen lassen möchten."
177
+ "Select this check box if you wish to translate your SEO content (keywords, meta tags, etc.).","Aktivieren Sie diese Checkbox, falls Sie die SEO-Inhalte (z.B. Keywords, Suchbegriffe, Meta-Tags etc.) übersetzen lassen wollen."
178
+ Selected CMS pages/blocks,Ausgewählte CMS-Seiten/-Blöcke
179
+ Selected categories,Ausgewählte Kategorien
180
+ Selected eMail-Templates,Ausgewählte E-Mail-Vorlagen
181
+ Selected language files,Ausgewählte Sprachdateien
182
+ Selected products,Ausgewählte Artikel
183
+ Selecting this check box will assign all CMS pages without existing target language translations.<br/><b>Important: All previous assignments will be cancelled!</b>,"Wenn Sie diese Checkbox aktivieren, werden alle CMS-Seiten zugeordnet bei denen noch keine Texte in der Zielsprache angelegt worden sind.<br/><b>Achtung: Alle vorherigen Zuordnungen werden gelöscht!</b>"
184
+ Selecting this check box will assign all categories for which no texts have been created in the target language.<br/><b>Important: All previous assignments will be cancelled!</b>,"Wenn Sie diese Checkbox aktivieren, werden alle Kategorien zugeordnet bei denen noch keine Texte in der Zielsprache angelegt worden sind.<br/><b>Achtung: Alle vorherigen Zuordnungen werden gelöscht!</b>"
185
+ Selecting this check box will assign all items for which no text has been created in the target language.<br><b>Important: All previous assignments will be cancelled!</b>,"Wenn Sie diese Checkbox aktivieren, werden alle Artikel zugeordnet bei denen noch keine Texte in der Zielsprache angelegt worden sind.<br><b>Achtung: Alle vorherigen Zuordnungen werden gelöscht!</b>"
186
+ Selecting this check box will export all email templates without a counterpart in the target language.<br><b>Important: All previous assignments will be cancelled!</b>,"Wenn Sie diese Checkbox aktivieren, werden alle E-Mail-Vorlagen exportiert bei denen noch keine Vorlage in der Zielsprache existiert.<br><b>Achtung: Alle vorherigen Zuordnungen werden gelöscht!</b>"
187
+ Selecting this check box will trigger a check of all language files for missing translations. All entries without an existing translation will be exported.<br><b>Important: All previous assignments will be cancelled!</b>,"Wenn Sie diese Checkbox aktivieren, werden alle Sprachdateien eingelesen und nach fehlenden Übersetzungen überprüft. Alle Einträge ohne Übersetzung werden exportiert.<br><b>Achtung: Alle vorherigen Zuordnungen werden gelöscht!</b>"
188
+ Sending data,Sende Daten
189
+ Settings,Einstellungen
190
+ Shop Name,Shopname
191
+ Shop URL,Shop-URL
192
+ Show all,Alle anzeigen
193
+ Simple Product,Einfacher Artikel
194
+ Source storeview,Ausgangs-Storeview
195
+ Specify a <b>Source Storeview</b> and <b>Target Storeview</b> for the requested translation.,Legen Sie den <b>Ausgangs-</b> und <b>Ziel-Storeview</b> für die gewünschte Übersetzung fest.
196
+ "Specify the source language (source storeview) and the target language (target storeview).<br/>The language of the current storeview is shown in brackets. You can amend the language setting in Magento if the language shown is incorrect:<br/>System -> Configuration, then select “Storeview” at the top left. Set the correct language via General -> Locale Options (here you can also double-check all other language/country settings for your shop)","Legen Sie hier fest, von welcher Ausgangssprache (Ausgangs-Storeview) eine Übersetzung zu welcher Zielsprache (Ziel-Storeview) durchgeführt werden soll.<br/>In Klammern wird die für den jeweiligen Storeview aktuell gesetzte Spracheinstellung angezeigt. Wird dort nicht die richtige Sprache angezeigt, korrigieren Sie die Spracheinstellung in Magento:<br/>System -> Konfiguration, dann oben links ""Storeview"" wählen. Stellen Sie unter „Allgemein -> Optionen für Lokalisierungen“ die korrekte Sprache ein (hier können Sie auch die anderen Sprach-/Ländereinstellungen überprüfen)"
197
+ Status,Status
198
+ Stock Availability,Lagerbestand
199
+ Street,Straße
200
+ System requirements,Systemvoraussetzungen
201
+ Target storeview,Ziel-Storeview
202
+ Telefax,Fax
203
+ The *.zip-File was extracted. Click “Begin import” to upload the translations.,Die *.zip-Datei wurde entpackt. Die Übersetzungen können Sie nun mit Klick auf den Button 'Import starten' einspielen.
204
+ There seems to be a problem with your login data. Please check username and password!,Es ist ein Login-Fehler aufgetreten. Bitte prüfen Sie Ihre Eingaben!
205
+ This information is your personal access data for the Eurotext AG translation portal.<br/>Registration is free of charge. You will receive your personal access information within 24 hours (weekdays).,Mit diesen Daten erhalten Sie Ihren persönlichen Zugang zum Übersetzungsportal der Eurotext AG.<br/>Durch die Registrierung entstehen keine Kosten. Die Übersendung Ihrer Zugangsdaten erfolgt innerhalb der nächsten 24 Stunden (werktags).
206
+ This process may take a few minutes depending on project size. Please wait until a message is displayed that the import has been completed.,"Dieser Vorgang kann je nach Textmenge mehrere Minuten dauern. Brechen Sie den Vorgang nicht ab und warten Sie, bis der Abschluss des Imports bestätigt wird."
207
+ Translate,Übersetzen
208
+ Translation portal successfully connected!,Verbindung zum Übersetzungsportal erfolgreich!
209
+ Try again,Nochmal versuchen
210
+ Unable to save project settings,Projekteinstellungen konnten nicht gespeichert werden
211
+ Unknown Error,Unerwarteter Fehler
212
+ Unsupported language,Nicht unterstützte Sprache
213
+ User name,Benutzername
214
+ Virtual Product,Virtueller Artikel
215
+ We recommend creating a backup before importing.,Wir empfehlen zuvor eine Datensicherung durchzuführen.
216
+ "You will receive your login details from your customer service representative at Eurotext AG as soon as you have completed your registration. Please enter your user name, password and customer number in ""Settings"".<br/><span style=""font-style:italic;""><span style=""color:red;"">Important:</span> Ensure that a storeview for the target language was created in the shop before exporting the text for translation, and that the storeview settings have the correct language settings</span>","Sie erhalten Ihre Zugangsdaten von Ihrem Kundenbetreuer der Eurotext AG sobald Sie die Registrierung erfolgreich abgeschlossen haben. Bitte tragen Sie im Menüpunkt 'Einstellungen' Ihren Benutzernamen, Ihr Passwort und Ihre Kundennummer ein.<br/><span style=""font-style:italic;""><span style=""color:red;"">Achtung:</span> Denken Sie daran, dass Sie vor dem Export der zu übersetzenden Texte einen Storeview für die Zielsprache im Shop angelegt und in den Storeview-Einstellungen die korrekten Spracheinstellungen hinterlegt haben müssen</span>"
217
+ Your Eurotext customer service representative will send you your translations in a zip file. This zip file can be uploaded and imported to he relevant project.,Sie erhalten die Übersetzungen von Ihrem Eurotext-Kundenbetreuer in einer ZIP-Datei. Die ZIP-Datei kann nun im jeweiligen Projekt hochgeladen und importiert werden.
218
+ Your Magento installation allows the translation of URLs of categories and products.,Ihre Magento-Installation erlaubt auch die Übersetzung der URLs von Kategorien und Artikeln.
219
+ Your Magento languages,Ihre Magento-Sprachen
220
+ Zip,PLZ
221
+ eMail Address,E-Mail-Adresse
222
+ or,oder
223
+ translationManager Quick Start Guide,translationMANAGER Schnellstart-Anleitung
app/locale/en_US/Eurotext_TranslationManager.csv ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Do you really want to delete this project?","Do you really want to delete this project?"
2
+ "Do you really want to reset this project?","Do you really want to reset this project?"
3
+ "Reset","Reset"
4
+ "There seems to be a problem with your login data. Please check username and password!","There seems to be a problem with your login data. Please check username and password!"
5
+ "Here you will find a list of the settings required for operating the Eurotext interface","Here you will find a list of the settings required for operating the Eurotext interface"
6
+ "Please deactivate your popup blocker for the Magento backend.","Please deactivate your popup blocker for the Magento backend."
7
+ "E-Mail-Address","E-Mail-Address"
8
+ "No","No"
9
+ "Select the check box <b>Export URL keys?</b> if you would like to have URLs of products and categories translated.","Select the check box <b>Export URL keys?</b> if you would like to have URLs of products and categories translated."
10
+ "Select the check box <b>Export attributes and attribute options?</b> if you would like to have the product attributes and associated values translated.","Select the check box <b>Export attributes and attribute options?</b> if you would like to have the product attributes and associated values translated."
11
+ "Select the check box <b>Export SEO content?</b> if you would like to have the SEO content (e.g. keywords, search terms, meta tags, etc.) translated.","Select the check box <b>Export SEO content?</b> if you would like to have the SEO content (e.g. keywords, search terms, meta tags, etc.) translated."
12
+ "Please read the translationMANAGER documentation for more information.","Please read the translationMANAGER documentation for more information."
13
+ "<b>Please note:</b> For safety reasons, we recommend importing to a staging system first. Make sure to backup all your data before importing.","<b>Please note:</b> For safety reasons, we recommend importing to a staging system first. Make sure to backup all your data before importing."
14
+ "translationMANAGER Quick Start Guide","translationMANAGER Quick Start Guide"
15
+ "Your Eurotext customer service representative will send you your translations in a zip file. This zip file can be uploaded and imported to the relevant project.","Your Eurotext customer service representative will send you your translations in a zip file. This zip file can be uploaded and imported to the relevant project."
16
+ "Clicking the button <b>Request your free quote now</b> will transmit your texts for translation to the translation portal, and you will receive a detailed quote for your translation project within the next 24 hours (weekdays).","Clicking the button <b>Request your free quote now</b> will transmit your texts for translation to the translation portal, and you will receive a detailed quote for your translation project within the next 24 hours (weekdays)."
17
+ "Here you will find a list of required settings for running translationMANAGER.","Here you will find a list of required settings for running translationMANAGER."
18
+ "Make sure to complete the registration process and enter your access information before submitting your first translation project. Fill out all fields of the registration form and click <b><i>Save and send</i>.</b> Eurotext will send you your login details within 24 hours (weekdays). Enter that information in the menu item <b><i>Settings</i></b>.","Make sure to complete the registration process and enter your access information before submitting your first translation project. Fill out all fields of the registration form and click <b><i>Save and send</i>.</b> Eurotext will send you your login details within 24 hours (weekdays). Enter that information in the menu item <b><i>Settings</i></b>."
19
+ "You will receive your login details from your customer service representative at Eurotext AG as soon as you have completed your registration. Please enter your user name, password and customer number in ""Settings"".<br/><span style='font-style:italic;'><span style='color:red;'>Important:</span> Ensure that a storeview for the target language was created in the shop before exporting the text for translation, and that the storeview settings have the correct language settings</span>","You will receive your login details from your customer service representative at Eurotext AG as soon as you have completed your registration. Please enter your user name, password and customer number in ""Settings"".<br/><span style='font-style:italic;'><span style='color:red;'>Important:</span> Ensure that a storeview for the target language was created in the shop before exporting the text for translation, and that the storeview settings have the correct language settings</span>"
20
+ "Create a new project for each translation order.","Create a new project for each translation order."
21
+ "Each project must have a name assigned (we recommend something unique and identifiable).","Each project must have a name assigned (we recommend something unique and identifiable)."
22
+ "Specify a <b>Source Storeview</b> and <b>Target Storeview</b> for the requested translation.","Specify a <b>Source Storeview</b> and <b>Target Storeview</b> for the requested translation."
23
+ "Select the products, categories, CMS pages, or email templates you need translated","Select the products, categories, CMS pages, or email templates you need translated"
24
+ "Products, categories, CMS pages, or email templates can be selected individually (<b>Select […]</b>), or alternatively:","Products, categories, CMS pages, or email templates can be selected individually (<b>Select […]</b>), or alternatively:"
25
+ "<b>Select all [...] without existing translation:</b> The module will automatically select all items, categories, CMS pages and email templates for which no translated text exists in the target language.","<b>Select all [...] without existing translation:</b> The module will automatically select all items, categories, CMS pages and email templates for which no translated text exists in the target language."
26
+ "<i><span style='color:red;'>Important:</span> All previous assignments will be deleted!</i>","<i><span style='color:red;'>Important:</span> All previous assignments will be deleted!</i>"
27
+ "Clicking <b>Select language files</b> will allow you to manually select specific shop system texts in the selection window for translation.","Clicking <b>Select language files</b> will allow you to manually select specific shop system texts in the selection window for translation."
28
+ "Clicking <b>Select all language files without existing translation</b> will automatically select all files and check for missing translations. All entries without an existing translation will be exported.","Clicking <b>Select all language files without existing translation</b> will automatically select all files and check for missing translations. All entries without an existing translation will be exported."
29
+ "<i><span style='color:red;'>Important:</span> We recommend using this setting only for the initial translation of a new online shop, as it will trigger a translation of ALL language files for the selected target language!</i>","<i><span style='color:red;'>Important:</span> We recommend using this setting only for the initial translation of a new online shop, as it will trigger a translation of ALL language files for the selected target language!</i>"
30
+ "This information is your personal access data for the Eurotext AG translation portal.<br/>Registration is free of charge. You will receive your personal access information within 24 hours (weekdays).","This information is your personal access data for the Eurotext AG translation portal.<br/>Registration is free of charge. You will receive your personal access information within 24 hours (weekdays)."
31
+ "Select the check box <b>Export URL keys?</b> if you would like to have URLs of products and categories translated.","Select the check box <b>Export URL keys?</b> if you would like to have URLs of products and categories translated."
32
+ "Select the check box <b>Export attributes and attribute options?</b> if you would like to have the product attributes and associated values translated.","Select the check box <b>Export attributes and attribute options?</b> if you would like to have the product attributes and associated values translated."
33
+ "Select the check box <b>Export SEO content?</b> if you would like to have the SEO content (e.g. keywords, search terms, meta tags, etc.) translated.","Select the check box <b>Export SEO content?</b> if you would like to have the SEO content (e.g. keywords, search terms, meta tags, etc.) translated."
34
+ "Please read the translationMANAGER documentation for more information.","Please read the translationMANAGER documentation for more information."
35
+ "<b>Please note:</b> For safety reasons, we recommend importing to a staging system first. Make sure to backup all your data before importing.","<b>Please note:</b> For safety reasons, we recommend importing to a staging system first. Make sure to backup all your data before importing."
36
+ "translationMANAGER Quick Start Guide","translationMANAGER Quick Start Guide"
37
+ "Your Eurotext customer service representative will send you your translations in a zip file. This zip file can be uploaded and imported to the relevant project.","Your Eurotext customer service representative will send you your translations in a zip file. This zip file can be uploaded and imported to the relevant project."
38
+ "Clicking the button <b>Request your free quote now</b> will transmit your texts for translation to the translation portal, and you will receive a detailed quote for your translation project within the next 24 hours (weekdays).","Clicking the button <b>Request your free quote now</b> will transmit your texts for translation to the translation portal, and you will receive a detailed quote for your translation project within the next 24 hours (weekdays)."
39
+ "Here you will find a list of required settings for running translationMANAGER.","Here you will find a list of required settings for running translationMANAGER."
40
+ "Make sure to complete the registration process and enter your access information before submitting your first translation project. Fill out all fields of the registration form and click <b><i>Save and send</i>.</b> Eurotext will send you your login details within 24 hours (weekdays). Enter that information in the menu item <b><i>Settings</i></b>.","Make sure to complete the registration process and enter your access information before submitting your first translation project. Fill out all fields of the registration form and click <b><i>Save and send</i>.</b> Eurotext will send you your login details within 24 hours (weekdays). Enter that information in the menu item <b><i>Settings</i></b>."
41
+ "You will receive your login details from your customer service representative at Eurotext AG as soon as you have completed your registration. Please enter your user name, password and customer number in ""Settings"".<br/><span style='font-style:italic;'><span style='color:red;'>Important:</span> Ensure that a storeview for the target language was created in the shop before exporting the text for translation, and that the storeview settings have the correct language settings</span>","You will receive your login details from your customer service representative at Eurotext AG as soon as you have completed your registration. Please enter your user name, password and customer number in ""Settings"".<br/><span style='font-style:italic;'><span style='color:red;'>Important:</span> Ensure that a storeview for the target language was created in the shop before exporting the text for translation, and that the storeview settings have the correct language settings</span>"
42
+ "Create a new project for each translation order.","Create a new project for each translation order."
43
+ "Each project must have a name assigned (we recommend something unique and identifiable).","Each project must have a name assigned (we recommend something unique and identifiable)."
44
+ "Specify a <b>Source Storeview</b> and <b>Target Storeview</b> for the requested translation.","Specify a <b>Source Storeview</b> and <b>Target Storeview</b> for the requested translation."
45
+ "Select the items, categories, CMS pages, or email templates you need translated","Select the items, categories, CMS pages, or email templates you need translated"
46
+ "Items, categories, CMS pages, or email templates can be selected individually (<b>Select […]</b>), or alternatively:","Items, categories, CMS pages, or email templates can be selected individually (<b>Select […]</b>), or alternatively:"
47
+ "<b>Select all [...] without existing translation:</b> The module will automatically select all Products, categories, CMS pages and email templates for which no translated text exists in the target language.","<b>Select all [...] without existing translation:</b> The module will automatically select all Products, categories, CMS pages and email templates for which no translated text exists in the target language."
48
+ "<i><span style='color:red;'>Important:</span> All previous assignments will be deleted!</i>","<i><span style='color:red;'>Important:</span> All previous assignments will be deleted!</i>"
49
+ "Clicking <b>Select language files</b> will allow you to manually select specific shop system texts in the selection window for translation.","Clicking <b>Select language files</b> will allow you to manually select specific shop system texts in the selection window for translation."
50
+ "Clicking <b>Select all language files without existing translation</b> will automatically select all files and check for missing translations. All entries without an existing translation will be exported.","Clicking <b>Select all language files without existing translation</b> will automatically select all files and check for missing translations. All entries without an existing translation will be exported."
51
+ "<i><span style='color:red;'>Important:</span> We recommend using this setting only for the initial translation of a new online shop, as it will trigger a translation of ALL language files for the selected target language!</i>","<i><span style='color:red;'>Important:</span> We recommend using this setting only for the initial translation of a new online shop, as it will trigger a translation of ALL language files for the selected target language!</i>"
52
+ "This information is your personal access data for the Eurotext AG translation portal.<br/>Registration is free of charge. You will receive your personal access information within 24 hours (weekdays).","This information is your personal access data for the Eurotext AG translation portal.<br/>Registration is free of charge. You will receive your personal access information within 24 hours (weekdays)."
53
+ "Phone","Phone"
54
+ "Telefax","Fax"
55
+ "The *.zip-File was extracted. Click ""Begin import"" to upload the translations.","The *.zip-File was extracted. Click ""Begin import"" to upload the translations."
56
+ "We recommend creating a backup before importing.","We recommend creating a backup before importing."
57
+ "Import the *.zip file containing the finished translations here","Import the *.zip file containing the finished translations here"
58
+ "This process may take a few minutes depending on project size. Please wait until a message is displayed that the import has been completed.","This process may take a few minutes depending on project size. Please wait until a message is displayed that the import has been completed."
59
+ "Clicking the button below will transmit the selected texts to the translation portal, and you will receive a detailed quote for the translation of your texts within 24h (weekdays).","Clicking the button below will transmit the selected texts to the translation portal, and you will receive a detailed quote for the translation of your texts within 24h (weekdays)."
60
+ "Select product texts, category texts, or CMS pages for translation.","Select product texts, category texts, or CMS pages for translation."
61
+ "Request your free quote now","Request your free quote now"
62
+ "Select this check box if you wish to translate your SEO content (keywords, meta tags, etc.).","Select this check box if you wish to translate your SEO content (keywords, meta tags, etc.)."
63
+ "Select this check box if you wish to translate product attributes and associated values.","Select this check box if you wish to translate product attributes and associated values."
64
+ "Select this check box if you wish to translate product and category URLs","Select this check box if you wish to translate product and category URLs"
65
+ "Quote request","Quote request"
66
+ "Note","Note"
67
+ "Action","Action"
68
+ "Select language files","Select language files"
69
+ "Select email templates","Select email templates"
70
+ "Select CMS pages","Select CMS pages"
71
+ "Selecting this check box will assign all CMS pages without existing target language translations.<br/><b>Important: All previous assignments will be cancelled!</b>","Selecting this check box will assign all CMS pages without existing target language translations.<br/><b>Important: All previous assignments will be cancelled!</b>"
72
+ "Select all categories without existing translations","Select all categories without existing translations"
73
+ "Select all CMS pages without existing translations","Select all CMS pages without existing translations"
74
+ "Select all email templates without existing translations","Select all email templates without existing translations"
75
+ "Select all entries in language files without existing translations","Select all entries in language files without existing translations"
76
+ "Export SEO content?","Export SEO content?"
77
+ "Export attributes und attribute options?","Export attributes und attribute options?"
78
+ "Export URL keys?","Export URL keys?"
79
+ "Select categories","Select categories"
80
+ "Selecting this check box will trigger a check of all language files for missing translations. All entries without an existing translation will be exported.<br><b>Important: All previous assignments will be cancelled!</b>","Selecting this check box will trigger a check of all language files for missing translations. All entries without an existing translation will be exported.<br><b>Important: All previous assignments will be cancelled!</b>"
81
+ "Selecting this check box will export all email templates without a counterpart in the target language.<br><b>Important: All previous assignments will be cancelled!</b>","Selecting this check box will export all email templates without a counterpart in the target language.<br><b>Important: All previous assignments will be cancelled!</b>"
82
+ "Selecting this check box will assign all categories for which no texts have been created in the target language.<br/><b>Important: All previous assignments will be cancelled!</b>","Selecting this check box will assign all categories for which no texts have been created in the target language.<br/><b>Important: All previous assignments will be cancelled!</b>"
83
+ "or","or"
84
+ "Select all products without existing translations","Select all products without existing translations"
85
+ "Selecting this check box will assign all items for which no text has been created in the target language.<br><b>Important: All previous assignments will be cancelled!</b>","Selecting this check box will assign all items for which no text has been created in the target language.<br><b>Important: All previous assignments will be cancelled!</b>"
86
+ "Select products","Select products"
87
+ "Target storeview","Target storeview"
88
+ "Source storeview","Source storeview"
89
+ "Specify the source language (source storeview) and the target language (target storeview).<br/>The language of the current storeview is shown in brackets. You can amend the language setting in Magento if the language shown is incorrect:<br/>System -> Configuration, then select ""Storeview"" at the top left. Set the correct language via General -> Locale Options (here you can also double-check all other language/country settings for your shop)","Specify the source language (source storeview) and the target language (target storeview).<br/>The language of the current storeview is shown in brackets. You can amend the language setting in Magento if the language shown is incorrect:<br/>System -> Configuration, then select ""Storeview"" at the top left. Set the correct language via General -> Locale Options (here you can also double-check all other language/country settings for your shop)"
90
+ "Create a unique project name here. This will allow you better recognize your various projects later.","Create a unique project name here. This will allow you better recognize your various projects later."
91
+ "Project name","Project name"
92
+ "Import translations","Import translations"
93
+ "Delete","Delete"
94
+ "Your Magento installation allows the translation of URLs of categories and products.","Your Magento installation allows the translation of URLs of categories and products."
95
+ "Customer ID","Customer ID"
96
+ "Settings","Settings"
97
+ "Edit information","Edit information"
98
+ "Save and send","Save and send"
99
+ "Shop URL","Shop URL"
100
+ "Mr.","Mr."
101
+ "Ms.","Ms."
102
+ "Any information you provide is voluntary. Your data will be used for the purpose of logging in to the translation portal and for project processing in your dealings with Eurotext AG only. Your information will not be forwarded to third parties. You can request a deletion of your information at any time by sending an email to <a href='mailto:datenschutz@eurotext.de'>privacy@eurotext.de</a>.","Any information you provide is voluntary. Your data will be used for the purpose of logging in to the translation portal and for project processing in your dealings with Eurotext AG only. Your information will not be forwarded to third parties. You can request a deletion of your information at any time by sending an email to <a href='mailto:datenschutz@eurotext.de'>privacy@eurotext.de</a>."
103
+ "Important","Important"
104
+ "Please enter all requested information!","Please enter all requested information!"
105
+ "City","City"
106
+ "Zip","Zip"
107
+ "Company","Company"
108
+ "Street","Street"
109
+ "House No","House No"
110
+ "Country","Country"
111
+ "Last Name","Last Name"
112
+ "First Name","First Name"
113
+ "Salutation","Salutation"
114
+ "Shop Name","Shop Name"
115
+ "Please enter your contact information here","Please enter your contact information here"
116
+ "A storeview does not exist (anymore)","A storeview does not exist (anymore)"
117
+ "Import translations now","Import translations now"
118
+ "Select all products displayed","Select all products displayed"
119
+ "Select all products","Select all products"
120
+ "Select all categories displayed","Select all categories displayed"
121
+ "Select all categories","Select all categories"
122
+ "Select all CMS pages displayed","Select all CMS pages displayed"
123
+ "Select all CMS pages","Select all CMS pages"
124
+ "Select all email templates displayed","Select all email templates displayed"
125
+ "Select all email templates","Select all email templates"
126
+ "Select all language files displayed","Select all language files displayed"
127
+ "Select all language files","Select all language files"
128
+ "No selection","No selection"
129
+ "No file was selected, or the file selected is not a ZIP file","No file was selected, or the file selected is not a ZIP file"
130
+ "Analyzing","Analyzing"
131
+ "Project settings","Project settings"
132
+ "Project","Project"
133
+ "Please enter a project name!","Please enter a project name!"
134
+ "Help","Help"
135
+ "Eurotext","Eurotext"
136
+ "Projects","Projects"
137
+ "User name","User name"
138
+ "Password","Password"
139
+ "Language settings","Language settings"
140
+ "Your Magento languages","Your Magento languages"
141
+ "Language of the translation portal","Language of the translation portal"
142
+ "Save","Save"
143
+ "Open help","Open help"
144
+ "Saved","Saved"
145
+ "Please wait","Please wait"
146
+ "System requirements","System requirements"
147
+ "Designation","Designation"
148
+ "Current value","Current value"
149
+ "Required value","Required value"
150
+ "Unsupported language","Unsupported language"
151
+ "Registration","Registration"
152
+ "Importing","Importing"
153
+ "Edit","Edit"
154
+ "Please wait - saving project settings","Please wait - saving project settings"
155
+ "Please wait - this may take a while","Please wait - this may take a while"
156
+ "Last update","Last update"
157
+ "Project name","Project name"
158
+ "Source storeview","Source storeview"
159
+ "Status","Status"
160
+ "Action","Action"
161
+ "Checking category","Checking category"
162
+ "Checking email templates","Checking email templates"
163
+ "Checking attributes","Checking attributes"
164
+ "Analyzing","Analyzing"
165
+ "Searching language files","Searching language files"
166
+ "Generating language files","Generating language files"
167
+ "Generating ZIP archive","Generating ZIP archive"
168
+ "Sending data","Sending data"
169
+ "Done","Done"
170
+ "Processing","Processing"
171
+ "Processing file","Processing file"
172
+ "Error message","Error message"
173
+ "No file specified!","No file specified!"
174
+ "New project","New project"
175
+ "Project saved!","Project saved!"
176
+ "Please select source storeview!","Please select source storeview!"
177
+ "Please select target storeview!","Please select target storeview!"
178
+ "Source and target storeviews must not be the same!","Source and target storeviews must not be the same!"
179
+ "Language file import completed.","Language file import completed."
180
+ "Checking CMS page","Checking CMS page"
181
+ "Checking Product","Checking product"
182
+ "Checking category","Checking category"
183
+ "Not yet selected","Not yet selected"
184
+ "In progress","In progress"
185
+ "Import","Import"
186
+ "Loaded","Loaded"
187
+ "Already submitted. Project settings can no longer be edited.","Already submitted. Project settings can no longer be edited."
188
+ "Begin import","Begin import"
189
+ "Processing - please wait...","Processing - please wait..."
190
+ "Loading - please wait...","Loading - please wait..."
191
+ "Unable to save project settings","Unable to save project settings"
192
+ "Save project","Save project"
193
+ "Translation portal successfully connected!","Translation portal successfully connected!"
194
+ "Not yet saved","Not yet saved"
195
+ "Last save","Last save"
196
+ "New project","New project"
197
+ "-- Select storeview --","-- Select storeview --"
198
+ "Select products","Select products"
199
+ "Find","Find"
200
+ "Find products","Find products"
201
+ "Enter designation or SKU","Enter designation or SKU"
202
+ "Page","Page"
203
+ "No result","No result"
204
+ "Selected products","Selected products"
205
+ "Translate","Translate"
206
+ "Select categories","Select categories"
207
+ "Selected categories","Selected categories"
208
+ "Select CMS pages","Select CMS pages"
209
+ "Selected CMS pages","Selected CMS pages"
210
+ "Installed","Installed"
211
+ "Missing","Missing"
212
+ "Exit","Exit"
213
+ "Try again","Try again"
214
+ "No projects created","No projects created"
215
+ "New","New"
216
+ "Products per file","Products per file"
217
+ "Categories per file","Categories per file"
218
+ "CMS pages per file","CMS pages per file"
219
+ "Current value for 'max_execution_time'","Current value for 'max_execution_time'"
220
+ "Additional settings","Additional settings"
221
+ "Select language files","Select language files"
222
+ "Selected language files","Selected language files"
223
+ "Select language files","Select language files"
224
+ "Please wait","Please wait"
225
+ "Status","Status"
226
+ "Enabling this option writes an eurotext.log file into {{base_dir}}/var/log","Enabling this option writes an eurotext.log file into {{base_dir}}/var/log"
227
+ "Debug Mode","Debug Mode"
package.xml ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>eurotext_translationMANAGER</name>
4
+ <version>1.1.2.2</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://eurotext-ecommerce.com/wp-content/uploads/2016/02/EurotextAG_Nutzungsbedingungen_Softwareanwendungen.pdf">Nutzungsbedingungen f&#xFC;r Softwareanwendungen der Eurotext AG</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Start your translation projects with native translation professionals.</summary>
10
+ <description>Website &amp; Online Shop Translations Made Easy. The translationMANAGER allows you to export all text content from your online shop with just a few mouse clicks and then send it to the Eurotext translation portal. All types of content, including product descriptions, SEO content, framework or any other texts, are automatically included.&#xD;
11
+ &#xD;
12
+ Our translations are created where our customers have their marketplace. We absolutely emphasize that our over 4,000 professional translators create translations only into the language they grew up with. That is how we will always be able to guarantee that the translations are not only correct and authentic, but that they also take into account regional and cultural peculiarities.&#xD;
13
+ &#xD;
14
+ A good translation must be more than just linguistically correct; it must comply with technical guidelines to ensure an optimized workflow across all instances of the quality assurance process, the elimination of errors, and keeping costs low. That is why we offer periodic workshops for our professional translators to support them in their daily work.</description>
15
+ <notes>Version 1.1.2.2&#xD;
16
+ ====================================================================================&#xD;
17
+ - Eigene Kategorieattribute &#xFC;bersetzen: Exportieren Sie Ihre benutzerdefinierten Attribute (siehe Dokumentation)&#xD;
18
+ &#xD;
19
+ Version 1.1.2.1&#xD;
20
+ ====================================================================================&#xD;
21
+ - Duplicate URL-Key Fehler behoben&#xD;
22
+ &#xD;
23
+ Version 1.1.2.0&#xD;
24
+ ====================================================================================&#xD;
25
+ - &#xDC;berarbeiteter DEBUG Modus. Kann nun im Backend (System -&gt; Configuration -&gt; Developer -&gt; Log) aktiviert werden&#xD;
26
+ - Log-Datei wird in /var/log/eurotext.log erstellt, schwere Fehler werden auch&#xD;
27
+ bei ausgeschaltetem DEBUG Modus in eurotext_fatal.log gespeichert&#xD;
28
+ - Basisverzeichnis jetzt in /var/eurotext und nicht mehr /eurotext&#xD;
29
+ - FTP-Pr&#xFC;fung jetzt mit DEBUG Ausgabe&#xD;
30
+ - Diverse Verbesserungen in der Verwendung von Magento-Verzeichnissen&#xD;
31
+ &#xD;
32
+ Version 1.1.1.0&#xD;
33
+ ====================================================================================&#xD;
34
+ - Es werden nur noch die ausgew&#xE4;hlten Sprachdateien exportiert&#xD;
35
+ - Fehlerbehebungen&#xD;
36
+ &#xD;
37
+ Version 1.1.0.0&#xD;
38
+ ====================================================================================&#xD;
39
+ - Eigene Artikelattribute &#xFC;bersetzen: Exportieren Sie Ihre benutzerdefinierten Attribute (siehe Dokumentation)&#xD;
40
+ - Absicherung: Bei &#xDC;bertragungsfehlern zum Eurotext Server wird das Paket nach /var/export gesichert.&#xD;
41
+ - Fehlerkorrekturen und Sicherheitsupdates&#xD;
42
+ - Verbesserte und stabilere Programmbasis&#xD;
43
+ &#xD;
44
+ Version 1.0.0.9&#xD;
45
+ ====================================================================================&#xD;
46
+ - Der Lagerstatus wird bei der Produktauswahl nun auch f&#xFC;r 'virtuelle' Artikel/Hauptartikel wie im Backend eingestellt angezeigt&#xD;
47
+ &#xD;
48
+ Version 1.0.0.8&#xD;
49
+ ====================================================================================&#xD;
50
+ - Bei der Produktauswahl kann nun auch nach Artikeltyp gefiltert werden&#xD;
51
+ &#xD;
52
+ Version 1.0.0.7&#xD;
53
+ ====================================================================================&#xD;
54
+ - Javascript-Fehler beseitigt&#xD;
55
+ - Update-Skript arbeitet nun toleranter&#xD;
56
+ &#xD;
57
+ Version 1.0.0.6&#xD;
58
+ ====================================================================================&#xD;
59
+ - Bei der Auswahl der Produkte sind nun zwei zus&#xE4;tzliche Filter (nach Status und nach Lagerbestand) verf&#xFC;gbar&#xD;
60
+ - Es k&#xF6;nnen nun auch statische Bl&#xF6;cke exportiert und wieder importiert werden. Die statischen Bl&#xF6;cke werden mit bei der Auswahl der CMS-Seiten angezeigt&#xD;
61
+ &#xD;
62
+ Version 1.0.0.5&#xD;
63
+ ====================================================================================&#xD;
64
+ - Es werden im Kommentar-Feld der control.xml mehr Projektinformationen angegeben&#xD;
65
+ - Ein Projekt kann nun jeweils um einen Schritt zur&#xFC;ckgesetzt werden&#xD;
66
+ - Es wird nun die korrekte Modulversion in der control.xml angegeben&#xD;
67
+ &#xD;
68
+ Version 1.0.0.4&#xD;
69
+ ====================================================================================&#xD;
70
+ - Das Modul exportiert Attribute nun auch, wenn f&#xFC;r das Quell-Storeview keine Bezeichnung&#xD;
71
+ vorhanden ist. Es wird dann der Admin-Standardtext exportiert.&#xD;
72
+ &#xD;
73
+ Version 1.0.0.3&#xD;
74
+ ====================================================================================&#xD;
75
+ - URL-Key wird im Ziel-Storeview nun nur noch (anhand der Produkt-/Kategorie-Bezeichnung) &#xD;
76
+ gesetzt, wenn zuvor noch kein Wert gesetzt war (also "Use default values" / &#xD;
77
+ "Standardwerte &#xFC;bernehmen" aktiv war)&#xD;
78
+ Zuvor wurde der URL-Key gesetzt, wenn der Wert leer bzw. gleich dem Standardwert gesetzt war&#xD;
79
+ - Einstellung "Use default values" / "Standardwerte &#xFC;bernehmen" sollte bei Feldern, die in den &#xD;
80
+ &#xDC;bersetzungsdateien nicht vorhanden sind, nun nicht mehr verloren gehen.&#xD;
81
+ &#xD;
82
+ Version 1.0.0.2&#xD;
83
+ ====================================================================================&#xD;
84
+ - Wurden zwischen Export und Import Produktbilder im Magento-Backend gel&#xF6;scht f&#xFC;r die &#xDC;bersetzungen erzeugt wurden, konnte der Import fehlschlagen.&#xD;
85
+ Beim Import fehlende Produktbilder werden nun &#xFC;bersprungen.&#xD;
86
+ &#xD;
87
+ Version 1.0.0.1&#xD;
88
+ ====================================================================================&#xD;
89
+ - Erweiterung der Produktauswahl um die M&#xF6;glichkeit auch nach Kategorien zu filtern&#xD;
90
+ und so ggf. schnell ganze Kategorien &#xFC;bersetzen zu k&#xF6;nnen&#xD;
91
+ - Wurden URL-Felder nicht mit exportiert, wurden diese beim Import auf die Standardwerte gesetzt statt diese anhand der (&#xFC;bersetzten) Bezeichnung zu generieren</notes>
92
+ <authors><author><name>Eurotext AG</name><user>eurotext</user><email>info@eurotext.de</email></author></authors>
93
+ <date>2016-06-30</date>
94
+ <time>09:58:17</time>
95
+ <contents><target name="magecommunity"><dir name="Eurotext"><dir name="TranslationManager"><dir name="Block"><file name="Help.php" hash="ca61b416958f9a5304a94a9ce69df7da"/><file name="Projects.php" hash="ce7c629995f4c977d5bed0c91659961c"/><file name="Register.php" hash="1fb433fe50938c92b848ddbb8b6568e9"/><dir name="Response"><file name="Ajax.php" hash="54b099def382c680ca5e06be65aaa8e7"/></dir><file name="Selectcategories.php" hash="26572a232119318b324c6173e09c248d"/><file name="Selectcmspages.php" hash="0b91f0c03677b44b4c9b8c8243c18fd2"/><file name="Selectemails.php" hash="45f64f329f2b0f158a2d8e33a1ad8310"/><file name="Selectlangfiles.php" hash="2fdaca1539e88fbc4f44116a9453abc2"/><file name="Selectproducts.php" hash="d3d74ab6393d6521cc0b46523f8e882b"/><file name="Settings.php" hash="4e5bc502fd871664ca56f2a6851f01a7"/></dir><dir name="Helper"><file name="Data.php" hash="48331ebc701a0e6cbf00b6da56660717"/><file name="Eurotext.php" hash="8b6b610dbd3f63257ce44bceeb0743e1"/></dir><dir name="Model"><dir name="Resource"><file name="Setup.php" hash="992b5dbedd9dc6d669ff7a7949c790ab"/></dir><file name=".DS_Store" hash="7c3522d12f27b1bc414fac586a4b971f"/></dir><dir name="controllers"><dir name="Adminhtml"><dir name="Eurotext"><dir name="Translationmanager"><file name="HelpController.php" hash="2073b9b65b11df0e31157128af9d47c0"/><file name="ProjectsController.php" hash="dd9148ba09b929295a810e69e2948636"/><file name="RegisterController.php" hash="6d610017c92deba24e65223fb5fd9c54"/><file name="SelectcategoriesController.php" hash="79a4c379f47f9d353e850d5afaa321f2"/><file name="SelectcmspagesController.php" hash="c65ee51e92b29c0322c0924fa6ca9dac"/><file name="SelectemailsController.php" hash="e8b2bdd15cdae614012bc05abdd81d63"/><file name="SelectlangfilesController.php" hash="5df24ee920c75cf4a530611481826b50"/><file name="SelectproductsController.php" hash="c75d91b8706f25e9e4bb47f3843f5e3d"/><file name="SettingsController.php" hash="9f3916cfa1e9ffe8ba82955c9f48c58a"/></dir><file name=".DS_Store" hash="be9480710cd7aa8bcc8ca162bbe60791"/></dir><file name=".DS_Store" hash="af0a7d68dfc1505f5fd04c55baff3727"/></dir><file name=".DS_Store" hash="9d6e9b7cea3d170ee9c8cfccccc4ee80"/></dir><dir name="etc"><file name="adminhtml.xml" hash="a7235397925e4a68962ceaf8bd498c8c"/><file name="config.xml" hash="d78d111c2b25a56153bc9e2bb3b6da97"/><file name="jstranslator.xml" hash="5a9f0af14a162abf86bc2f2afad5f841"/><file name="system.xml" hash="6fc0f8c314886f0cb3671db6efdaa7f6"/></dir><dir name="sql"><dir name="eurotext_translationmanager_setup"><file name="mysql4-install-1.0.0.0.php" hash="e18c0008fdf86231a2b9fc5fdaf772f6"/><file name="mysql4-upgrade-1.0.0.5-1.0.0.6.php" hash="ea4e8bfeb96b41454a6270fc679a97fc"/><file name="mysql4-upgrade-1.0.0.7-1.0.0.8.php" hash="b610342ad985cd23f823cb05048c172d"/></dir></dir><file name=".DS_Store" hash="a747456783fab7a1190bb4bb3c45424a"/></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="base"><dir name="default"><dir name="layout"><dir name="eurotext"><file name="translationmanager.xml" hash="3a44d296d48ae2b34424ec2a61b7434f"/></dir></dir><dir name="template"><dir name="eurotext"><dir name="translationmanager"><file name="help.phtml" hash="a3b027449fb83c4c6fbc563d8ed23e93"/><file name="projects.phtml" hash="a42131faebe288458943a8efc7b00665"/><file name="register.phtml" hash="a3f8f80005c2d2991571f2dab9634cf5"/><file name="selectcategories.phtml" hash="885c57925f375c8b6b32ec986bb58de2"/><file name="selectcmspages.phtml" hash="616ae977dd80b588084b061862f05095"/><file name="selectemails.phtml" hash="a5c31d90157f73ff702f92af35b323df"/><file name="selectlangfiles.phtml" hash="a121b4a5c0a40474260a9c94947100c0"/><file name="selectproducts.phtml" hash="4c54a9d4ea954711812896279337dd6c"/><file name="settings.phtml" hash="845d24724446ec369b95c605fa1e5189"/></dir></dir></dir></dir></dir></dir></target><target name="magelocale"><dir name="en_US"><file name="Eurotext_TranslationManager.csv" hash="8b76eae9c7f6318779e19ce2050365cb"/></dir><dir name="de_DE"><file name="Eurotext_TranslationManager.csv" hash="bdcdf13c3490d89fa92779fbc42465da"/></dir></target><target name="mageetc"><dir name="modules"><file name="Eurotext_TranslationManager.xml" hash="c07fa4fd43f46687c594e3ecd76b72c9"/></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="base"><dir name="default"><dir name="eurotext"><dir name="translationmanager"><dir name="css"><file name="styles.css" hash="ca6ddd95cc42d1a802aaa1fa85d401a5"/></dir><dir name="images"><file name="btt_suche.png" hash="9b74d62b9840cafb6990352c52a39458"/><file name="cat-minus.png" hash="d092af81ca37c821e13f27cc9bc44378"/><file name="cat-none.png" hash="88805f6b1ed90aa66db3cc6f89c83db0"/><file name="cat-plus.png" hash="16d73e360bebbeb16c4115ceea771b56"/><file name="eurotext_logo.png" hash="87120ee2d376e3195e2e7cd8b9661ba7"/><file name="eurotext_table_head.png" hash="36583e9470155c585304e305133e13ec"/><file name="logo_dixeno.png" hash="dd23f387d0eee8a9494724a470d8742e"/><file name="logo_eurotext.png" hash="7a8627f728f203823fbee1575d223131"/><file name="pager_arrow_left.gif" hash="6e44f608dac8eb8be1d1ebee5a3285aa"/><file name="pager_arrow_left_off.gif" hash="30f5e56195ab3546b36f4016db5254be"/><file name="pager_arrow_right.gif" hash="cc79526156b7e0c8abce61fad3d53f77"/><file name="pager_arrow_right_off.gif" hash="f01b7dad9acf0180b5c62edbd7ea9939"/></dir><dir name="js"><file name="eurotext-jquery-1.9.1.js" hash="345bf9214f44da83c245ef933035937d"/><file name="eurotext.js" hash="7fa29d5a26baa83adccbe1c082e362a6"/></dir></dir></dir></dir></dir></dir></target></contents>
96
+ <compatible/>
97
+ <dependencies><required><php><min>5.2.0</min><max>7.0.0</max></php></required></dependencies>
98
+ </package>
skin/adminhtml/base/default/eurotext/translationmanager/css/styles.css ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .eurotext_td { padding-right:20px; }
2
+ .et_tdc { text-align:center;vertical-align:top; }
3
+ .et_toptd { vertical-align:top; }
4
+ .et_infobox { color:gray; font-style:italic; }
5
+ .et_btt
6
+ {
7
+ -moz-border-radius:3px 3px 3px 3px;
8
+ -webkit-border-radius:3px 3px 3px 3px;
9
+ border-radius:3px 3px 3px 3px;
10
+ background:-moz-linear-gradient(center top , #FFFFFF, #EFEFEF) repeat scroll 0 0 #F6F6F6;
11
+ background:-webkit-gradient(linear,center top, center bottom, from(#FFFFFF), to(#EFEFEF));
12
+ border:1px solid #CCCCCC;
13
+ cursor:pointer;
14
+ overflow:visible;
15
+ display:inline-block;
16
+ padding-left:10px;
17
+ padding-right:10px;
18
+ padding-top:5px;
19
+ padding-bottom:5px;
20
+ vertical-align:middle;
21
+ white-space:nowrap;
22
+ text-decoration:none
23
+ }
24
+
25
+ .eurotext_category_plus
26
+ {
27
+ background:url('../images/cat-plus.png');
28
+ width:16px;
29
+ height:18px;
30
+ }
31
+
32
+ .eurotext_category_minus
33
+ {
34
+ background:url('../images/cat-minus.png');
35
+ width:16px;
36
+ height:18px;
37
+ }
38
+
39
+ .eurotext_category_none
40
+ {
41
+ background:url('../images/cat-none.png');
42
+ width:16px;
43
+ height:18px;
44
+ }
45
+
46
+ .eurotext_config
47
+ {
48
+ background:#FAFAFA;
49
+ border:2px solid #A3B8BF;
50
+ border-top:0px;
51
+ }
52
+ .eurotext_confighead
53
+ {
54
+ background:#6f8992; padding:2px 10px;
55
+ color:white;
56
+ font-weight:bold;
57
+ }
58
+
59
+ .eurotext_toptd
60
+ {
61
+ vertical-align:top;
62
+ }
63
+
64
+ .eurotext_edittext
65
+ {
66
+ padding:5px;
67
+ }
68
+
69
+ .eurotext_logo
70
+ {
71
+ background:url('../images/eurotext_logo.png'); width:453px; height:69px; display:inline-block;
72
+ }
73
+
74
+ .logo_eurotext
75
+ {
76
+ background:url('../images/logo_eurotext.png'); width:244px; height:69px; display:inline-block;
77
+ }
78
+
79
+
80
+ tr.selected_project { background:#F6C27E !important; }
81
+
82
+ .et_error { font-weight:bold; color:red; }
83
+ .et_ok { font-weight:bold; color:green; }
84
+ .et_th { padding-right:10px; font-weight:bold; }
85
+ .et_tc { padding-right:10px; }
86
+ .et_header { font-size:11pt; font-weight:bold; margin-bottom:10px; }
87
+
88
+ .eurotext_grid table td
89
+ {
90
+ padding:15px;
91
+ }
92
+
93
+ .eurotext_head
94
+ {
95
+ padding: 2px 4px 2px 14px !important; font-weight:bold !important;
96
+ }
97
+
98
+ .eurotext_head2
99
+ {
100
+ padding: 10px !important;
101
+ font-weight:bold !important;
102
+ font-size:12pt !important;
103
+
104
+ background: #ebebeb; /* Old browsers */
105
+ background: -moz-linear-gradient(top, #ebebeb 0%, #a5a5a5 93%, #a5a5a5 100%); /* FF3.6+ */
106
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ebebeb), color-stop(93%,#a5a5a5), color-stop(100%,#a5a5a5)); /* Chrome,Safari4+ */
107
+ background: -webkit-linear-gradient(top, #ebebeb 0%,#a5a5a5 93%,#a5a5a5 100%); /* Chrome10+,Safari5.1+ */
108
+ background: -o-linear-gradient(top, #ebebeb 0%,#a5a5a5 93%,#a5a5a5 100%); /* Opera 11.10+ */
109
+ background: -ms-linear-gradient(top, #ebebeb 0%,#a5a5a5 93%,#a5a5a5 100%); /* IE10+ */
110
+ background: linear-gradient(to bottom, #ebebeb 0%,#a5a5a5 93%,#a5a5a5 100%); /* W3C */
111
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ebebeb', endColorstr='#a5a5a5',GradientType=0 ); /* IE6-9 */
112
+ }
113
+
114
+ .eurotext_layertitle
115
+ {
116
+ font-size:11pt;
117
+ color:#F59200;
118
+ font-weight:bold;
119
+ }
120
+
121
+ .eurotext_layerstrip
122
+ {
123
+ border-top:6px #DFDFDF solid;
124
+ margin-bottom:15px;
125
+ }
126
+
127
+ .eurotext_layercellheader
128
+ {
129
+ background-color:#6E8992;
130
+ color:white;
131
+ font-weight:bold;
132
+ padding:3px;
133
+ }
134
+
135
+ .eurotext_back_disabled
136
+ {
137
+ background:url('../images/pager_arrow_left_off.gif');
138
+ width:13px;
139
+ height:13px;
140
+ display:inline-block;
141
+ vertical-align:middle;
142
+ }
143
+
144
+ .eurotext_back_enabled
145
+ {
146
+ background:url('../images/pager_arrow_left.gif');
147
+ width:13px;
148
+ height:13px;
149
+ display:inline-block;
150
+ vertical-align:middle;
151
+ }
152
+
153
+ .eurotext_next_disabled
154
+ {
155
+ background:url('../images/pager_arrow_right_off.gif');
156
+ width:13px;
157
+ height:13px;
158
+ display:inline-block;
159
+ vertical-align:middle;
160
+ }
161
+
162
+ .eurotext_next_enabled
163
+ {
164
+ background:url('../images/pager_arrow_right.gif');
165
+ width:13px;
166
+ height:13px;
167
+ display:inline-block;
168
+ vertical-align:middle;
169
+ }
170
+
171
+ .eurotext_r1
172
+ {
173
+ background-color:#F6F6F6;
174
+ padding:4px;
175
+ }
176
+
177
+ .eurotext_r0
178
+ {
179
+ background-color:#FFFFFF;
180
+ padding:4px;
181
+ }
182
+
183
+ .eurotext2_r1
184
+ {
185
+ background-color:#F2F2F2;
186
+ padding:4px;
187
+ }
188
+
189
+ .eurotext2_r0
190
+ {
191
+ background-color:#EAEAEA;
192
+ padding:4px;
193
+ }
194
+
195
+ .eurotext_vcenter
196
+ {
197
+ vertical-align:middle;
198
+ }
199
+
200
+ .eurotext_findbtt
201
+ {
202
+ background:url('../images/btt_suche.png');
203
+ width:52px;
204
+ height:21px;
205
+ display:inline-block;
206
+ }
skin/adminhtml/base/default/eurotext/translationmanager/images/btt_suche.png ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/cat-minus.png ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/cat-none.png ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/cat-plus.png ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/eurotext_logo.png ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/eurotext_table_head.png ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/logo_dixeno.png ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/logo_eurotext.png ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/pager_arrow_left.gif ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/pager_arrow_left_off.gif ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/pager_arrow_right.gif ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/images/pager_arrow_right_off.gif ADDED
Binary file
skin/adminhtml/base/default/eurotext/translationmanager/js/eurotext-jquery-1.9.1.js ADDED
@@ -0,0 +1,9597 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * jQuery JavaScript Library v1.9.1
3
+ * http://jquery.com/
4
+ *
5
+ * Includes Sizzle.js
6
+ * http://sizzlejs.com/
7
+ *
8
+ * Copyright 2005, 2012 jQuery Foundation, Inc. and other contributors
9
+ * Released under the MIT license
10
+ * http://jquery.org/license
11
+ *
12
+ * Date: 2013-2-4
13
+ */
14
+ (function( window, undefined ) {
15
+
16
+ // Can't do this because several apps including ASP.NET trace
17
+ // the stack via arguments.caller.callee and Firefox dies if
18
+ // you try to trace through "use strict" call chains. (#13335)
19
+ // Support: Firefox 18+
20
+ //"use strict";
21
+ var
22
+ // The deferred used on DOM ready
23
+ readyList,
24
+
25
+ // A central reference to the root jQuery(document)
26
+ rootjQuery,
27
+
28
+ // Support: IE<9
29
+ // For `typeof node.method` instead of `node.method !== undefined`
30
+ core_strundefined = typeof undefined,
31
+
32
+ // Use the correct document accordingly with window argument (sandbox)
33
+ document = window.document,
34
+ location = window.location,
35
+
36
+ // Map over jQuery in case of overwrite
37
+ _jQuery = window.jQuery,
38
+
39
+ // Map over the $ in case of overwrite
40
+ _$ = window.$,
41
+
42
+ // [[Class]] -> type pairs
43
+ class2type = {},
44
+
45
+ // List of deleted data cache ids, so we can reuse them
46
+ core_deletedIds = [],
47
+
48
+ core_version = "1.9.1",
49
+
50
+ // Save a reference to some core methods
51
+ core_concat = core_deletedIds.concat,
52
+ core_push = core_deletedIds.push,
53
+ core_slice = core_deletedIds.slice,
54
+ core_indexOf = core_deletedIds.indexOf,
55
+ core_toString = class2type.toString,
56
+ core_hasOwn = class2type.hasOwnProperty,
57
+ core_trim = core_version.trim,
58
+
59
+ // Define a local copy of jQuery
60
+ jQuery = function( selector, context ) {
61
+ // The jQuery object is actually just the init constructor 'enhanced'
62
+ return new jQuery.fn.init( selector, context, rootjQuery );
63
+ },
64
+
65
+ // Used for matching numbers
66
+ core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
67
+
68
+ // Used for splitting on whitespace
69
+ core_rnotwhite = /\S+/g,
70
+
71
+ // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
72
+ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
73
+
74
+ // A simple way to check for HTML strings
75
+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
76
+ // Strict HTML recognition (#11290: must start with <)
77
+ rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,
78
+
79
+ // Match a standalone tag
80
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
81
+
82
+ // JSON RegExp
83
+ rvalidchars = /^[\],:{}\s]*$/,
84
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
85
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
86
+ rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,
87
+
88
+ // Matches dashed string for camelizing
89
+ rmsPrefix = /^-ms-/,
90
+ rdashAlpha = /-([\da-z])/gi,
91
+
92
+ // Used by jQuery.camelCase as callback to replace()
93
+ fcamelCase = function( all, letter ) {
94
+ return letter.toUpperCase();
95
+ },
96
+
97
+ // The ready event handler
98
+ completed = function( event ) {
99
+
100
+ // readyState === "complete" is good enough for us to call the dom ready in oldIE
101
+ if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
102
+ detach();
103
+ jQuery.ready();
104
+ }
105
+ },
106
+ // Clean-up method for dom ready events
107
+ detach = function() {
108
+ if ( document.addEventListener ) {
109
+ document.removeEventListener( "DOMContentLoaded", completed, false );
110
+ window.removeEventListener( "load", completed, false );
111
+
112
+ } else {
113
+ document.detachEvent( "onreadystatechange", completed );
114
+ window.detachEvent( "onload", completed );
115
+ }
116
+ };
117
+
118
+ jQuery.fn = jQuery.prototype = {
119
+ // The current version of jQuery being used
120
+ jquery: core_version,
121
+
122
+ constructor: jQuery,
123
+ init: function( selector, context, rootjQuery ) {
124
+ var match, elem;
125
+
126
+ // HANDLE: $(""), $(null), $(undefined), $(false)
127
+ if ( !selector ) {
128
+ return this;
129
+ }
130
+
131
+ // Handle HTML strings
132
+ if ( typeof selector === "string" ) {
133
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
134
+ // Assume that strings that start and end with <> are HTML and skip the regex check
135
+ match = [ null, selector, null ];
136
+
137
+ } else {
138
+ match = rquickExpr.exec( selector );
139
+ }
140
+
141
+ // Match html or make sure no context is specified for #id
142
+ if ( match && (match[1] || !context) ) {
143
+
144
+ // HANDLE: $(html) -> $(array)
145
+ if ( match[1] ) {
146
+ context = context instanceof jQuery ? context[0] : context;
147
+
148
+ // scripts is true for back-compat
149
+ jQuery.merge( this, jQuery.parseHTML(
150
+ match[1],
151
+ context && context.nodeType ? context.ownerDocument || context : document,
152
+ true
153
+ ) );
154
+
155
+ // HANDLE: $(html, props)
156
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
157
+ for ( match in context ) {
158
+ // Properties of context are called as methods if possible
159
+ if ( jQuery.isFunction( this[ match ] ) ) {
160
+ this[ match ]( context[ match ] );
161
+
162
+ // ...and otherwise set as attributes
163
+ } else {
164
+ this.attr( match, context[ match ] );
165
+ }
166
+ }
167
+ }
168
+
169
+ return this;
170
+
171
+ // HANDLE: $(#id)
172
+ } else {
173
+ elem = document.getElementById( match[2] );
174
+
175
+ // Check parentNode to catch when Blackberry 4.6 returns
176
+ // nodes that are no longer in the document #6963
177
+ if ( elem && elem.parentNode ) {
178
+ // Handle the case where IE and Opera return items
179
+ // by name instead of ID
180
+ if ( elem.id !== match[2] ) {
181
+ return rootjQuery.find( selector );
182
+ }
183
+
184
+ // Otherwise, we inject the element directly into the jQuery object
185
+ this.length = 1;
186
+ this[0] = elem;
187
+ }
188
+
189
+ this.context = document;
190
+ this.selector = selector;
191
+ return this;
192
+ }
193
+
194
+ // HANDLE: $(expr, $(...))
195
+ } else if ( !context || context.jquery ) {
196
+ return ( context || rootjQuery ).find( selector );
197
+
198
+ // HANDLE: $(expr, context)
199
+ // (which is just equivalent to: $(context).find(expr)
200
+ } else {
201
+ return this.constructor( context ).find( selector );
202
+ }
203
+
204
+ // HANDLE: $(DOMElement)
205
+ } else if ( selector.nodeType ) {
206
+ this.context = this[0] = selector;
207
+ this.length = 1;
208
+ return this;
209
+
210
+ // HANDLE: $(function)
211
+ // Shortcut for document ready
212
+ } else if ( jQuery.isFunction( selector ) ) {
213
+ return rootjQuery.ready( selector );
214
+ }
215
+
216
+ if ( selector.selector !== undefined ) {
217
+ this.selector = selector.selector;
218
+ this.context = selector.context;
219
+ }
220
+
221
+ return jQuery.makeArray( selector, this );
222
+ },
223
+
224
+ // Start with an empty selector
225
+ selector: "",
226
+
227
+ // The default length of a jQuery object is 0
228
+ length: 0,
229
+
230
+ // The number of elements contained in the matched element set
231
+ size: function() {
232
+ return this.length;
233
+ },
234
+
235
+ toArray: function() {
236
+ return core_slice.call( this );
237
+ },
238
+
239
+ // Get the Nth element in the matched element set OR
240
+ // Get the whole matched element set as a clean array
241
+ get: function( num ) {
242
+ return num == null ?
243
+
244
+ // Return a 'clean' array
245
+ this.toArray() :
246
+
247
+ // Return just the object
248
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
249
+ },
250
+
251
+ // Take an array of elements and push it onto the stack
252
+ // (returning the new matched element set)
253
+ pushStack: function( elems ) {
254
+
255
+ // Build a new jQuery matched element set
256
+ var ret = jQuery.merge( this.constructor(), elems );
257
+
258
+ // Add the old object onto the stack (as a reference)
259
+ ret.prevObject = this;
260
+ ret.context = this.context;
261
+
262
+ // Return the newly-formed element set
263
+ return ret;
264
+ },
265
+
266
+ // Execute a callback for every element in the matched set.
267
+ // (You can seed the arguments with an array of args, but this is
268
+ // only used internally.)
269
+ each: function( callback, args ) {
270
+ return jQuery.each( this, callback, args );
271
+ },
272
+
273
+ ready: function( fn ) {
274
+ // Add the callback
275
+ jQuery.ready.promise().done( fn );
276
+
277
+ return this;
278
+ },
279
+
280
+ slice: function() {
281
+ return this.pushStack( core_slice.apply( this, arguments ) );
282
+ },
283
+
284
+ first: function() {
285
+ return this.eq( 0 );
286
+ },
287
+
288
+ last: function() {
289
+ return this.eq( -1 );
290
+ },
291
+
292
+ eq: function( i ) {
293
+ var len = this.length,
294
+ j = +i + ( i < 0 ? len : 0 );
295
+ return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
296
+ },
297
+
298
+ map: function( callback ) {
299
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
300
+ return callback.call( elem, i, elem );
301
+ }));
302
+ },
303
+
304
+ end: function() {
305
+ return this.prevObject || this.constructor(null);
306
+ },
307
+
308
+ // For internal use only.
309
+ // Behaves like an Array's method, not like a jQuery method.
310
+ push: core_push,
311
+ sort: [].sort,
312
+ splice: [].splice
313
+ };
314
+
315
+ // Give the init function the jQuery prototype for later instantiation
316
+ jQuery.fn.init.prototype = jQuery.fn;
317
+
318
+ jQuery.extend = jQuery.fn.extend = function() {
319
+ var src, copyIsArray, copy, name, options, clone,
320
+ target = arguments[0] || {},
321
+ i = 1,
322
+ length = arguments.length,
323
+ deep = false;
324
+
325
+ // Handle a deep copy situation
326
+ if ( typeof target === "boolean" ) {
327
+ deep = target;
328
+ target = arguments[1] || {};
329
+ // skip the boolean and the target
330
+ i = 2;
331
+ }
332
+
333
+ // Handle case when target is a string or something (possible in deep copy)
334
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
335
+ target = {};
336
+ }
337
+
338
+ // extend jQuery itself if only one argument is passed
339
+ if ( length === i ) {
340
+ target = this;
341
+ --i;
342
+ }
343
+
344
+ for ( ; i < length; i++ ) {
345
+ // Only deal with non-null/undefined values
346
+ if ( (options = arguments[ i ]) != null ) {
347
+ // Extend the base object
348
+ for ( name in options ) {
349
+ src = target[ name ];
350
+ copy = options[ name ];
351
+
352
+ // Prevent never-ending loop
353
+ if ( target === copy ) {
354
+ continue;
355
+ }
356
+
357
+ // Recurse if we're merging plain objects or arrays
358
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
359
+ if ( copyIsArray ) {
360
+ copyIsArray = false;
361
+ clone = src && jQuery.isArray(src) ? src : [];
362
+
363
+ } else {
364
+ clone = src && jQuery.isPlainObject(src) ? src : {};
365
+ }
366
+
367
+ // Never move original objects, clone them
368
+ target[ name ] = jQuery.extend( deep, clone, copy );
369
+
370
+ // Don't bring in undefined values
371
+ } else if ( copy !== undefined ) {
372
+ target[ name ] = copy;
373
+ }
374
+ }
375
+ }
376
+ }
377
+
378
+ // Return the modified object
379
+ return target;
380
+ };
381
+
382
+ jQuery.extend({
383
+ noConflict: function( deep ) {
384
+ if ( window.$ === jQuery ) {
385
+ window.$ = _$;
386
+ }
387
+
388
+ if ( deep && window.jQuery === jQuery ) {
389
+ window.jQuery = _jQuery;
390
+ }
391
+
392
+ return jQuery;
393
+ },
394
+
395
+ // Is the DOM ready to be used? Set to true once it occurs.
396
+ isReady: false,
397
+
398
+ // A counter to track how many items to wait for before
399
+ // the ready event fires. See #6781
400
+ readyWait: 1,
401
+
402
+ // Hold (or release) the ready event
403
+ holdReady: function( hold ) {
404
+ if ( hold ) {
405
+ jQuery.readyWait++;
406
+ } else {
407
+ jQuery.ready( true );
408
+ }
409
+ },
410
+
411
+ // Handle when the DOM is ready
412
+ ready: function( wait ) {
413
+
414
+ // Abort if there are pending holds or we're already ready
415
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
416
+ return;
417
+ }
418
+
419
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
420
+ if ( !document.body ) {
421
+ return setTimeout( jQuery.ready );
422
+ }
423
+
424
+ // Remember that the DOM is ready
425
+ jQuery.isReady = true;
426
+
427
+ // If a normal DOM Ready event fired, decrement, and wait if need be
428
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
429
+ return;
430
+ }
431
+
432
+ // If there are functions bound, to execute
433
+ readyList.resolveWith( document, [ jQuery ] );
434
+
435
+ // Trigger any bound ready events
436
+ if ( jQuery.fn.trigger ) {
437
+ jQuery( document ).trigger("ready").off("ready");
438
+ }
439
+ },
440
+
441
+ // See test/unit/core.js for details concerning isFunction.
442
+ // Since version 1.3, DOM methods and functions like alert
443
+ // aren't supported. They return false on IE (#2968).
444
+ isFunction: function( obj ) {
445
+ return jQuery.type(obj) === "function";
446
+ },
447
+
448
+ isArray: Array.isArray || function( obj ) {
449
+ return jQuery.type(obj) === "array";
450
+ },
451
+
452
+ isWindow: function( obj ) {
453
+ return obj != null && obj == obj.window;
454
+ },
455
+
456
+ isNumeric: function( obj ) {
457
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
458
+ },
459
+
460
+ type: function( obj ) {
461
+ if ( obj == null ) {
462
+ return String( obj );
463
+ }
464
+ return typeof obj === "object" || typeof obj === "function" ?
465
+ class2type[ core_toString.call(obj) ] || "object" :
466
+ typeof obj;
467
+ },
468
+
469
+ isPlainObject: function( obj ) {
470
+ // Must be an Object.
471
+ // Because of IE, we also have to check the presence of the constructor property.
472
+ // Make sure that DOM nodes and window objects don't pass through, as well
473
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
474
+ return false;
475
+ }
476
+
477
+ try {
478
+ // Not own constructor property must be Object
479
+ if ( obj.constructor &&
480
+ !core_hasOwn.call(obj, "constructor") &&
481
+ !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
482
+ return false;
483
+ }
484
+ } catch ( e ) {
485
+ // IE8,9 Will throw exceptions on certain host objects #9897
486
+ return false;
487
+ }
488
+
489
+ // Own properties are enumerated firstly, so to speed up,
490
+ // if last one is own, then all properties are own.
491
+
492
+ var key;
493
+ for ( key in obj ) {}
494
+
495
+ return key === undefined || core_hasOwn.call( obj, key );
496
+ },
497
+
498
+ isEmptyObject: function( obj ) {
499
+ var name;
500
+ for ( name in obj ) {
501
+ return false;
502
+ }
503
+ return true;
504
+ },
505
+
506
+ error: function( msg ) {
507
+ throw new Error( msg );
508
+ },
509
+
510
+ // data: string of html
511
+ // context (optional): If specified, the fragment will be created in this context, defaults to document
512
+ // keepScripts (optional): If true, will include scripts passed in the html string
513
+ parseHTML: function( data, context, keepScripts ) {
514
+ if ( !data || typeof data !== "string" ) {
515
+ return null;
516
+ }
517
+ if ( typeof context === "boolean" ) {
518
+ keepScripts = context;
519
+ context = false;
520
+ }
521
+ context = context || document;
522
+
523
+ var parsed = rsingleTag.exec( data ),
524
+ scripts = !keepScripts && [];
525
+
526
+ // Single tag
527
+ if ( parsed ) {
528
+ return [ context.createElement( parsed[1] ) ];
529
+ }
530
+
531
+ parsed = jQuery.buildFragment( [ data ], context, scripts );
532
+ if ( scripts ) {
533
+ jQuery( scripts ).remove();
534
+ }
535
+ return jQuery.merge( [], parsed.childNodes );
536
+ },
537
+
538
+ parseJSON: function( data ) {
539
+ // Attempt to parse using the native JSON parser first
540
+ if ( window.JSON && window.JSON.parse ) {
541
+ return window.JSON.parse( data );
542
+ }
543
+
544
+ if ( data === null ) {
545
+ return data;
546
+ }
547
+
548
+ if ( typeof data === "string" ) {
549
+
550
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
551
+ data = jQuery.trim( data );
552
+
553
+ if ( data ) {
554
+ // Make sure the incoming data is actual JSON
555
+ // Logic borrowed from http://json.org/json2.js
556
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
557
+ .replace( rvalidtokens, "]" )
558
+ .replace( rvalidbraces, "")) ) {
559
+
560
+ return ( new Function( "return " + data ) )();
561
+ }
562
+ }
563
+ }
564
+
565
+ jQuery.error( "Invalid JSON: " + data );
566
+ },
567
+
568
+ // Cross-browser xml parsing
569
+ parseXML: function( data ) {
570
+ var xml, tmp;
571
+ if ( !data || typeof data !== "string" ) {
572
+ return null;
573
+ }
574
+ try {
575
+ if ( window.DOMParser ) { // Standard
576
+ tmp = new DOMParser();
577
+ xml = tmp.parseFromString( data , "text/xml" );
578
+ } else { // IE
579
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
580
+ xml.async = "false";
581
+ xml.loadXML( data );
582
+ }
583
+ } catch( e ) {
584
+ xml = undefined;
585
+ }
586
+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
587
+ jQuery.error( "Invalid XML: " + data );
588
+ }
589
+ return xml;
590
+ },
591
+
592
+ noop: function() {},
593
+
594
+ // Evaluates a script in a global context
595
+ // Workarounds based on findings by Jim Driscoll
596
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
597
+ globalEval: function( data ) {
598
+ if ( data && jQuery.trim( data ) ) {
599
+ // We use execScript on Internet Explorer
600
+ // We use an anonymous function so that context is window
601
+ // rather than jQuery in Firefox
602
+ ( window.execScript || function( data ) {
603
+ window[ "eval" ].call( window, data );
604
+ } )( data );
605
+ }
606
+ },
607
+
608
+ // Convert dashed to camelCase; used by the css and data modules
609
+ // Microsoft forgot to hump their vendor prefix (#9572)
610
+ camelCase: function( string ) {
611
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
612
+ },
613
+
614
+ nodeName: function( elem, name ) {
615
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
616
+ },
617
+
618
+ // args is for internal usage only
619
+ each: function( obj, callback, args ) {
620
+ var value,
621
+ i = 0,
622
+ length = obj.length,
623
+ isArray = isArraylike( obj );
624
+
625
+ if ( args ) {
626
+ if ( isArray ) {
627
+ for ( ; i < length; i++ ) {
628
+ value = callback.apply( obj[ i ], args );
629
+
630
+ if ( value === false ) {
631
+ break;
632
+ }
633
+ }
634
+ } else {
635
+ for ( i in obj ) {
636
+ value = callback.apply( obj[ i ], args );
637
+
638
+ if ( value === false ) {
639
+ break;
640
+ }
641
+ }
642
+ }
643
+
644
+ // A special, fast, case for the most common use of each
645
+ } else {
646
+ if ( isArray ) {
647
+ for ( ; i < length; i++ ) {
648
+ value = callback.call( obj[ i ], i, obj[ i ] );
649
+
650
+ if ( value === false ) {
651
+ break;
652
+ }
653
+ }
654
+ } else {
655
+ for ( i in obj ) {
656
+ value = callback.call( obj[ i ], i, obj[ i ] );
657
+
658
+ if ( value === false ) {
659
+ break;
660
+ }
661
+ }
662
+ }
663
+ }
664
+
665
+ return obj;
666
+ },
667
+
668
+ // Use native String.trim function wherever possible
669
+ trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
670
+ function( text ) {
671
+ return text == null ?
672
+ "" :
673
+ core_trim.call( text );
674
+ } :
675
+
676
+ // Otherwise use our own trimming functionality
677
+ function( text ) {
678
+ return text == null ?
679
+ "" :
680
+ ( text + "" ).replace( rtrim, "" );
681
+ },
682
+
683
+ // results is for internal usage only
684
+ makeArray: function( arr, results ) {
685
+ var ret = results || [];
686
+
687
+ if ( arr != null ) {
688
+ if ( isArraylike( Object(arr) ) ) {
689
+ jQuery.merge( ret,
690
+ typeof arr === "string" ?
691
+ [ arr ] : arr
692
+ );
693
+ } else {
694
+ core_push.call( ret, arr );
695
+ }
696
+ }
697
+
698
+ return ret;
699
+ },
700
+
701
+ inArray: function( elem, arr, i ) {
702
+ var len;
703
+
704
+ if ( arr ) {
705
+ if ( core_indexOf ) {
706
+ return core_indexOf.call( arr, elem, i );
707
+ }
708
+
709
+ len = arr.length;
710
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
711
+
712
+ for ( ; i < len; i++ ) {
713
+ // Skip accessing in sparse arrays
714
+ if ( i in arr && arr[ i ] === elem ) {
715
+ return i;
716
+ }
717
+ }
718
+ }
719
+
720
+ return -1;
721
+ },
722
+
723
+ merge: function( first, second ) {
724
+ var l = second.length,
725
+ i = first.length,
726
+ j = 0;
727
+
728
+ if ( typeof l === "number" ) {
729
+ for ( ; j < l; j++ ) {
730
+ first[ i++ ] = second[ j ];
731
+ }
732
+ } else {
733
+ while ( second[j] !== undefined ) {
734
+ first[ i++ ] = second[ j++ ];
735
+ }
736
+ }
737
+
738
+ first.length = i;
739
+
740
+ return first;
741
+ },
742
+
743
+ grep: function( elems, callback, inv ) {
744
+ var retVal,
745
+ ret = [],
746
+ i = 0,
747
+ length = elems.length;
748
+ inv = !!inv;
749
+
750
+ // Go through the array, only saving the items
751
+ // that pass the validator function
752
+ for ( ; i < length; i++ ) {
753
+ retVal = !!callback( elems[ i ], i );
754
+ if ( inv !== retVal ) {
755
+ ret.push( elems[ i ] );
756
+ }
757
+ }
758
+
759
+ return ret;
760
+ },
761
+
762
+ // arg is for internal usage only
763
+ map: function( elems, callback, arg ) {
764
+ var value,
765
+ i = 0,
766
+ length = elems.length,
767
+ isArray = isArraylike( elems ),
768
+ ret = [];
769
+
770
+ // Go through the array, translating each of the items to their
771
+ if ( isArray ) {
772
+ for ( ; i < length; i++ ) {
773
+ value = callback( elems[ i ], i, arg );
774
+
775
+ if ( value != null ) {
776
+ ret[ ret.length ] = value;
777
+ }
778
+ }
779
+
780
+ // Go through every key on the object,
781
+ } else {
782
+ for ( i in elems ) {
783
+ value = callback( elems[ i ], i, arg );
784
+
785
+ if ( value != null ) {
786
+ ret[ ret.length ] = value;
787
+ }
788
+ }
789
+ }
790
+
791
+ // Flatten any nested arrays
792
+ return core_concat.apply( [], ret );
793
+ },
794
+
795
+ // A global GUID counter for objects
796
+ guid: 1,
797
+
798
+ // Bind a function to a context, optionally partially applying any
799
+ // arguments.
800
+ proxy: function( fn, context ) {
801
+ var args, proxy, tmp;
802
+
803
+ if ( typeof context === "string" ) {
804
+ tmp = fn[ context ];
805
+ context = fn;
806
+ fn = tmp;
807
+ }
808
+
809
+ // Quick check to determine if target is callable, in the spec
810
+ // this throws a TypeError, but we will just return undefined.
811
+ if ( !jQuery.isFunction( fn ) ) {
812
+ return undefined;
813
+ }
814
+
815
+ // Simulated bind
816
+ args = core_slice.call( arguments, 2 );
817
+ proxy = function() {
818
+ return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
819
+ };
820
+
821
+ // Set the guid of unique handler to the same of original handler, so it can be removed
822
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
823
+
824
+ return proxy;
825
+ },
826
+
827
+ // Multifunctional method to get and set values of a collection
828
+ // The value/s can optionally be executed if it's a function
829
+ access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
830
+ var i = 0,
831
+ length = elems.length,
832
+ bulk = key == null;
833
+
834
+ // Sets many values
835
+ if ( jQuery.type( key ) === "object" ) {
836
+ chainable = true;
837
+ for ( i in key ) {
838
+ jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
839
+ }
840
+
841
+ // Sets one value
842
+ } else if ( value !== undefined ) {
843
+ chainable = true;
844
+
845
+ if ( !jQuery.isFunction( value ) ) {
846
+ raw = true;
847
+ }
848
+
849
+ if ( bulk ) {
850
+ // Bulk operations run against the entire set
851
+ if ( raw ) {
852
+ fn.call( elems, value );
853
+ fn = null;
854
+
855
+ // ...except when executing function values
856
+ } else {
857
+ bulk = fn;
858
+ fn = function( elem, key, value ) {
859
+ return bulk.call( jQuery( elem ), value );
860
+ };
861
+ }
862
+ }
863
+
864
+ if ( fn ) {
865
+ for ( ; i < length; i++ ) {
866
+ fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
867
+ }
868
+ }
869
+ }
870
+
871
+ return chainable ?
872
+ elems :
873
+
874
+ // Gets
875
+ bulk ?
876
+ fn.call( elems ) :
877
+ length ? fn( elems[0], key ) : emptyGet;
878
+ },
879
+
880
+ now: function() {
881
+ return ( new Date() ).getTime();
882
+ }
883
+ });
884
+
885
+ jQuery.ready.promise = function( obj ) {
886
+ if ( !readyList ) {
887
+
888
+ readyList = jQuery.Deferred();
889
+
890
+ // Catch cases where $(document).ready() is called after the browser event has already occurred.
891
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
892
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
893
+ if ( document.readyState === "complete" ) {
894
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
895
+ setTimeout( jQuery.ready );
896
+
897
+ // Standards-based browsers support DOMContentLoaded
898
+ } else if ( document.addEventListener ) {
899
+ // Use the handy event callback
900
+ document.addEventListener( "DOMContentLoaded", completed, false );
901
+
902
+ // A fallback to window.onload, that will always work
903
+ window.addEventListener( "load", completed, false );
904
+
905
+ // If IE event model is used
906
+ } else {
907
+ // Ensure firing before onload, maybe late but safe also for iframes
908
+ document.attachEvent( "onreadystatechange", completed );
909
+
910
+ // A fallback to window.onload, that will always work
911
+ window.attachEvent( "onload", completed );
912
+
913
+ // If IE and not a frame
914
+ // continually check to see if the document is ready
915
+ var top = false;
916
+
917
+ try {
918
+ top = window.frameElement == null && document.documentElement;
919
+ } catch(e) {}
920
+
921
+ if ( top && top.doScroll ) {
922
+ (function doScrollCheck() {
923
+ if ( !jQuery.isReady ) {
924
+
925
+ try {
926
+ // Use the trick by Diego Perini
927
+ // http://javascript.nwbox.com/IEContentLoaded/
928
+ top.doScroll("left");
929
+ } catch(e) {
930
+ return setTimeout( doScrollCheck, 50 );
931
+ }
932
+
933
+ // detach all dom ready events
934
+ detach();
935
+
936
+ // and execute any waiting functions
937
+ jQuery.ready();
938
+ }
939
+ })();
940
+ }
941
+ }
942
+ }
943
+ return readyList.promise( obj );
944
+ };
945
+
946
+ // Populate the class2type map
947
+ jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
948
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
949
+ });
950
+
951
+ function isArraylike( obj ) {
952
+ var length = obj.length,
953
+ type = jQuery.type( obj );
954
+
955
+ if ( jQuery.isWindow( obj ) ) {
956
+ return false;
957
+ }
958
+
959
+ if ( obj.nodeType === 1 && length ) {
960
+ return true;
961
+ }
962
+
963
+ return type === "array" || type !== "function" &&
964
+ ( length === 0 ||
965
+ typeof length === "number" && length > 0 && ( length - 1 ) in obj );
966
+ }
967
+
968
+ // All jQuery objects should point back to these
969
+ rootjQuery = jQuery(document);
970
+ // String to Object options format cache
971
+ var optionsCache = {};
972
+
973
+ // Convert String-formatted options into Object-formatted ones and store in cache
974
+ function createOptions( options ) {
975
+ var object = optionsCache[ options ] = {};
976
+ jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
977
+ object[ flag ] = true;
978
+ });
979
+ return object;
980
+ }
981
+
982
+ /*
983
+ * Create a callback list using the following parameters:
984
+ *
985
+ * options: an optional list of space-separated options that will change how
986
+ * the callback list behaves or a more traditional option object
987
+ *
988
+ * By default a callback list will act like an event callback list and can be
989
+ * "fired" multiple times.
990
+ *
991
+ * Possible options:
992
+ *
993
+ * once: will ensure the callback list can only be fired once (like a Deferred)
994
+ *
995
+ * memory: will keep track of previous values and will call any callback added
996
+ * after the list has been fired right away with the latest "memorized"
997
+ * values (like a Deferred)
998
+ *
999
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
1000
+ *
1001
+ * stopOnFalse: interrupt callings when a callback returns false
1002
+ *
1003
+ */
1004
+ jQuery.Callbacks = function( options ) {
1005
+
1006
+ // Convert options from String-formatted to Object-formatted if needed
1007
+ // (we check in cache first)
1008
+ options = typeof options === "string" ?
1009
+ ( optionsCache[ options ] || createOptions( options ) ) :
1010
+ jQuery.extend( {}, options );
1011
+
1012
+ var // Flag to know if list is currently firing
1013
+ firing,
1014
+ // Last fire value (for non-forgettable lists)
1015
+ memory,
1016
+ // Flag to know if list was already fired
1017
+ fired,
1018
+ // End of the loop when firing
1019
+ firingLength,
1020
+ // Index of currently firing callback (modified by remove if needed)
1021
+ firingIndex,
1022
+ // First callback to fire (used internally by add and fireWith)
1023
+ firingStart,
1024
+ // Actual callback list
1025
+ list = [],
1026
+ // Stack of fire calls for repeatable lists
1027
+ stack = !options.once && [],
1028
+ // Fire callbacks
1029
+ fire = function( data ) {
1030
+ memory = options.memory && data;
1031
+ fired = true;
1032
+ firingIndex = firingStart || 0;
1033
+ firingStart = 0;
1034
+ firingLength = list.length;
1035
+ firing = true;
1036
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
1037
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
1038
+ memory = false; // To prevent further calls using add
1039
+ break;
1040
+ }
1041
+ }
1042
+ firing = false;
1043
+ if ( list ) {
1044
+ if ( stack ) {
1045
+ if ( stack.length ) {
1046
+ fire( stack.shift() );
1047
+ }
1048
+ } else if ( memory ) {
1049
+ list = [];
1050
+ } else {
1051
+ self.disable();
1052
+ }
1053
+ }
1054
+ },
1055
+ // Actual Callbacks object
1056
+ self = {
1057
+ // Add a callback or a collection of callbacks to the list
1058
+ add: function() {
1059
+ if ( list ) {
1060
+ // First, we save the current length
1061
+ var start = list.length;
1062
+ (function add( args ) {
1063
+ jQuery.each( args, function( _, arg ) {
1064
+ var type = jQuery.type( arg );
1065
+ if ( type === "function" ) {
1066
+ if ( !options.unique || !self.has( arg ) ) {
1067
+ list.push( arg );
1068
+ }
1069
+ } else if ( arg && arg.length && type !== "string" ) {
1070
+ // Inspect recursively
1071
+ add( arg );
1072
+ }
1073
+ });
1074
+ })( arguments );
1075
+ // Do we need to add the callbacks to the
1076
+ // current firing batch?
1077
+ if ( firing ) {
1078
+ firingLength = list.length;
1079
+ // With memory, if we're not firing then
1080
+ // we should call right away
1081
+ } else if ( memory ) {
1082
+ firingStart = start;
1083
+ fire( memory );
1084
+ }
1085
+ }
1086
+ return this;
1087
+ },
1088
+ // Remove a callback from the list
1089
+ remove: function() {
1090
+ if ( list ) {
1091
+ jQuery.each( arguments, function( _, arg ) {
1092
+ var index;
1093
+ while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
1094
+ list.splice( index, 1 );
1095
+ // Handle firing indexes
1096
+ if ( firing ) {
1097
+ if ( index <= firingLength ) {
1098
+ firingLength--;
1099
+ }
1100
+ if ( index <= firingIndex ) {
1101
+ firingIndex--;
1102
+ }
1103
+ }
1104
+ }
1105
+ });
1106
+ }
1107
+ return this;
1108
+ },
1109
+ // Check if a given callback is in the list.
1110
+ // If no argument is given, return whether or not list has callbacks attached.
1111
+ has: function( fn ) {
1112
+ return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
1113
+ },
1114
+ // Remove all callbacks from the list
1115
+ empty: function() {
1116
+ list = [];
1117
+ return this;
1118
+ },
1119
+ // Have the list do nothing anymore
1120
+ disable: function() {
1121
+ list = stack = memory = undefined;
1122
+ return this;
1123
+ },
1124
+ // Is it disabled?
1125
+ disabled: function() {
1126
+ return !list;
1127
+ },
1128
+ // Lock the list in its current state
1129
+ lock: function() {
1130
+ stack = undefined;
1131
+ if ( !memory ) {
1132
+ self.disable();
1133
+ }
1134
+ return this;
1135
+ },
1136
+ // Is it locked?
1137
+ locked: function() {
1138
+ return !stack;
1139
+ },
1140
+ // Call all callbacks with the given context and arguments
1141
+ fireWith: function( context, args ) {
1142
+ args = args || [];
1143
+ args = [ context, args.slice ? args.slice() : args ];
1144
+ if ( list && ( !fired || stack ) ) {
1145
+ if ( firing ) {
1146
+ stack.push( args );
1147
+ } else {
1148
+ fire( args );
1149
+ }
1150
+ }
1151
+ return this;
1152
+ },
1153
+ // Call all the callbacks with the given arguments
1154
+ fire: function() {
1155
+ self.fireWith( this, arguments );
1156
+ return this;
1157
+ },
1158
+ // To know if the callbacks have already been called at least once
1159
+ fired: function() {
1160
+ return !!fired;
1161
+ }
1162
+ };
1163
+
1164
+ return self;
1165
+ };
1166
+ jQuery.extend({
1167
+
1168
+ Deferred: function( func ) {
1169
+ var tuples = [
1170
+ // action, add listener, listener list, final state
1171
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
1172
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
1173
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
1174
+ ],
1175
+ state = "pending",
1176
+ promise = {
1177
+ state: function() {
1178
+ return state;
1179
+ },
1180
+ always: function() {
1181
+ deferred.done( arguments ).fail( arguments );
1182
+ return this;
1183
+ },
1184
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
1185
+ var fns = arguments;
1186
+ return jQuery.Deferred(function( newDefer ) {
1187
+ jQuery.each( tuples, function( i, tuple ) {
1188
+ var action = tuple[ 0 ],
1189
+ fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
1190
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
1191
+ deferred[ tuple[1] ](function() {
1192
+ var returned = fn && fn.apply( this, arguments );
1193
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
1194
+ returned.promise()
1195
+ .done( newDefer.resolve )
1196
+ .fail( newDefer.reject )
1197
+ .progress( newDefer.notify );
1198
+ } else {
1199
+ newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
1200
+ }
1201
+ });
1202
+ });
1203
+ fns = null;
1204
+ }).promise();
1205
+ },
1206
+ // Get a promise for this deferred
1207
+ // If obj is provided, the promise aspect is added to the object
1208
+ promise: function( obj ) {
1209
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
1210
+ }
1211
+ },
1212
+ deferred = {};
1213
+
1214
+ // Keep pipe for back-compat
1215
+ promise.pipe = promise.then;
1216
+
1217
+ // Add list-specific methods
1218
+ jQuery.each( tuples, function( i, tuple ) {
1219
+ var list = tuple[ 2 ],
1220
+ stateString = tuple[ 3 ];
1221
+
1222
+ // promise[ done | fail | progress ] = list.add
1223
+ promise[ tuple[1] ] = list.add;
1224
+
1225
+ // Handle state
1226
+ if ( stateString ) {
1227
+ list.add(function() {
1228
+ // state = [ resolved | rejected ]
1229
+ state = stateString;
1230
+
1231
+ // [ reject_list | resolve_list ].disable; progress_list.lock
1232
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
1233
+ }
1234
+
1235
+ // deferred[ resolve | reject | notify ]
1236
+ deferred[ tuple[0] ] = function() {
1237
+ deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
1238
+ return this;
1239
+ };
1240
+ deferred[ tuple[0] + "With" ] = list.fireWith;
1241
+ });
1242
+
1243
+ // Make the deferred a promise
1244
+ promise.promise( deferred );
1245
+
1246
+ // Call given func if any
1247
+ if ( func ) {
1248
+ func.call( deferred, deferred );
1249
+ }
1250
+
1251
+ // All done!
1252
+ return deferred;
1253
+ },
1254
+
1255
+ // Deferred helper
1256
+ when: function( subordinate /* , ..., subordinateN */ ) {
1257
+ var i = 0,
1258
+ resolveValues = core_slice.call( arguments ),
1259
+ length = resolveValues.length,
1260
+
1261
+ // the count of uncompleted subordinates
1262
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
1263
+
1264
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
1265
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
1266
+
1267
+ // Update function for both resolve and progress values
1268
+ updateFunc = function( i, contexts, values ) {
1269
+ return function( value ) {
1270
+ contexts[ i ] = this;
1271
+ values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
1272
+ if( values === progressValues ) {
1273
+ deferred.notifyWith( contexts, values );
1274
+ } else if ( !( --remaining ) ) {
1275
+ deferred.resolveWith( contexts, values );
1276
+ }
1277
+ };
1278
+ },
1279
+
1280
+ progressValues, progressContexts, resolveContexts;
1281
+
1282
+ // add listeners to Deferred subordinates; treat others as resolved
1283
+ if ( length > 1 ) {
1284
+ progressValues = new Array( length );
1285
+ progressContexts = new Array( length );
1286
+ resolveContexts = new Array( length );
1287
+ for ( ; i < length; i++ ) {
1288
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
1289
+ resolveValues[ i ].promise()
1290
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
1291
+ .fail( deferred.reject )
1292
+ .progress( updateFunc( i, progressContexts, progressValues ) );
1293
+ } else {
1294
+ --remaining;
1295
+ }
1296
+ }
1297
+ }
1298
+
1299
+ // if we're not waiting on anything, resolve the master
1300
+ if ( !remaining ) {
1301
+ deferred.resolveWith( resolveContexts, resolveValues );
1302
+ }
1303
+
1304
+ return deferred.promise();
1305
+ }
1306
+ });
1307
+ jQuery.support = (function() {
1308
+
1309
+ var support, all, a,
1310
+ input, select, fragment,
1311
+ opt, eventName, isSupported, i,
1312
+ div = document.createElement("div");
1313
+
1314
+ // Setup
1315
+ div.setAttribute( "className", "t" );
1316
+ div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
1317
+
1318
+ // Support tests won't run in some limited or non-browser environments
1319
+ all = div.getElementsByTagName("*");
1320
+ a = div.getElementsByTagName("a")[ 0 ];
1321
+ if ( !all || !a || !all.length ) {
1322
+ return {};
1323
+ }
1324
+
1325
+ // First batch of tests
1326
+ select = document.createElement("select");
1327
+ opt = select.appendChild( document.createElement("option") );
1328
+ input = div.getElementsByTagName("input")[ 0 ];
1329
+
1330
+ a.style.cssText = "top:1px;float:left;opacity:.5";
1331
+ support = {
1332
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
1333
+ getSetAttribute: div.className !== "t",
1334
+
1335
+ // IE strips leading whitespace when .innerHTML is used
1336
+ leadingWhitespace: div.firstChild.nodeType === 3,
1337
+
1338
+ // Make sure that tbody elements aren't automatically inserted
1339
+ // IE will insert them into empty tables
1340
+ tbody: !div.getElementsByTagName("tbody").length,
1341
+
1342
+ // Make sure that link elements get serialized correctly by innerHTML
1343
+ // This requires a wrapper element in IE
1344
+ htmlSerialize: !!div.getElementsByTagName("link").length,
1345
+
1346
+ // Get the style information from getAttribute
1347
+ // (IE uses .cssText instead)
1348
+ style: /top/.test( a.getAttribute("style") ),
1349
+
1350
+ // Make sure that URLs aren't manipulated
1351
+ // (IE normalizes it by default)
1352
+ hrefNormalized: a.getAttribute("href") === "/a",
1353
+
1354
+ // Make sure that element opacity exists
1355
+ // (IE uses filter instead)
1356
+ // Use a regex to work around a WebKit issue. See #5145
1357
+ opacity: /^0.5/.test( a.style.opacity ),
1358
+
1359
+ // Verify style float existence
1360
+ // (IE uses styleFloat instead of cssFloat)
1361
+ cssFloat: !!a.style.cssFloat,
1362
+
1363
+ // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
1364
+ checkOn: !!input.value,
1365
+
1366
+ // Make sure that a selected-by-default option has a working selected property.
1367
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
1368
+ optSelected: opt.selected,
1369
+
1370
+ // Tests for enctype support on a form (#6743)
1371
+ enctype: !!document.createElement("form").enctype,
1372
+
1373
+ // Makes sure cloning an html5 element does not cause problems
1374
+ // Where outerHTML is undefined, this still works
1375
+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
1376
+
1377
+ // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
1378
+ boxModel: document.compatMode === "CSS1Compat",
1379
+
1380
+ // Will be defined later
1381
+ deleteExpando: true,
1382
+ noCloneEvent: true,
1383
+ inlineBlockNeedsLayout: false,
1384
+ shrinkWrapBlocks: false,
1385
+ reliableMarginRight: true,
1386
+ boxSizingReliable: true,
1387
+ pixelPosition: false
1388
+ };
1389
+
1390
+ // Make sure checked status is properly cloned
1391
+ input.checked = true;
1392
+ support.noCloneChecked = input.cloneNode( true ).checked;
1393
+
1394
+ // Make sure that the options inside disabled selects aren't marked as disabled
1395
+ // (WebKit marks them as disabled)
1396
+ select.disabled = true;
1397
+ support.optDisabled = !opt.disabled;
1398
+
1399
+ // Support: IE<9
1400
+ try {
1401
+ delete div.test;
1402
+ } catch( e ) {
1403
+ support.deleteExpando = false;
1404
+ }
1405
+
1406
+ // Check if we can trust getAttribute("value")
1407
+ input = document.createElement("input");
1408
+ input.setAttribute( "value", "" );
1409
+ support.input = input.getAttribute( "value" ) === "";
1410
+
1411
+ // Check if an input maintains its value after becoming a radio
1412
+ input.value = "t";
1413
+ input.setAttribute( "type", "radio" );
1414
+ support.radioValue = input.value === "t";
1415
+
1416
+ // #11217 - WebKit loses check when the name is after the checked attribute
1417
+ input.setAttribute( "checked", "t" );
1418
+ input.setAttribute( "name", "t" );
1419
+
1420
+ fragment = document.createDocumentFragment();
1421
+ fragment.appendChild( input );
1422
+
1423
+ // Check if a disconnected checkbox will retain its checked
1424
+ // value of true after appended to the DOM (IE6/7)
1425
+ support.appendChecked = input.checked;
1426
+
1427
+ // WebKit doesn't clone checked state correctly in fragments
1428
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
1429
+
1430
+ // Support: IE<9
1431
+ // Opera does not clone events (and typeof div.attachEvent === undefined).
1432
+ // IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
1433
+ if ( div.attachEvent ) {
1434
+ div.attachEvent( "onclick", function() {
1435
+ support.noCloneEvent = false;
1436
+ });
1437
+
1438
+ div.cloneNode( true ).click();
1439
+ }
1440
+
1441
+ // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
1442
+ // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php
1443
+ for ( i in { submit: true, change: true, focusin: true }) {
1444
+ div.setAttribute( eventName = "on" + i, "t" );
1445
+
1446
+ support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
1447
+ }
1448
+
1449
+ div.style.backgroundClip = "content-box";
1450
+ div.cloneNode( true ).style.backgroundClip = "";
1451
+ support.clearCloneStyle = div.style.backgroundClip === "content-box";
1452
+
1453
+ // Run tests that need a body at doc ready
1454
+ jQuery(function() {
1455
+ var container, marginDiv, tds,
1456
+ divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
1457
+ body = document.getElementsByTagName("body")[0];
1458
+
1459
+ if ( !body ) {
1460
+ // Return for frameset docs that don't have a body
1461
+ return;
1462
+ }
1463
+
1464
+ container = document.createElement("div");
1465
+ container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
1466
+
1467
+ body.appendChild( container ).appendChild( div );
1468
+
1469
+ // Support: IE8
1470
+ // Check if table cells still have offsetWidth/Height when they are set
1471
+ // to display:none and there are still other visible table cells in a
1472
+ // table row; if so, offsetWidth/Height are not reliable for use when
1473
+ // determining if an element has been hidden directly using
1474
+ // display:none (it is still safe to use offsets if a parent element is
1475
+ // hidden; don safety goggles and see bug #4512 for more information).
1476
+ div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
1477
+ tds = div.getElementsByTagName("td");
1478
+ tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
1479
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
1480
+
1481
+ tds[ 0 ].style.display = "";
1482
+ tds[ 1 ].style.display = "none";
1483
+
1484
+ // Support: IE8
1485
+ // Check if empty table cells still have offsetWidth/Height
1486
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
1487
+
1488
+ // Check box-sizing and margin behavior
1489
+ div.innerHTML = "";
1490
+ div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
1491
+ support.boxSizing = ( div.offsetWidth === 4 );
1492
+ support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
1493
+
1494
+ // Use window.getComputedStyle because jsdom on node.js will break without it.
1495
+ if ( window.getComputedStyle ) {
1496
+ support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
1497
+ support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
1498
+
1499
+ // Check if div with explicit width and no margin-right incorrectly
1500
+ // gets computed margin-right based on width of container. (#3333)
1501
+ // Fails in WebKit before Feb 2011 nightlies
1502
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
1503
+ marginDiv = div.appendChild( document.createElement("div") );
1504
+ marginDiv.style.cssText = div.style.cssText = divReset;
1505
+ marginDiv.style.marginRight = marginDiv.style.width = "0";
1506
+ div.style.width = "1px";
1507
+
1508
+ support.reliableMarginRight =
1509
+ !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
1510
+ }
1511
+
1512
+ if ( typeof div.style.zoom !== core_strundefined ) {
1513
+ // Support: IE<8
1514
+ // Check if natively block-level elements act like inline-block
1515
+ // elements when setting their display to 'inline' and giving
1516
+ // them layout
1517
+ div.innerHTML = "";
1518
+ div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
1519
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
1520
+
1521
+ // Support: IE6
1522
+ // Check if elements with layout shrink-wrap their children
1523
+ div.style.display = "block";
1524
+ div.innerHTML = "<div></div>";
1525
+ div.firstChild.style.width = "5px";
1526
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
1527
+
1528
+ if ( support.inlineBlockNeedsLayout ) {
1529
+ // Prevent IE 6 from affecting layout for positioned elements #11048
1530
+ // Prevent IE from shrinking the body in IE 7 mode #12869
1531
+ // Support: IE<8
1532
+ body.style.zoom = 1;
1533
+ }
1534
+ }
1535
+
1536
+ body.removeChild( container );
1537
+
1538
+ // Null elements to avoid leaks in IE
1539
+ container = div = tds = marginDiv = null;
1540
+ });
1541
+
1542
+ // Null elements to avoid leaks in IE
1543
+ all = select = fragment = opt = a = input = null;
1544
+
1545
+ return support;
1546
+ })();
1547
+
1548
+ var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
1549
+ rmultiDash = /([A-Z])/g;
1550
+
1551
+ function internalData( elem, name, data, pvt /* Internal Use Only */ ){
1552
+ if ( !jQuery.acceptData( elem ) ) {
1553
+ return;
1554
+ }
1555
+
1556
+ var thisCache, ret,
1557
+ internalKey = jQuery.expando,
1558
+ getByName = typeof name === "string",
1559
+
1560
+ // We have to handle DOM nodes and JS objects differently because IE6-7
1561
+ // can't GC object references properly across the DOM-JS boundary
1562
+ isNode = elem.nodeType,
1563
+
1564
+ // Only DOM nodes need the global jQuery cache; JS object data is
1565
+ // attached directly to the object so GC can occur automatically
1566
+ cache = isNode ? jQuery.cache : elem,
1567
+
1568
+ // Only defining an ID for JS objects if its cache already exists allows
1569
+ // the code to shortcut on the same path as a DOM node with no cache
1570
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
1571
+
1572
+ // Avoid doing any more work than we need to when trying to get data on an
1573
+ // object that has no data at all
1574
+ if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
1575
+ return;
1576
+ }
1577
+
1578
+ if ( !id ) {
1579
+ // Only DOM nodes need a new unique ID for each element since their data
1580
+ // ends up in the global cache
1581
+ if ( isNode ) {
1582
+ elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;
1583
+ } else {
1584
+ id = internalKey;
1585
+ }
1586
+ }
1587
+
1588
+ if ( !cache[ id ] ) {
1589
+ cache[ id ] = {};
1590
+
1591
+ // Avoids exposing jQuery metadata on plain JS objects when the object
1592
+ // is serialized using JSON.stringify
1593
+ if ( !isNode ) {
1594
+ cache[ id ].toJSON = jQuery.noop;
1595
+ }
1596
+ }
1597
+
1598
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
1599
+ // shallow copied over onto the existing cache
1600
+ if ( typeof name === "object" || typeof name === "function" ) {
1601
+ if ( pvt ) {
1602
+ cache[ id ] = jQuery.extend( cache[ id ], name );
1603
+ } else {
1604
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
1605
+ }
1606
+ }
1607
+
1608
+ thisCache = cache[ id ];
1609
+
1610
+ // jQuery data() is stored in a separate object inside the object's internal data
1611
+ // cache in order to avoid key collisions between internal data and user-defined
1612
+ // data.
1613
+ if ( !pvt ) {
1614
+ if ( !thisCache.data ) {
1615
+ thisCache.data = {};
1616
+ }
1617
+
1618
+ thisCache = thisCache.data;
1619
+ }
1620
+
1621
+ if ( data !== undefined ) {
1622
+ thisCache[ jQuery.camelCase( name ) ] = data;
1623
+ }
1624
+
1625
+ // Check for both converted-to-camel and non-converted data property names
1626
+ // If a data property was specified
1627
+ if ( getByName ) {
1628
+
1629
+ // First Try to find as-is property data
1630
+ ret = thisCache[ name ];
1631
+
1632
+ // Test for null|undefined property data
1633
+ if ( ret == null ) {
1634
+
1635
+ // Try to find the camelCased property
1636
+ ret = thisCache[ jQuery.camelCase( name ) ];
1637
+ }
1638
+ } else {
1639
+ ret = thisCache;
1640
+ }
1641
+
1642
+ return ret;
1643
+ }
1644
+
1645
+ function internalRemoveData( elem, name, pvt ) {
1646
+ if ( !jQuery.acceptData( elem ) ) {
1647
+ return;
1648
+ }
1649
+
1650
+ var i, l, thisCache,
1651
+ isNode = elem.nodeType,
1652
+
1653
+ // See jQuery.data for more information
1654
+ cache = isNode ? jQuery.cache : elem,
1655
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
1656
+
1657
+ // If there is already no cache entry for this object, there is no
1658
+ // purpose in continuing
1659
+ if ( !cache[ id ] ) {
1660
+ return;
1661
+ }
1662
+
1663
+ if ( name ) {
1664
+
1665
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
1666
+
1667
+ if ( thisCache ) {
1668
+
1669
+ // Support array or space separated string names for data keys
1670
+ if ( !jQuery.isArray( name ) ) {
1671
+
1672
+ // try the string as a key before any manipulation
1673
+ if ( name in thisCache ) {
1674
+ name = [ name ];
1675
+ } else {
1676
+
1677
+ // split the camel cased version by spaces unless a key with the spaces exists
1678
+ name = jQuery.camelCase( name );
1679
+ if ( name in thisCache ) {
1680
+ name = [ name ];
1681
+ } else {
1682
+ name = name.split(" ");
1683
+ }
1684
+ }
1685
+ } else {
1686
+ // If "name" is an array of keys...
1687
+ // When data is initially created, via ("key", "val") signature,
1688
+ // keys will be converted to camelCase.
1689
+ // Since there is no way to tell _how_ a key was added, remove
1690
+ // both plain key and camelCase key. #12786
1691
+ // This will only penalize the array argument path.
1692
+ name = name.concat( jQuery.map( name, jQuery.camelCase ) );
1693
+ }
1694
+
1695
+ for ( i = 0, l = name.length; i < l; i++ ) {
1696
+ delete thisCache[ name[i] ];
1697
+ }
1698
+
1699
+ // If there is no data left in the cache, we want to continue
1700
+ // and let the cache object itself get destroyed
1701
+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
1702
+ return;
1703
+ }
1704
+ }
1705
+ }
1706
+
1707
+ // See jQuery.data for more information
1708
+ if ( !pvt ) {
1709
+ delete cache[ id ].data;
1710
+
1711
+ // Don't destroy the parent cache unless the internal data object
1712
+ // had been the only thing left in it
1713
+ if ( !isEmptyDataObject( cache[ id ] ) ) {
1714
+ return;
1715
+ }
1716
+ }
1717
+
1718
+ // Destroy the cache
1719
+ if ( isNode ) {
1720
+ jQuery.cleanData( [ elem ], true );
1721
+
1722
+ // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
1723
+ } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
1724
+ delete cache[ id ];
1725
+
1726
+ // When all else fails, null
1727
+ } else {
1728
+ cache[ id ] = null;
1729
+ }
1730
+ }
1731
+
1732
+ jQuery.extend({
1733
+ cache: {},
1734
+
1735
+ // Unique for each copy of jQuery on the page
1736
+ // Non-digits removed to match rinlinejQuery
1737
+ expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
1738
+
1739
+ // The following elements throw uncatchable exceptions if you
1740
+ // attempt to add expando properties to them.
1741
+ noData: {
1742
+ "embed": true,
1743
+ // Ban all objects except for Flash (which handle expandos)
1744
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
1745
+ "applet": true
1746
+ },
1747
+
1748
+ hasData: function( elem ) {
1749
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
1750
+ return !!elem && !isEmptyDataObject( elem );
1751
+ },
1752
+
1753
+ data: function( elem, name, data ) {
1754
+ return internalData( elem, name, data );
1755
+ },
1756
+
1757
+ removeData: function( elem, name ) {
1758
+ return internalRemoveData( elem, name );
1759
+ },
1760
+
1761
+ // For internal use only.
1762
+ _data: function( elem, name, data ) {
1763
+ return internalData( elem, name, data, true );
1764
+ },
1765
+
1766
+ _removeData: function( elem, name ) {
1767
+ return internalRemoveData( elem, name, true );
1768
+ },
1769
+
1770
+ // A method for determining if a DOM node can handle the data expando
1771
+ acceptData: function( elem ) {
1772
+ // Do not set data on non-element because it will not be cleared (#8335).
1773
+ if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
1774
+ return false;
1775
+ }
1776
+
1777
+ var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
1778
+
1779
+ // nodes accept data unless otherwise specified; rejection can be conditional
1780
+ return !noData || noData !== true && elem.getAttribute("classid") === noData;
1781
+ }
1782
+ });
1783
+
1784
+ jQuery.fn.extend({
1785
+ data: function( key, value ) {
1786
+ var attrs, name,
1787
+ elem = this[0],
1788
+ i = 0,
1789
+ data = null;
1790
+
1791
+ // Gets all values
1792
+ if ( key === undefined ) {
1793
+ if ( this.length ) {
1794
+ data = jQuery.data( elem );
1795
+
1796
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
1797
+ attrs = elem.attributes;
1798
+ for ( ; i < attrs.length; i++ ) {
1799
+ name = attrs[i].name;
1800
+
1801
+ if ( !name.indexOf( "data-" ) ) {
1802
+ name = jQuery.camelCase( name.slice(5) );
1803
+
1804
+ dataAttr( elem, name, data[ name ] );
1805
+ }
1806
+ }
1807
+ jQuery._data( elem, "parsedAttrs", true );
1808
+ }
1809
+ }
1810
+
1811
+ return data;
1812
+ }
1813
+
1814
+ // Sets multiple values
1815
+ if ( typeof key === "object" ) {
1816
+ return this.each(function() {
1817
+ jQuery.data( this, key );
1818
+ });
1819
+ }
1820
+
1821
+ return jQuery.access( this, function( value ) {
1822
+
1823
+ if ( value === undefined ) {
1824
+ // Try to fetch any internally stored data first
1825
+ return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
1826
+ }
1827
+
1828
+ this.each(function() {
1829
+ jQuery.data( this, key, value );
1830
+ });
1831
+ }, null, value, arguments.length > 1, null, true );
1832
+ },
1833
+
1834
+ removeData: function( key ) {
1835
+ return this.each(function() {
1836
+ jQuery.removeData( this, key );
1837
+ });
1838
+ }
1839
+ });
1840
+
1841
+ function dataAttr( elem, key, data ) {
1842
+ // If nothing was found internally, try to fetch any
1843
+ // data from the HTML5 data-* attribute
1844
+ if ( data === undefined && elem.nodeType === 1 ) {
1845
+
1846
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
1847
+
1848
+ data = elem.getAttribute( name );
1849
+
1850
+ if ( typeof data === "string" ) {
1851
+ try {
1852
+ data = data === "true" ? true :
1853
+ data === "false" ? false :
1854
+ data === "null" ? null :
1855
+ // Only convert to a number if it doesn't change the string
1856
+ +data + "" === data ? +data :
1857
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
1858
+ data;
1859
+ } catch( e ) {}
1860
+
1861
+ // Make sure we set the data so it isn't changed later
1862
+ jQuery.data( elem, key, data );
1863
+
1864
+ } else {
1865
+ data = undefined;
1866
+ }
1867
+ }
1868
+
1869
+ return data;
1870
+ }
1871
+
1872
+ // checks a cache object for emptiness
1873
+ function isEmptyDataObject( obj ) {
1874
+ var name;
1875
+ for ( name in obj ) {
1876
+
1877
+ // if the public data object is empty, the private is still empty
1878
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
1879
+ continue;
1880
+ }
1881
+ if ( name !== "toJSON" ) {
1882
+ return false;
1883
+ }
1884
+ }
1885
+
1886
+ return true;
1887
+ }
1888
+ jQuery.extend({
1889
+ queue: function( elem, type, data ) {
1890
+ var queue;
1891
+
1892
+ if ( elem ) {
1893
+ type = ( type || "fx" ) + "queue";
1894
+ queue = jQuery._data( elem, type );
1895
+
1896
+ // Speed up dequeue by getting out quickly if this is just a lookup
1897
+ if ( data ) {
1898
+ if ( !queue || jQuery.isArray(data) ) {
1899
+ queue = jQuery._data( elem, type, jQuery.makeArray(data) );
1900
+ } else {
1901
+ queue.push( data );
1902
+ }
1903
+ }
1904
+ return queue || [];
1905
+ }
1906
+ },
1907
+
1908
+ dequeue: function( elem, type ) {
1909
+ type = type || "fx";
1910
+
1911
+ var queue = jQuery.queue( elem, type ),
1912
+ startLength = queue.length,
1913
+ fn = queue.shift(),
1914
+ hooks = jQuery._queueHooks( elem, type ),
1915
+ next = function() {
1916
+ jQuery.dequeue( elem, type );
1917
+ };
1918
+
1919
+ // If the fx queue is dequeued, always remove the progress sentinel
1920
+ if ( fn === "inprogress" ) {
1921
+ fn = queue.shift();
1922
+ startLength--;
1923
+ }
1924
+
1925
+ hooks.cur = fn;
1926
+ if ( fn ) {
1927
+
1928
+ // Add a progress sentinel to prevent the fx queue from being
1929
+ // automatically dequeued
1930
+ if ( type === "fx" ) {
1931
+ queue.unshift( "inprogress" );
1932
+ }
1933
+
1934
+ // clear up the last queue stop function
1935
+ delete hooks.stop;
1936
+ fn.call( elem, next, hooks );
1937
+ }
1938
+
1939
+ if ( !startLength && hooks ) {
1940
+ hooks.empty.fire();
1941
+ }
1942
+ },
1943
+
1944
+ // not intended for public consumption - generates a queueHooks object, or returns the current one
1945
+ _queueHooks: function( elem, type ) {
1946
+ var key = type + "queueHooks";
1947
+ return jQuery._data( elem, key ) || jQuery._data( elem, key, {
1948
+ empty: jQuery.Callbacks("once memory").add(function() {
1949
+ jQuery._removeData( elem, type + "queue" );
1950
+ jQuery._removeData( elem, key );
1951
+ })
1952
+ });
1953
+ }
1954
+ });
1955
+
1956
+ jQuery.fn.extend({
1957
+ queue: function( type, data ) {
1958
+ var setter = 2;
1959
+
1960
+ if ( typeof type !== "string" ) {
1961
+ data = type;
1962
+ type = "fx";
1963
+ setter--;
1964
+ }
1965
+
1966
+ if ( arguments.length < setter ) {
1967
+ return jQuery.queue( this[0], type );
1968
+ }
1969
+
1970
+ return data === undefined ?
1971
+ this :
1972
+ this.each(function() {
1973
+ var queue = jQuery.queue( this, type, data );
1974
+
1975
+ // ensure a hooks for this queue
1976
+ jQuery._queueHooks( this, type );
1977
+
1978
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
1979
+ jQuery.dequeue( this, type );
1980
+ }
1981
+ });
1982
+ },
1983
+ dequeue: function( type ) {
1984
+ return this.each(function() {
1985
+ jQuery.dequeue( this, type );
1986
+ });
1987
+ },
1988
+ // Based off of the plugin by Clint Helfers, with permission.
1989
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
1990
+ delay: function( time, type ) {
1991
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
1992
+ type = type || "fx";
1993
+
1994
+ return this.queue( type, function( next, hooks ) {
1995
+ var timeout = setTimeout( next, time );
1996
+ hooks.stop = function() {
1997
+ clearTimeout( timeout );
1998
+ };
1999
+ });
2000
+ },
2001
+ clearQueue: function( type ) {
2002
+ return this.queue( type || "fx", [] );
2003
+ },
2004
+ // Get a promise resolved when queues of a certain type
2005
+ // are emptied (fx is the type by default)
2006
+ promise: function( type, obj ) {
2007
+ var tmp,
2008
+ count = 1,
2009
+ defer = jQuery.Deferred(),
2010
+ elements = this,
2011
+ i = this.length,
2012
+ resolve = function() {
2013
+ if ( !( --count ) ) {
2014
+ defer.resolveWith( elements, [ elements ] );
2015
+ }
2016
+ };
2017
+
2018
+ if ( typeof type !== "string" ) {
2019
+ obj = type;
2020
+ type = undefined;
2021
+ }
2022
+ type = type || "fx";
2023
+
2024
+ while( i-- ) {
2025
+ tmp = jQuery._data( elements[ i ], type + "queueHooks" );
2026
+ if ( tmp && tmp.empty ) {
2027
+ count++;
2028
+ tmp.empty.add( resolve );
2029
+ }
2030
+ }
2031
+ resolve();
2032
+ return defer.promise( obj );
2033
+ }
2034
+ });
2035
+ var nodeHook, boolHook,
2036
+ rclass = /[\t\r\n]/g,
2037
+ rreturn = /\r/g,
2038
+ rfocusable = /^(?:input|select|textarea|button|object)$/i,
2039
+ rclickable = /^(?:a|area)$/i,
2040
+ rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,
2041
+ ruseDefault = /^(?:checked|selected)$/i,
2042
+ getSetAttribute = jQuery.support.getSetAttribute,
2043
+ getSetInput = jQuery.support.input;
2044
+
2045
+ jQuery.fn.extend({
2046
+ attr: function( name, value ) {
2047
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
2048
+ },
2049
+
2050
+ removeAttr: function( name ) {
2051
+ return this.each(function() {
2052
+ jQuery.removeAttr( this, name );
2053
+ });
2054
+ },
2055
+
2056
+ prop: function( name, value ) {
2057
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
2058
+ },
2059
+
2060
+ removeProp: function( name ) {
2061
+ name = jQuery.propFix[ name ] || name;
2062
+ return this.each(function() {
2063
+ // try/catch handles cases where IE balks (such as removing a property on window)
2064
+ try {
2065
+ this[ name ] = undefined;
2066
+ delete this[ name ];
2067
+ } catch( e ) {}
2068
+ });
2069
+ },
2070
+
2071
+ addClass: function( value ) {
2072
+ var classes, elem, cur, clazz, j,
2073
+ i = 0,
2074
+ len = this.length,
2075
+ proceed = typeof value === "string" && value;
2076
+
2077
+ if ( jQuery.isFunction( value ) ) {
2078
+ return this.each(function( j ) {
2079
+ jQuery( this ).addClass( value.call( this, j, this.className ) );
2080
+ });
2081
+ }
2082
+
2083
+ if ( proceed ) {
2084
+ // The disjunction here is for better compressibility (see removeClass)
2085
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
2086
+
2087
+ for ( ; i < len; i++ ) {
2088
+ elem = this[ i ];
2089
+ cur = elem.nodeType === 1 && ( elem.className ?
2090
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
2091
+ " "
2092
+ );
2093
+
2094
+ if ( cur ) {
2095
+ j = 0;
2096
+ while ( (clazz = classes[j++]) ) {
2097
+ if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
2098
+ cur += clazz + " ";
2099
+ }
2100
+ }
2101
+ elem.className = jQuery.trim( cur );
2102
+
2103
+ }
2104
+ }
2105
+ }
2106
+
2107
+ return this;
2108
+ },
2109
+
2110
+ removeClass: function( value ) {
2111
+ var classes, elem, cur, clazz, j,
2112
+ i = 0,
2113
+ len = this.length,
2114
+ proceed = arguments.length === 0 || typeof value === "string" && value;
2115
+
2116
+ if ( jQuery.isFunction( value ) ) {
2117
+ return this.each(function( j ) {
2118
+ jQuery( this ).removeClass( value.call( this, j, this.className ) );
2119
+ });
2120
+ }
2121
+ if ( proceed ) {
2122
+ classes = ( value || "" ).match( core_rnotwhite ) || [];
2123
+
2124
+ for ( ; i < len; i++ ) {
2125
+ elem = this[ i ];
2126
+ // This expression is here for better compressibility (see addClass)
2127
+ cur = elem.nodeType === 1 && ( elem.className ?
2128
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
2129
+ ""
2130
+ );
2131
+
2132
+ if ( cur ) {
2133
+ j = 0;
2134
+ while ( (clazz = classes[j++]) ) {
2135
+ // Remove *all* instances
2136
+ while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
2137
+ cur = cur.replace( " " + clazz + " ", " " );
2138
+ }
2139
+ }
2140
+ elem.className = value ? jQuery.trim( cur ) : "";
2141
+ }
2142
+ }
2143
+ }
2144
+
2145
+ return this;
2146
+ },
2147
+
2148
+ toggleClass: function( value, stateVal ) {
2149
+ var type = typeof value,
2150
+ isBool = typeof stateVal === "boolean";
2151
+
2152
+ if ( jQuery.isFunction( value ) ) {
2153
+ return this.each(function( i ) {
2154
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
2155
+ });
2156
+ }
2157
+
2158
+ return this.each(function() {
2159
+ if ( type === "string" ) {
2160
+ // toggle individual class names
2161
+ var className,
2162
+ i = 0,
2163
+ self = jQuery( this ),
2164
+ state = stateVal,
2165
+ classNames = value.match( core_rnotwhite ) || [];
2166
+
2167
+ while ( (className = classNames[ i++ ]) ) {
2168
+ // check each className given, space separated list
2169
+ state = isBool ? state : !self.hasClass( className );
2170
+ self[ state ? "addClass" : "removeClass" ]( className );
2171
+ }
2172
+
2173
+ // Toggle whole class name
2174
+ } else if ( type === core_strundefined || type === "boolean" ) {
2175
+ if ( this.className ) {
2176
+ // store className if set
2177
+ jQuery._data( this, "__className__", this.className );
2178
+ }
2179
+
2180
+ // If the element has a class name or if we're passed "false",
2181
+ // then remove the whole classname (if there was one, the above saved it).
2182
+ // Otherwise bring back whatever was previously saved (if anything),
2183
+ // falling back to the empty string if nothing was stored.
2184
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
2185
+ }
2186
+ });
2187
+ },
2188
+
2189
+ hasClass: function( selector ) {
2190
+ var className = " " + selector + " ",
2191
+ i = 0,
2192
+ l = this.length;
2193
+ for ( ; i < l; i++ ) {
2194
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
2195
+ return true;
2196
+ }
2197
+ }
2198
+
2199
+ return false;
2200
+ },
2201
+
2202
+ val: function( value ) {
2203
+ var ret, hooks, isFunction,
2204
+ elem = this[0];
2205
+
2206
+ if ( !arguments.length ) {
2207
+ if ( elem ) {
2208
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
2209
+
2210
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
2211
+ return ret;
2212
+ }
2213
+
2214
+ ret = elem.value;
2215
+
2216
+ return typeof ret === "string" ?
2217
+ // handle most common string cases
2218
+ ret.replace(rreturn, "") :
2219
+ // handle cases where value is null/undef or number
2220
+ ret == null ? "" : ret;
2221
+ }
2222
+
2223
+ return;
2224
+ }
2225
+
2226
+ isFunction = jQuery.isFunction( value );
2227
+
2228
+ return this.each(function( i ) {
2229
+ var val,
2230
+ self = jQuery(this);
2231
+
2232
+ if ( this.nodeType !== 1 ) {
2233
+ return;
2234
+ }
2235
+
2236
+ if ( isFunction ) {
2237
+ val = value.call( this, i, self.val() );
2238
+ } else {
2239
+ val = value;
2240
+ }
2241
+
2242
+ // Treat null/undefined as ""; convert numbers to string
2243
+ if ( val == null ) {
2244
+ val = "";
2245
+ } else if ( typeof val === "number" ) {
2246
+ val += "";
2247
+ } else if ( jQuery.isArray( val ) ) {
2248
+ val = jQuery.map(val, function ( value ) {
2249
+ return value == null ? "" : value + "";
2250
+ });
2251
+ }
2252
+
2253
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
2254
+
2255
+ // If set returns undefined, fall back to normal setting
2256
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
2257
+ this.value = val;
2258
+ }
2259
+ });
2260
+ }
2261
+ });
2262
+
2263
+ jQuery.extend({
2264
+ valHooks: {
2265
+ option: {
2266
+ get: function( elem ) {
2267
+ // attributes.value is undefined in Blackberry 4.7 but
2268
+ // uses .value. See #6932
2269
+ var val = elem.attributes.value;
2270
+ return !val || val.specified ? elem.value : elem.text;
2271
+ }
2272
+ },
2273
+ select: {
2274
+ get: function( elem ) {
2275
+ var value, option,
2276
+ options = elem.options,
2277
+ index = elem.selectedIndex,
2278
+ one = elem.type === "select-one" || index < 0,
2279
+ values = one ? null : [],
2280
+ max = one ? index + 1 : options.length,
2281
+ i = index < 0 ?
2282
+ max :
2283
+ one ? index : 0;
2284
+
2285
+ // Loop through all the selected options
2286
+ for ( ; i < max; i++ ) {
2287
+ option = options[ i ];
2288
+
2289
+ // oldIE doesn't update selected after form reset (#2551)
2290
+ if ( ( option.selected || i === index ) &&
2291
+ // Don't return options that are disabled or in a disabled optgroup
2292
+ ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
2293
+ ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
2294
+
2295
+ // Get the specific value for the option
2296
+ value = jQuery( option ).val();
2297
+
2298
+ // We don't need an array for one selects
2299
+ if ( one ) {
2300
+ return value;
2301
+ }
2302
+
2303
+ // Multi-Selects return an array
2304
+ values.push( value );
2305
+ }
2306
+ }
2307
+
2308
+ return values;
2309
+ },
2310
+
2311
+ set: function( elem, value ) {
2312
+ var values = jQuery.makeArray( value );
2313
+
2314
+ jQuery(elem).find("option").each(function() {
2315
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
2316
+ });
2317
+
2318
+ if ( !values.length ) {
2319
+ elem.selectedIndex = -1;
2320
+ }
2321
+ return values;
2322
+ }
2323
+ }
2324
+ },
2325
+
2326
+ attr: function( elem, name, value ) {
2327
+ var hooks, notxml, ret,
2328
+ nType = elem.nodeType;
2329
+
2330
+ // don't get/set attributes on text, comment and attribute nodes
2331
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
2332
+ return;
2333
+ }
2334
+
2335
+ // Fallback to prop when attributes are not supported
2336
+ if ( typeof elem.getAttribute === core_strundefined ) {
2337
+ return jQuery.prop( elem, name, value );
2338
+ }
2339
+
2340
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
2341
+
2342
+ // All attributes are lowercase
2343
+ // Grab necessary hook if one is defined
2344
+ if ( notxml ) {
2345
+ name = name.toLowerCase();
2346
+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
2347
+ }
2348
+
2349
+ if ( value !== undefined ) {
2350
+
2351
+ if ( value === null ) {
2352
+ jQuery.removeAttr( elem, name );
2353
+
2354
+ } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
2355
+ return ret;
2356
+
2357
+ } else {
2358
+ elem.setAttribute( name, value + "" );
2359
+ return value;
2360
+ }
2361
+
2362
+ } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
2363
+ return ret;
2364
+
2365
+ } else {
2366
+
2367
+ // In IE9+, Flash objects don't have .getAttribute (#12945)
2368
+ // Support: IE9+
2369
+ if ( typeof elem.getAttribute !== core_strundefined ) {
2370
+ ret = elem.getAttribute( name );
2371
+ }
2372
+
2373
+ // Non-existent attributes return null, we normalize to undefined
2374
+ return ret == null ?
2375
+ undefined :
2376
+ ret;
2377
+ }
2378
+ },
2379
+
2380
+ removeAttr: function( elem, value ) {
2381
+ var name, propName,
2382
+ i = 0,
2383
+ attrNames = value && value.match( core_rnotwhite );
2384
+
2385
+ if ( attrNames && elem.nodeType === 1 ) {
2386
+ while ( (name = attrNames[i++]) ) {
2387
+ propName = jQuery.propFix[ name ] || name;
2388
+
2389
+ // Boolean attributes get special treatment (#10870)
2390
+ if ( rboolean.test( name ) ) {
2391
+ // Set corresponding property to false for boolean attributes
2392
+ // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8
2393
+ if ( !getSetAttribute && ruseDefault.test( name ) ) {
2394
+ elem[ jQuery.camelCase( "default-" + name ) ] =
2395
+ elem[ propName ] = false;
2396
+ } else {
2397
+ elem[ propName ] = false;
2398
+ }
2399
+
2400
+ // See #9699 for explanation of this approach (setting first, then removal)
2401
+ } else {
2402
+ jQuery.attr( elem, name, "" );
2403
+ }
2404
+
2405
+ elem.removeAttribute( getSetAttribute ? name : propName );
2406
+ }
2407
+ }
2408
+ },
2409
+
2410
+ attrHooks: {
2411
+ type: {
2412
+ set: function( elem, value ) {
2413
+ if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
2414
+ // Setting the type on a radio button after the value resets the value in IE6-9
2415
+ // Reset value to default in case type is set after value during creation
2416
+ var val = elem.value;
2417
+ elem.setAttribute( "type", value );
2418
+ if ( val ) {
2419
+ elem.value = val;
2420
+ }
2421
+ return value;
2422
+ }
2423
+ }
2424
+ }
2425
+ },
2426
+
2427
+ propFix: {
2428
+ tabindex: "tabIndex",
2429
+ readonly: "readOnly",
2430
+ "for": "htmlFor",
2431
+ "class": "className",
2432
+ maxlength: "maxLength",
2433
+ cellspacing: "cellSpacing",
2434
+ cellpadding: "cellPadding",
2435
+ rowspan: "rowSpan",
2436
+ colspan: "colSpan",
2437
+ usemap: "useMap",
2438
+ frameborder: "frameBorder",
2439
+ contenteditable: "contentEditable"
2440
+ },
2441
+
2442
+ prop: function( elem, name, value ) {
2443
+ var ret, hooks, notxml,
2444
+ nType = elem.nodeType;
2445
+
2446
+ // don't get/set properties on text, comment and attribute nodes
2447
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
2448
+ return;
2449
+ }
2450
+
2451
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
2452
+
2453
+ if ( notxml ) {
2454
+ // Fix name and attach hooks
2455
+ name = jQuery.propFix[ name ] || name;
2456
+ hooks = jQuery.propHooks[ name ];
2457
+ }
2458
+
2459
+ if ( value !== undefined ) {
2460
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
2461
+ return ret;
2462
+
2463
+ } else {
2464
+ return ( elem[ name ] = value );
2465
+ }
2466
+
2467
+ } else {
2468
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
2469
+ return ret;
2470
+
2471
+ } else {
2472
+ return elem[ name ];
2473
+ }
2474
+ }
2475
+ },
2476
+
2477
+ propHooks: {
2478
+ tabIndex: {
2479
+ get: function( elem ) {
2480
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
2481
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
2482
+ var attributeNode = elem.getAttributeNode("tabindex");
2483
+
2484
+ return attributeNode && attributeNode.specified ?
2485
+ parseInt( attributeNode.value, 10 ) :
2486
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
2487
+ 0 :
2488
+ undefined;
2489
+ }
2490
+ }
2491
+ }
2492
+ });
2493
+
2494
+ // Hook for boolean attributes
2495
+ boolHook = {
2496
+ get: function( elem, name ) {
2497
+ var
2498
+ // Use .prop to determine if this attribute is understood as boolean
2499
+ prop = jQuery.prop( elem, name ),
2500
+
2501
+ // Fetch it accordingly
2502
+ attr = typeof prop === "boolean" && elem.getAttribute( name ),
2503
+ detail = typeof prop === "boolean" ?
2504
+
2505
+ getSetInput && getSetAttribute ?
2506
+ attr != null :
2507
+ // oldIE fabricates an empty string for missing boolean attributes
2508
+ // and conflates checked/selected into attroperties
2509
+ ruseDefault.test( name ) ?
2510
+ elem[ jQuery.camelCase( "default-" + name ) ] :
2511
+ !!attr :
2512
+
2513
+ // fetch an attribute node for properties not recognized as boolean
2514
+ elem.getAttributeNode( name );
2515
+
2516
+ return detail && detail.value !== false ?
2517
+ name.toLowerCase() :
2518
+ undefined;
2519
+ },
2520
+ set: function( elem, value, name ) {
2521
+ if ( value === false ) {
2522
+ // Remove boolean attributes when set to false
2523
+ jQuery.removeAttr( elem, name );
2524
+ } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
2525
+ // IE<8 needs the *property* name
2526
+ elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
2527
+
2528
+ // Use defaultChecked and defaultSelected for oldIE
2529
+ } else {
2530
+ elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
2531
+ }
2532
+
2533
+ return name;
2534
+ }
2535
+ };
2536
+
2537
+ // fix oldIE value attroperty
2538
+ if ( !getSetInput || !getSetAttribute ) {
2539
+ jQuery.attrHooks.value = {
2540
+ get: function( elem, name ) {
2541
+ var ret = elem.getAttributeNode( name );
2542
+ return jQuery.nodeName( elem, "input" ) ?
2543
+
2544
+ // Ignore the value *property* by using defaultValue
2545
+ elem.defaultValue :
2546
+
2547
+ ret && ret.specified ? ret.value : undefined;
2548
+ },
2549
+ set: function( elem, value, name ) {
2550
+ if ( jQuery.nodeName( elem, "input" ) ) {
2551
+ // Does not return so that setAttribute is also used
2552
+ elem.defaultValue = value;
2553
+ } else {
2554
+ // Use nodeHook if defined (#1954); otherwise setAttribute is fine
2555
+ return nodeHook && nodeHook.set( elem, value, name );
2556
+ }
2557
+ }
2558
+ };
2559
+ }
2560
+
2561
+ // IE6/7 do not support getting/setting some attributes with get/setAttribute
2562
+ if ( !getSetAttribute ) {
2563
+
2564
+ // Use this for any attribute in IE6/7
2565
+ // This fixes almost every IE6/7 issue
2566
+ nodeHook = jQuery.valHooks.button = {
2567
+ get: function( elem, name ) {
2568
+ var ret = elem.getAttributeNode( name );
2569
+ return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ?
2570
+ ret.value :
2571
+ undefined;
2572
+ },
2573
+ set: function( elem, value, name ) {
2574
+ // Set the existing or create a new attribute node
2575
+ var ret = elem.getAttributeNode( name );
2576
+ if ( !ret ) {
2577
+ elem.setAttributeNode(
2578
+ (ret = elem.ownerDocument.createAttribute( name ))
2579
+ );
2580
+ }
2581
+
2582
+ ret.value = value += "";
2583
+
2584
+ // Break association with cloned elements by also using setAttribute (#9646)
2585
+ return name === "value" || value === elem.getAttribute( name ) ?
2586
+ value :
2587
+ undefined;
2588
+ }
2589
+ };
2590
+
2591
+ // Set contenteditable to false on removals(#10429)
2592
+ // Setting to empty string throws an error as an invalid value
2593
+ jQuery.attrHooks.contenteditable = {
2594
+ get: nodeHook.get,
2595
+ set: function( elem, value, name ) {
2596
+ nodeHook.set( elem, value === "" ? false : value, name );
2597
+ }
2598
+ };
2599
+
2600
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
2601
+ // This is for removals
2602
+ jQuery.each([ "width", "height" ], function( i, name ) {
2603
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
2604
+ set: function( elem, value ) {
2605
+ if ( value === "" ) {
2606
+ elem.setAttribute( name, "auto" );
2607
+ return value;
2608
+ }
2609
+ }
2610
+ });
2611
+ });
2612
+ }
2613
+
2614
+
2615
+ // Some attributes require a special call on IE
2616
+ // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
2617
+ if ( !jQuery.support.hrefNormalized ) {
2618
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
2619
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
2620
+ get: function( elem ) {
2621
+ var ret = elem.getAttribute( name, 2 );
2622
+ return ret == null ? undefined : ret;
2623
+ }
2624
+ });
2625
+ });
2626
+
2627
+ // href/src property should get the full normalized URL (#10299/#12915)
2628
+ jQuery.each([ "href", "src" ], function( i, name ) {
2629
+ jQuery.propHooks[ name ] = {
2630
+ get: function( elem ) {
2631
+ return elem.getAttribute( name, 4 );
2632
+ }
2633
+ };
2634
+ });
2635
+ }
2636
+
2637
+ if ( !jQuery.support.style ) {
2638
+ jQuery.attrHooks.style = {
2639
+ get: function( elem ) {
2640
+ // Return undefined in the case of empty string
2641
+ // Note: IE uppercases css property names, but if we were to .toLowerCase()
2642
+ // .cssText, that would destroy case senstitivity in URL's, like in "background"
2643
+ return elem.style.cssText || undefined;
2644
+ },
2645
+ set: function( elem, value ) {
2646
+ return ( elem.style.cssText = value + "" );
2647
+ }
2648
+ };
2649
+ }
2650
+
2651
+ // Safari mis-reports the default selected property of an option
2652
+ // Accessing the parent's selectedIndex property fixes it
2653
+ if ( !jQuery.support.optSelected ) {
2654
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
2655
+ get: function( elem ) {
2656
+ var parent = elem.parentNode;
2657
+
2658
+ if ( parent ) {
2659
+ parent.selectedIndex;
2660
+
2661
+ // Make sure that it also works with optgroups, see #5701
2662
+ if ( parent.parentNode ) {
2663
+ parent.parentNode.selectedIndex;
2664
+ }
2665
+ }
2666
+ return null;
2667
+ }
2668
+ });
2669
+ }
2670
+
2671
+ // IE6/7 call enctype encoding
2672
+ if ( !jQuery.support.enctype ) {
2673
+ jQuery.propFix.enctype = "encoding";
2674
+ }
2675
+
2676
+ // Radios and checkboxes getter/setter
2677
+ if ( !jQuery.support.checkOn ) {
2678
+ jQuery.each([ "radio", "checkbox" ], function() {
2679
+ jQuery.valHooks[ this ] = {
2680
+ get: function( elem ) {
2681
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
2682
+ return elem.getAttribute("value") === null ? "on" : elem.value;
2683
+ }
2684
+ };
2685
+ });
2686
+ }
2687
+ jQuery.each([ "radio", "checkbox" ], function() {
2688
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
2689
+ set: function( elem, value ) {
2690
+ if ( jQuery.isArray( value ) ) {
2691
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
2692
+ }
2693
+ }
2694
+ });
2695
+ });
2696
+ var rformElems = /^(?:input|select|textarea)$/i,
2697
+ rkeyEvent = /^key/,
2698
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
2699
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
2700
+ rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
2701
+
2702
+ function returnTrue() {
2703
+ return true;
2704
+ }
2705
+
2706
+ function returnFalse() {
2707
+ return false;
2708
+ }
2709
+
2710
+ /*
2711
+ * Helper functions for managing events -- not part of the public interface.
2712
+ * Props to Dean Edwards' addEvent library for many of the ideas.
2713
+ */
2714
+ jQuery.event = {
2715
+
2716
+ global: {},
2717
+
2718
+ add: function( elem, types, handler, data, selector ) {
2719
+ var tmp, events, t, handleObjIn,
2720
+ special, eventHandle, handleObj,
2721
+ handlers, type, namespaces, origType,
2722
+ elemData = jQuery._data( elem );
2723
+
2724
+ // Don't attach events to noData or text/comment nodes (but allow plain objects)
2725
+ if ( !elemData ) {
2726
+ return;
2727
+ }
2728
+
2729
+ // Caller can pass in an object of custom data in lieu of the handler
2730
+ if ( handler.handler ) {
2731
+ handleObjIn = handler;
2732
+ handler = handleObjIn.handler;
2733
+ selector = handleObjIn.selector;
2734
+ }
2735
+
2736
+ // Make sure that the handler has a unique ID, used to find/remove it later
2737
+ if ( !handler.guid ) {
2738
+ handler.guid = jQuery.guid++;
2739
+ }
2740
+
2741
+ // Init the element's event structure and main handler, if this is the first
2742
+ if ( !(events = elemData.events) ) {
2743
+ events = elemData.events = {};
2744
+ }
2745
+ if ( !(eventHandle = elemData.handle) ) {
2746
+ eventHandle = elemData.handle = function( e ) {
2747
+ // Discard the second event of a jQuery.event.trigger() and
2748
+ // when an event is called after a page has unloaded
2749
+ return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
2750
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
2751
+ undefined;
2752
+ };
2753
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
2754
+ eventHandle.elem = elem;
2755
+ }
2756
+
2757
+ // Handle multiple events separated by a space
2758
+ // jQuery(...).bind("mouseover mouseout", fn);
2759
+ types = ( types || "" ).match( core_rnotwhite ) || [""];
2760
+ t = types.length;
2761
+ while ( t-- ) {
2762
+ tmp = rtypenamespace.exec( types[t] ) || [];
2763
+ type = origType = tmp[1];
2764
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
2765
+
2766
+ // If event changes its type, use the special event handlers for the changed type
2767
+ special = jQuery.event.special[ type ] || {};
2768
+
2769
+ // If selector defined, determine special event api type, otherwise given type
2770
+ type = ( selector ? special.delegateType : special.bindType ) || type;
2771
+
2772
+ // Update special based on newly reset type
2773
+ special = jQuery.event.special[ type ] || {};
2774
+
2775
+ // handleObj is passed to all event handlers
2776
+ handleObj = jQuery.extend({
2777
+ type: type,
2778
+ origType: origType,
2779
+ data: data,
2780
+ handler: handler,
2781
+ guid: handler.guid,
2782
+ selector: selector,
2783
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
2784
+ namespace: namespaces.join(".")
2785
+ }, handleObjIn );
2786
+
2787
+ // Init the event handler queue if we're the first
2788
+ if ( !(handlers = events[ type ]) ) {
2789
+ handlers = events[ type ] = [];
2790
+ handlers.delegateCount = 0;
2791
+
2792
+ // Only use addEventListener/attachEvent if the special events handler returns false
2793
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
2794
+ // Bind the global event handler to the element
2795
+ if ( elem.addEventListener ) {
2796
+ elem.addEventListener( type, eventHandle, false );
2797
+
2798
+ } else if ( elem.attachEvent ) {
2799
+ elem.attachEvent( "on" + type, eventHandle );
2800
+ }
2801
+ }
2802
+ }
2803
+
2804
+ if ( special.add ) {
2805
+ special.add.call( elem, handleObj );
2806
+
2807
+ if ( !handleObj.handler.guid ) {
2808
+ handleObj.handler.guid = handler.guid;
2809
+ }
2810
+ }
2811
+
2812
+ // Add to the element's handler list, delegates in front
2813
+ if ( selector ) {
2814
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
2815
+ } else {
2816
+ handlers.push( handleObj );
2817
+ }
2818
+
2819
+ // Keep track of which events have ever been used, for event optimization
2820
+ jQuery.event.global[ type ] = true;
2821
+ }
2822
+
2823
+ // Nullify elem to prevent memory leaks in IE
2824
+ elem = null;
2825
+ },
2826
+
2827
+ // Detach an event or set of events from an element
2828
+ remove: function( elem, types, handler, selector, mappedTypes ) {
2829
+ var j, handleObj, tmp,
2830
+ origCount, t, events,
2831
+ special, handlers, type,
2832
+ namespaces, origType,
2833
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem );
2834
+
2835
+ if ( !elemData || !(events = elemData.events) ) {
2836
+ return;
2837
+ }
2838
+
2839
+ // Once for each type.namespace in types; type may be omitted
2840
+ types = ( types || "" ).match( core_rnotwhite ) || [""];
2841
+ t = types.length;
2842
+ while ( t-- ) {
2843
+ tmp = rtypenamespace.exec( types[t] ) || [];
2844
+ type = origType = tmp[1];
2845
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
2846
+
2847
+ // Unbind all events (on this namespace, if provided) for the element
2848
+ if ( !type ) {
2849
+ for ( type in events ) {
2850
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
2851
+ }
2852
+ continue;
2853
+ }
2854
+
2855
+ special = jQuery.event.special[ type ] || {};
2856
+ type = ( selector ? special.delegateType : special.bindType ) || type;
2857
+ handlers = events[ type ] || [];
2858
+ tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
2859
+
2860
+ // Remove matching events
2861
+ origCount = j = handlers.length;
2862
+ while ( j-- ) {
2863
+ handleObj = handlers[ j ];
2864
+
2865
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
2866
+ ( !handler || handler.guid === handleObj.guid ) &&
2867
+ ( !tmp || tmp.test( handleObj.namespace ) ) &&
2868
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
2869
+ handlers.splice( j, 1 );
2870
+
2871
+ if ( handleObj.selector ) {
2872
+ handlers.delegateCount--;
2873
+ }
2874
+ if ( special.remove ) {
2875
+ special.remove.call( elem, handleObj );
2876
+ }
2877
+ }
2878
+ }
2879
+
2880
+ // Remove generic event handler if we removed something and no more handlers exist
2881
+ // (avoids potential for endless recursion during removal of special event handlers)
2882
+ if ( origCount && !handlers.length ) {
2883
+ if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
2884
+ jQuery.removeEvent( elem, type, elemData.handle );
2885
+ }
2886
+
2887
+ delete events[ type ];
2888
+ }
2889
+ }
2890
+
2891
+ // Remove the expando if it's no longer used
2892
+ if ( jQuery.isEmptyObject( events ) ) {
2893
+ delete elemData.handle;
2894
+
2895
+ // removeData also checks for emptiness and clears the expando if empty
2896
+ // so use it instead of delete
2897
+ jQuery._removeData( elem, "events" );
2898
+ }
2899
+ },
2900
+
2901
+ trigger: function( event, data, elem, onlyHandlers ) {
2902
+ var handle, ontype, cur,
2903
+ bubbleType, special, tmp, i,
2904
+ eventPath = [ elem || document ],
2905
+ type = core_hasOwn.call( event, "type" ) ? event.type : event,
2906
+ namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
2907
+
2908
+ cur = tmp = elem = elem || document;
2909
+
2910
+ // Don't do events on text and comment nodes
2911
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
2912
+ return;
2913
+ }
2914
+
2915
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
2916
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
2917
+ return;
2918
+ }
2919
+
2920
+ if ( type.indexOf(".") >= 0 ) {
2921
+ // Namespaced trigger; create a regexp to match event type in handle()
2922
+ namespaces = type.split(".");
2923
+ type = namespaces.shift();
2924
+ namespaces.sort();
2925
+ }
2926
+ ontype = type.indexOf(":") < 0 && "on" + type;
2927
+
2928
+ // Caller can pass in a jQuery.Event object, Object, or just an event type string
2929
+ event = event[ jQuery.expando ] ?
2930
+ event :
2931
+ new jQuery.Event( type, typeof event === "object" && event );
2932
+
2933
+ event.isTrigger = true;
2934
+ event.namespace = namespaces.join(".");
2935
+ event.namespace_re = event.namespace ?
2936
+ new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
2937
+ null;
2938
+
2939
+ // Clean up the event in case it is being reused
2940
+ event.result = undefined;
2941
+ if ( !event.target ) {
2942
+ event.target = elem;
2943
+ }
2944
+
2945
+ // Clone any incoming data and prepend the event, creating the handler arg list
2946
+ data = data == null ?
2947
+ [ event ] :
2948
+ jQuery.makeArray( data, [ event ] );
2949
+
2950
+ // Allow special events to draw outside the lines
2951
+ special = jQuery.event.special[ type ] || {};
2952
+ if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
2953
+ return;
2954
+ }
2955
+
2956
+ // Determine event propagation path in advance, per W3C events spec (#9951)
2957
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
2958
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
2959
+
2960
+ bubbleType = special.delegateType || type;
2961
+ if ( !rfocusMorph.test( bubbleType + type ) ) {
2962
+ cur = cur.parentNode;
2963
+ }
2964
+ for ( ; cur; cur = cur.parentNode ) {
2965
+ eventPath.push( cur );
2966
+ tmp = cur;
2967
+ }
2968
+
2969
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
2970
+ if ( tmp === (elem.ownerDocument || document) ) {
2971
+ eventPath.push( tmp.defaultView || tmp.parentWindow || window );
2972
+ }
2973
+ }
2974
+
2975
+ // Fire handlers on the event path
2976
+ i = 0;
2977
+ while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
2978
+
2979
+ event.type = i > 1 ?
2980
+ bubbleType :
2981
+ special.bindType || type;
2982
+
2983
+ // jQuery handler
2984
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
2985
+ if ( handle ) {
2986
+ handle.apply( cur, data );
2987
+ }
2988
+
2989
+ // Native handler
2990
+ handle = ontype && cur[ ontype ];
2991
+ if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
2992
+ event.preventDefault();
2993
+ }
2994
+ }
2995
+ event.type = type;
2996
+
2997
+ // If nobody prevented the default action, do it now
2998
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
2999
+
3000
+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
3001
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
3002
+
3003
+ // Call a native DOM method on the target with the same name name as the event.
3004
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
3005
+ // Don't do default actions on window, that's where global variables be (#6170)
3006
+ if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
3007
+
3008
+ // Don't re-trigger an onFOO event when we call its FOO() method
3009
+ tmp = elem[ ontype ];
3010
+
3011
+ if ( tmp ) {
3012
+ elem[ ontype ] = null;
3013
+ }
3014
+
3015
+ // Prevent re-triggering of the same event, since we already bubbled it above
3016
+ jQuery.event.triggered = type;
3017
+ try {
3018
+ elem[ type ]();
3019
+ } catch ( e ) {
3020
+ // IE<9 dies on focus/blur to hidden element (#1486,#12518)
3021
+ // only reproducible on winXP IE8 native, not IE9 in IE8 mode
3022
+ }
3023
+ jQuery.event.triggered = undefined;
3024
+
3025
+ if ( tmp ) {
3026
+ elem[ ontype ] = tmp;
3027
+ }
3028
+ }
3029
+ }
3030
+ }
3031
+
3032
+ return event.result;
3033
+ },
3034
+
3035
+ dispatch: function( event ) {
3036
+
3037
+ // Make a writable jQuery.Event from the native event object
3038
+ event = jQuery.event.fix( event );
3039
+
3040
+ var i, ret, handleObj, matched, j,
3041
+ handlerQueue = [],
3042
+ args = core_slice.call( arguments ),
3043
+ handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
3044
+ special = jQuery.event.special[ event.type ] || {};
3045
+
3046
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
3047
+ args[0] = event;
3048
+ event.delegateTarget = this;
3049
+
3050
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
3051
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
3052
+ return;
3053
+ }
3054
+
3055
+ // Determine handlers
3056
+ handlerQueue = jQuery.event.handlers.call( this, event, handlers );
3057
+
3058
+ // Run delegates first; they may want to stop propagation beneath us
3059
+ i = 0;
3060
+ while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
3061
+ event.currentTarget = matched.elem;
3062
+
3063
+ j = 0;
3064
+ while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
3065
+
3066
+ // Triggered event must either 1) have no namespace, or
3067
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
3068
+ if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
3069
+
3070
+ event.handleObj = handleObj;
3071
+ event.data = handleObj.data;
3072
+
3073
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
3074
+ .apply( matched.elem, args );
3075
+
3076
+ if ( ret !== undefined ) {
3077
+ if ( (event.result = ret) === false ) {
3078
+ event.preventDefault();
3079
+ event.stopPropagation();
3080
+ }
3081
+ }
3082
+ }
3083
+ }
3084
+ }
3085
+
3086
+ // Call the postDispatch hook for the mapped type
3087
+ if ( special.postDispatch ) {
3088
+ special.postDispatch.call( this, event );
3089
+ }
3090
+
3091
+ return event.result;
3092
+ },
3093
+
3094
+ handlers: function( event, handlers ) {
3095
+ var sel, handleObj, matches, i,
3096
+ handlerQueue = [],
3097
+ delegateCount = handlers.delegateCount,
3098
+ cur = event.target;
3099
+
3100
+ // Find delegate handlers
3101
+ // Black-hole SVG <use> instance trees (#13180)
3102
+ // Avoid non-left-click bubbling in Firefox (#3861)
3103
+ if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
3104
+
3105
+ for ( ; cur != this; cur = cur.parentNode || this ) {
3106
+
3107
+ // Don't check non-elements (#13208)
3108
+ // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
3109
+ if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
3110
+ matches = [];
3111
+ for ( i = 0; i < delegateCount; i++ ) {
3112
+ handleObj = handlers[ i ];
3113
+
3114
+ // Don't conflict with Object.prototype properties (#13203)
3115
+ sel = handleObj.selector + " ";
3116
+
3117
+ if ( matches[ sel ] === undefined ) {
3118
+ matches[ sel ] = handleObj.needsContext ?
3119
+ jQuery( sel, this ).index( cur ) >= 0 :
3120
+ jQuery.find( sel, this, null, [ cur ] ).length;
3121
+ }
3122
+ if ( matches[ sel ] ) {
3123
+ matches.push( handleObj );
3124
+ }
3125
+ }
3126
+ if ( matches.length ) {
3127
+ handlerQueue.push({ elem: cur, handlers: matches });
3128
+ }
3129
+ }
3130
+ }
3131
+ }
3132
+
3133
+ // Add the remaining (directly-bound) handlers
3134
+ if ( delegateCount < handlers.length ) {
3135
+ handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
3136
+ }
3137
+
3138
+ return handlerQueue;
3139
+ },
3140
+
3141
+ fix: function( event ) {
3142
+ if ( event[ jQuery.expando ] ) {
3143
+ return event;
3144
+ }
3145
+
3146
+ // Create a writable copy of the event object and normalize some properties
3147
+ var i, prop, copy,
3148
+ type = event.type,
3149
+ originalEvent = event,
3150
+ fixHook = this.fixHooks[ type ];
3151
+
3152
+ if ( !fixHook ) {
3153
+ this.fixHooks[ type ] = fixHook =
3154
+ rmouseEvent.test( type ) ? this.mouseHooks :
3155
+ rkeyEvent.test( type ) ? this.keyHooks :
3156
+ {};
3157
+ }
3158
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
3159
+
3160
+ event = new jQuery.Event( originalEvent );
3161
+
3162
+ i = copy.length;
3163
+ while ( i-- ) {
3164
+ prop = copy[ i ];
3165
+ event[ prop ] = originalEvent[ prop ];
3166
+ }
3167
+
3168
+ // Support: IE<9
3169
+ // Fix target property (#1925)
3170
+ if ( !event.target ) {
3171
+ event.target = originalEvent.srcElement || document;
3172
+ }
3173
+
3174
+ // Support: Chrome 23+, Safari?
3175
+ // Target should not be a text node (#504, #13143)
3176
+ if ( event.target.nodeType === 3 ) {
3177
+ event.target = event.target.parentNode;
3178
+ }
3179
+
3180
+ // Support: IE<9
3181
+ // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
3182
+ event.metaKey = !!event.metaKey;
3183
+
3184
+ return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
3185
+ },
3186
+
3187
+ // Includes some event props shared by KeyEvent and MouseEvent
3188
+ props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
3189
+
3190
+ fixHooks: {},
3191
+
3192
+ keyHooks: {
3193
+ props: "char charCode key keyCode".split(" "),
3194
+ filter: function( event, original ) {
3195
+
3196
+ // Add which for key events
3197
+ if ( event.which == null ) {
3198
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
3199
+ }
3200
+
3201
+ return event;
3202
+ }
3203
+ },
3204
+
3205
+ mouseHooks: {
3206
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
3207
+ filter: function( event, original ) {
3208
+ var body, eventDoc, doc,
3209
+ button = original.button,
3210
+ fromElement = original.fromElement;
3211
+
3212
+ // Calculate pageX/Y if missing and clientX/Y available
3213
+ if ( event.pageX == null && original.clientX != null ) {
3214
+ eventDoc = event.target.ownerDocument || document;
3215
+ doc = eventDoc.documentElement;
3216
+ body = eventDoc.body;
3217
+
3218
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
3219
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
3220
+ }
3221
+
3222
+ // Add relatedTarget, if necessary
3223
+ if ( !event.relatedTarget && fromElement ) {
3224
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
3225
+ }
3226
+
3227
+ // Add which for click: 1 === left; 2 === middle; 3 === right
3228
+ // Note: button is not normalized, so don't use it
3229
+ if ( !event.which && button !== undefined ) {
3230
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
3231
+ }
3232
+
3233
+ return event;
3234
+ }
3235
+ },
3236
+
3237
+ special: {
3238
+ load: {
3239
+ // Prevent triggered image.load events from bubbling to window.load
3240
+ noBubble: true
3241
+ },
3242
+ click: {
3243
+ // For checkbox, fire native event so checked state will be right
3244
+ trigger: function() {
3245
+ if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
3246
+ this.click();
3247
+ return false;
3248
+ }
3249
+ }
3250
+ },
3251
+ focus: {
3252
+ // Fire native event if possible so blur/focus sequence is correct
3253
+ trigger: function() {
3254
+ if ( this !== document.activeElement && this.focus ) {
3255
+ try {
3256
+ this.focus();
3257
+ return false;
3258
+ } catch ( e ) {
3259
+ // Support: IE<9
3260
+ // If we error on focus to hidden element (#1486, #12518),
3261
+ // let .trigger() run the handlers
3262
+ }
3263
+ }
3264
+ },
3265
+ delegateType: "focusin"
3266
+ },
3267
+ blur: {
3268
+ trigger: function() {
3269
+ if ( this === document.activeElement && this.blur ) {
3270
+ this.blur();
3271
+ return false;
3272
+ }
3273
+ },
3274
+ delegateType: "focusout"
3275
+ },
3276
+
3277
+ beforeunload: {
3278
+ postDispatch: function( event ) {
3279
+
3280
+ // Even when returnValue equals to undefined Firefox will still show alert
3281
+ if ( event.result !== undefined ) {
3282
+ event.originalEvent.returnValue = event.result;
3283
+ }
3284
+ }
3285
+ }
3286
+ },
3287
+
3288
+ simulate: function( type, elem, event, bubble ) {
3289
+ // Piggyback on a donor event to simulate a different one.
3290
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
3291
+ // simulated event prevents default then we do the same on the donor.
3292
+ var e = jQuery.extend(
3293
+ new jQuery.Event(),
3294
+ event,
3295
+ { type: type,
3296
+ isSimulated: true,
3297
+ originalEvent: {}
3298
+ }
3299
+ );
3300
+ if ( bubble ) {
3301
+ jQuery.event.trigger( e, null, elem );
3302
+ } else {
3303
+ jQuery.event.dispatch.call( elem, e );
3304
+ }
3305
+ if ( e.isDefaultPrevented() ) {
3306
+ event.preventDefault();
3307
+ }
3308
+ }
3309
+ };
3310
+
3311
+ jQuery.removeEvent = document.removeEventListener ?
3312
+ function( elem, type, handle ) {
3313
+ if ( elem.removeEventListener ) {
3314
+ elem.removeEventListener( type, handle, false );
3315
+ }
3316
+ } :
3317
+ function( elem, type, handle ) {
3318
+ var name = "on" + type;
3319
+
3320
+ if ( elem.detachEvent ) {
3321
+
3322
+ // #8545, #7054, preventing memory leaks for custom events in IE6-8
3323
+ // detachEvent needed property on element, by name of that event, to properly expose it to GC
3324
+ if ( typeof elem[ name ] === core_strundefined ) {
3325
+ elem[ name ] = null;
3326
+ }
3327
+
3328
+ elem.detachEvent( name, handle );
3329
+ }
3330
+ };
3331
+
3332
+ jQuery.Event = function( src, props ) {
3333
+ // Allow instantiation without the 'new' keyword
3334
+ if ( !(this instanceof jQuery.Event) ) {
3335
+ return new jQuery.Event( src, props );
3336
+ }
3337
+
3338
+ // Event object
3339
+ if ( src && src.type ) {
3340
+ this.originalEvent = src;
3341
+ this.type = src.type;
3342
+
3343
+ // Events bubbling up the document may have been marked as prevented
3344
+ // by a handler lower down the tree; reflect the correct value.
3345
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
3346
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
3347
+
3348
+ // Event type
3349
+ } else {
3350
+ this.type = src;
3351
+ }
3352
+
3353
+ // Put explicitly provided properties onto the event object
3354
+ if ( props ) {
3355
+ jQuery.extend( this, props );
3356
+ }
3357
+
3358
+ // Create a timestamp if incoming event doesn't have one
3359
+ this.timeStamp = src && src.timeStamp || jQuery.now();
3360
+
3361
+ // Mark it as fixed
3362
+ this[ jQuery.expando ] = true;
3363
+ };
3364
+
3365
+ // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
3366
+ // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
3367
+ jQuery.Event.prototype = {
3368
+ isDefaultPrevented: returnFalse,
3369
+ isPropagationStopped: returnFalse,
3370
+ isImmediatePropagationStopped: returnFalse,
3371
+
3372
+ preventDefault: function() {
3373
+ var e = this.originalEvent;
3374
+
3375
+ this.isDefaultPrevented = returnTrue;
3376
+ if ( !e ) {
3377
+ return;
3378
+ }
3379
+
3380
+ // If preventDefault exists, run it on the original event
3381
+ if ( e.preventDefault ) {
3382
+ e.preventDefault();
3383
+
3384
+ // Support: IE
3385
+ // Otherwise set the returnValue property of the original event to false
3386
+ } else {
3387
+ e.returnValue = false;
3388
+ }
3389
+ },
3390
+ stopPropagation: function() {
3391
+ var e = this.originalEvent;
3392
+
3393
+ this.isPropagationStopped = returnTrue;
3394
+ if ( !e ) {
3395
+ return;
3396
+ }
3397
+ // If stopPropagation exists, run it on the original event
3398
+ if ( e.stopPropagation ) {
3399
+ e.stopPropagation();
3400
+ }
3401
+
3402
+ // Support: IE
3403
+ // Set the cancelBubble property of the original event to true
3404
+ e.cancelBubble = true;
3405
+ },
3406
+ stopImmediatePropagation: function() {
3407
+ this.isImmediatePropagationStopped = returnTrue;
3408
+ this.stopPropagation();
3409
+ }
3410
+ };
3411
+
3412
+ // Create mouseenter/leave events using mouseover/out and event-time checks
3413
+ jQuery.each({
3414
+ mouseenter: "mouseover",
3415
+ mouseleave: "mouseout"
3416
+ }, function( orig, fix ) {
3417
+ jQuery.event.special[ orig ] = {
3418
+ delegateType: fix,
3419
+ bindType: fix,
3420
+
3421
+ handle: function( event ) {
3422
+ var ret,
3423
+ target = this,
3424
+ related = event.relatedTarget,
3425
+ handleObj = event.handleObj;
3426
+
3427
+ // For mousenter/leave call the handler if related is outside the target.
3428
+ // NB: No relatedTarget if the mouse left/entered the browser window
3429
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
3430
+ event.type = handleObj.origType;
3431
+ ret = handleObj.handler.apply( this, arguments );
3432
+ event.type = fix;
3433
+ }
3434
+ return ret;
3435
+ }
3436
+ };
3437
+ });
3438
+
3439
+ // IE submit delegation
3440
+ if ( !jQuery.support.submitBubbles ) {
3441
+
3442
+ jQuery.event.special.submit = {
3443
+ setup: function() {
3444
+ // Only need this for delegated form submit events
3445
+ if ( jQuery.nodeName( this, "form" ) ) {
3446
+ return false;
3447
+ }
3448
+
3449
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
3450
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
3451
+ // Node name check avoids a VML-related crash in IE (#9807)
3452
+ var elem = e.target,
3453
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
3454
+ if ( form && !jQuery._data( form, "submitBubbles" ) ) {
3455
+ jQuery.event.add( form, "submit._submit", function( event ) {
3456
+ event._submit_bubble = true;
3457
+ });
3458
+ jQuery._data( form, "submitBubbles", true );
3459
+ }
3460
+ });
3461
+ // return undefined since we don't need an event listener
3462
+ },
3463
+
3464
+ postDispatch: function( event ) {
3465
+ // If form was submitted by the user, bubble the event up the tree
3466
+ if ( event._submit_bubble ) {
3467
+ delete event._submit_bubble;
3468
+ if ( this.parentNode && !event.isTrigger ) {
3469
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
3470
+ }
3471
+ }
3472
+ },
3473
+
3474
+ teardown: function() {
3475
+ // Only need this for delegated form submit events
3476
+ if ( jQuery.nodeName( this, "form" ) ) {
3477
+ return false;
3478
+ }
3479
+
3480
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
3481
+ jQuery.event.remove( this, "._submit" );
3482
+ }
3483
+ };
3484
+ }
3485
+
3486
+ // IE change delegation and checkbox/radio fix
3487
+ if ( !jQuery.support.changeBubbles ) {
3488
+
3489
+ jQuery.event.special.change = {
3490
+
3491
+ setup: function() {
3492
+
3493
+ if ( rformElems.test( this.nodeName ) ) {
3494
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
3495
+ // after a propertychange. Eat the blur-change in special.change.handle.
3496
+ // This still fires onchange a second time for check/radio after blur.
3497
+ if ( this.type === "checkbox" || this.type === "radio" ) {
3498
+ jQuery.event.add( this, "propertychange._change", function( event ) {
3499
+ if ( event.originalEvent.propertyName === "checked" ) {
3500
+ this._just_changed = true;
3501
+ }
3502
+ });
3503
+ jQuery.event.add( this, "click._change", function( event ) {
3504
+ if ( this._just_changed && !event.isTrigger ) {
3505
+ this._just_changed = false;
3506
+ }
3507
+ // Allow triggered, simulated change events (#11500)
3508
+ jQuery.event.simulate( "change", this, event, true );
3509
+ });
3510
+ }
3511
+ return false;
3512
+ }
3513
+ // Delegated event; lazy-add a change handler on descendant inputs
3514
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
3515
+ var elem = e.target;
3516
+
3517
+ if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
3518
+ jQuery.event.add( elem, "change._change", function( event ) {
3519
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
3520
+ jQuery.event.simulate( "change", this.parentNode, event, true );
3521
+ }
3522
+ });
3523
+ jQuery._data( elem, "changeBubbles", true );
3524
+ }
3525
+ });
3526
+ },
3527
+
3528
+ handle: function( event ) {
3529
+ var elem = event.target;
3530
+
3531
+ // Swallow native change events from checkbox/radio, we already triggered them above
3532
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
3533
+ return event.handleObj.handler.apply( this, arguments );
3534
+ }
3535
+ },
3536
+
3537
+ teardown: function() {
3538
+ jQuery.event.remove( this, "._change" );
3539
+
3540
+ return !rformElems.test( this.nodeName );
3541
+ }
3542
+ };
3543
+ }
3544
+
3545
+ // Create "bubbling" focus and blur events
3546
+ if ( !jQuery.support.focusinBubbles ) {
3547
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
3548
+
3549
+ // Attach a single capturing handler while someone wants focusin/focusout
3550
+ var attaches = 0,
3551
+ handler = function( event ) {
3552
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
3553
+ };
3554
+
3555
+ jQuery.event.special[ fix ] = {
3556
+ setup: function() {
3557
+ if ( attaches++ === 0 ) {
3558
+ document.addEventListener( orig, handler, true );
3559
+ }
3560
+ },
3561
+ teardown: function() {
3562
+ if ( --attaches === 0 ) {
3563
+ document.removeEventListener( orig, handler, true );
3564
+ }
3565
+ }
3566
+ };
3567
+ });
3568
+ }
3569
+
3570
+ jQuery.fn.extend({
3571
+
3572
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
3573
+ var type, origFn;
3574
+
3575
+ // Types can be a map of types/handlers
3576
+ if ( typeof types === "object" ) {
3577
+ // ( types-Object, selector, data )
3578
+ if ( typeof selector !== "string" ) {
3579
+ // ( types-Object, data )
3580
+ data = data || selector;
3581
+ selector = undefined;
3582
+ }
3583
+ for ( type in types ) {
3584
+ this.on( type, selector, data, types[ type ], one );
3585
+ }
3586
+ return this;
3587
+ }
3588
+
3589
+ if ( data == null && fn == null ) {
3590
+ // ( types, fn )
3591
+ fn = selector;
3592
+ data = selector = undefined;
3593
+ } else if ( fn == null ) {
3594
+ if ( typeof selector === "string" ) {
3595
+ // ( types, selector, fn )
3596
+ fn = data;
3597
+ data = undefined;
3598
+ } else {
3599
+ // ( types, data, fn )
3600
+ fn = data;
3601
+ data = selector;
3602
+ selector = undefined;
3603
+ }
3604
+ }
3605
+ if ( fn === false ) {
3606
+ fn = returnFalse;
3607
+ } else if ( !fn ) {
3608
+ return this;
3609
+ }
3610
+
3611
+ if ( one === 1 ) {
3612
+ origFn = fn;
3613
+ fn = function( event ) {
3614
+ // Can use an empty set, since event contains the info
3615
+ jQuery().off( event );
3616
+ return origFn.apply( this, arguments );
3617
+ };
3618
+ // Use same guid so caller can remove using origFn
3619
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
3620
+ }
3621
+ return this.each( function() {
3622
+ jQuery.event.add( this, types, fn, data, selector );
3623
+ });
3624
+ },
3625
+ one: function( types, selector, data, fn ) {
3626
+ return this.on( types, selector, data, fn, 1 );
3627
+ },
3628
+ off: function( types, selector, fn ) {
3629
+ var handleObj, type;
3630
+ if ( types && types.preventDefault && types.handleObj ) {
3631
+ // ( event ) dispatched jQuery.Event
3632
+ handleObj = types.handleObj;
3633
+ jQuery( types.delegateTarget ).off(
3634
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
3635
+ handleObj.selector,
3636
+ handleObj.handler
3637
+ );
3638
+ return this;
3639
+ }
3640
+ if ( typeof types === "object" ) {
3641
+ // ( types-object [, selector] )
3642
+ for ( type in types ) {
3643
+ this.off( type, selector, types[ type ] );
3644
+ }
3645
+ return this;
3646
+ }
3647
+ if ( selector === false || typeof selector === "function" ) {
3648
+ // ( types [, fn] )
3649
+ fn = selector;
3650
+ selector = undefined;
3651
+ }
3652
+ if ( fn === false ) {
3653
+ fn = returnFalse;
3654
+ }
3655
+ return this.each(function() {
3656
+ jQuery.event.remove( this, types, fn, selector );
3657
+ });
3658
+ },
3659
+
3660
+ bind: function( types, data, fn ) {
3661
+ return this.on( types, null, data, fn );
3662
+ },
3663
+ unbind: function( types, fn ) {
3664
+ return this.off( types, null, fn );
3665
+ },
3666
+
3667
+ delegate: function( selector, types, data, fn ) {
3668
+ return this.on( types, selector, data, fn );
3669
+ },
3670
+ undelegate: function( selector, types, fn ) {
3671
+ // ( namespace ) or ( selector, types [, fn] )
3672
+ return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
3673
+ },
3674
+
3675
+ trigger: function( type, data ) {
3676
+ return this.each(function() {
3677
+ jQuery.event.trigger( type, data, this );
3678
+ });
3679
+ },
3680
+ triggerHandler: function( type, data ) {
3681
+ var elem = this[0];
3682
+ if ( elem ) {
3683
+ return jQuery.event.trigger( type, data, elem, true );
3684
+ }
3685
+ }
3686
+ });
3687
+ /*!
3688
+ * Sizzle CSS Selector Engine
3689
+ * Copyright 2012 jQuery Foundation and other contributors
3690
+ * Released under the MIT license
3691
+ * http://sizzlejs.com/
3692
+ */
3693
+ (function( window, undefined ) {
3694
+
3695
+ var i,
3696
+ cachedruns,
3697
+ Expr,
3698
+ getText,
3699
+ isXML,
3700
+ compile,
3701
+ hasDuplicate,
3702
+ outermostContext,
3703
+
3704
+ // Local document vars
3705
+ setDocument,
3706
+ document,
3707
+ docElem,
3708
+ documentIsXML,
3709
+ rbuggyQSA,
3710
+ rbuggyMatches,
3711
+ matches,
3712
+ contains,
3713
+ sortOrder,
3714
+
3715
+ // Instance-specific data
3716
+ expando = "sizzle" + -(new Date()),
3717
+ preferredDoc = window.document,
3718
+ support = {},
3719
+ dirruns = 0,
3720
+ done = 0,
3721
+ classCache = createCache(),
3722
+ tokenCache = createCache(),
3723
+ compilerCache = createCache(),
3724
+
3725
+ // General-purpose constants
3726
+ strundefined = typeof undefined,
3727
+ MAX_NEGATIVE = 1 << 31,
3728
+
3729
+ // Array methods
3730
+ arr = [],
3731
+ pop = arr.pop,
3732
+ push = arr.push,
3733
+ slice = arr.slice,
3734
+ // Use a stripped-down indexOf if we can't use a native one
3735
+ indexOf = arr.indexOf || function( elem ) {
3736
+ var i = 0,
3737
+ len = this.length;
3738
+ for ( ; i < len; i++ ) {
3739
+ if ( this[i] === elem ) {
3740
+ return i;
3741
+ }
3742
+ }
3743
+ return -1;
3744
+ },
3745
+
3746
+
3747
+ // Regular expressions
3748
+
3749
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
3750
+ whitespace = "[\\x20\\t\\r\\n\\f]",
3751
+ // http://www.w3.org/TR/css3-syntax/#characters
3752
+ characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
3753
+
3754
+ // Loosely modeled on CSS identifier characters
3755
+ // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
3756
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
3757
+ identifier = characterEncoding.replace( "w", "w#" ),
3758
+
3759
+ // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
3760
+ operators = "([*^$|!~]?=)",
3761
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
3762
+ "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
3763
+
3764
+ // Prefer arguments quoted,
3765
+ // then not containing pseudos/brackets,
3766
+ // then attribute selectors/non-parenthetical expressions,
3767
+ // then anything else
3768
+ // These preferences are here to reduce the number of selectors
3769
+ // needing tokenize in the PSEUDO preFilter
3770
+ pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
3771
+
3772
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
3773
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
3774
+
3775
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
3776
+ rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
3777
+ rpseudo = new RegExp( pseudos ),
3778
+ ridentifier = new RegExp( "^" + identifier + "$" ),
3779
+
3780
+ matchExpr = {
3781
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
3782
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
3783
+ "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
3784
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
3785
+ "ATTR": new RegExp( "^" + attributes ),
3786
+ "PSEUDO": new RegExp( "^" + pseudos ),
3787
+ "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
3788
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
3789
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
3790
+ // For use in libraries implementing .is()
3791
+ // We use this for POS matching in `select`
3792
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
3793
+ whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
3794
+ },
3795
+
3796
+ rsibling = /[\x20\t\r\n\f]*[+~]/,
3797
+
3798
+ rnative = /^[^{]+\{\s*\[native code/,
3799
+
3800
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
3801
+ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
3802
+
3803
+ rinputs = /^(?:input|select|textarea|button)$/i,
3804
+ rheader = /^h\d$/i,
3805
+
3806
+ rescape = /'|\\/g,
3807
+ rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
3808
+
3809
+ // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
3810
+ runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
3811
+ funescape = function( _, escaped ) {
3812
+ var high = "0x" + escaped - 0x10000;
3813
+ // NaN means non-codepoint
3814
+ return high !== high ?
3815
+ escaped :
3816
+ // BMP codepoint
3817
+ high < 0 ?
3818
+ String.fromCharCode( high + 0x10000 ) :
3819
+ // Supplemental Plane codepoint (surrogate pair)
3820
+ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
3821
+ };
3822
+
3823
+ // Use a stripped-down slice if we can't use a native one
3824
+ try {
3825
+ slice.call( preferredDoc.documentElement.childNodes, 0 )[0].nodeType;
3826
+ } catch ( e ) {
3827
+ slice = function( i ) {
3828
+ var elem,
3829
+ results = [];
3830
+ while ( (elem = this[i++]) ) {
3831
+ results.push( elem );
3832
+ }
3833
+ return results;
3834
+ };
3835
+ }
3836
+
3837
+ /**
3838
+ * For feature detection
3839
+ * @param {Function} fn The function to test for native support
3840
+ */
3841
+ function isNative( fn ) {
3842
+ return rnative.test( fn + "" );
3843
+ }
3844
+
3845
+ /**
3846
+ * Create key-value caches of limited size
3847
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
3848
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
3849
+ * deleting the oldest entry
3850
+ */
3851
+ function createCache() {
3852
+ var cache,
3853
+ keys = [];
3854
+
3855
+ return (cache = function( key, value ) {
3856
+ // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
3857
+ if ( keys.push( key += " " ) > Expr.cacheLength ) {
3858
+ // Only keep the most recent entries
3859
+ delete cache[ keys.shift() ];
3860
+ }
3861
+ return (cache[ key ] = value);
3862
+ });
3863
+ }
3864
+
3865
+ /**
3866
+ * Mark a function for special use by Sizzle
3867
+ * @param {Function} fn The function to mark
3868
+ */
3869
+ function markFunction( fn ) {
3870
+ fn[ expando ] = true;
3871
+ return fn;
3872
+ }
3873
+
3874
+ /**
3875
+ * Support testing using an element
3876
+ * @param {Function} fn Passed the created div and expects a boolean result
3877
+ */
3878
+ function assert( fn ) {
3879
+ var div = document.createElement("div");
3880
+
3881
+ try {
3882
+ return fn( div );
3883
+ } catch (e) {
3884
+ return false;
3885
+ } finally {
3886
+ // release memory in IE
3887
+ div = null;
3888
+ }
3889
+ }
3890
+
3891
+ function Sizzle( selector, context, results, seed ) {
3892
+ var match, elem, m, nodeType,
3893
+ // QSA vars
3894
+ i, groups, old, nid, newContext, newSelector;
3895
+
3896
+ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
3897
+ setDocument( context );
3898
+ }
3899
+
3900
+ context = context || document;
3901
+ results = results || [];
3902
+
3903
+ if ( !selector || typeof selector !== "string" ) {
3904
+ return results;
3905
+ }
3906
+
3907
+ if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
3908
+ return [];
3909
+ }
3910
+
3911
+ if ( !documentIsXML && !seed ) {
3912
+
3913
+ // Shortcuts
3914
+ if ( (match = rquickExpr.exec( selector )) ) {
3915
+ // Speed-up: Sizzle("#ID")
3916
+ if ( (m = match[1]) ) {
3917
+ if ( nodeType === 9 ) {
3918
+ elem = context.getElementById( m );
3919
+ // Check parentNode to catch when Blackberry 4.6 returns
3920
+ // nodes that are no longer in the document #6963
3921
+ if ( elem && elem.parentNode ) {
3922
+ // Handle the case where IE, Opera, and Webkit return items
3923
+ // by name instead of ID
3924
+ if ( elem.id === m ) {
3925
+ results.push( elem );
3926
+ return results;
3927
+ }
3928
+ } else {
3929
+ return results;
3930
+ }
3931
+ } else {
3932
+ // Context is not a document
3933
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
3934
+ contains( context, elem ) && elem.id === m ) {
3935
+ results.push( elem );
3936
+ return results;
3937
+ }
3938
+ }
3939
+
3940
+ // Speed-up: Sizzle("TAG")
3941
+ } else if ( match[2] ) {
3942
+ push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
3943
+ return results;
3944
+
3945
+ // Speed-up: Sizzle(".CLASS")
3946
+ } else if ( (m = match[3]) && support.getByClassName && context.getElementsByClassName ) {
3947
+ push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
3948
+ return results;
3949
+ }
3950
+ }
3951
+
3952
+ // QSA path
3953
+ if ( support.qsa && !rbuggyQSA.test(selector) ) {
3954
+ old = true;
3955
+ nid = expando;
3956
+ newContext = context;
3957
+ newSelector = nodeType === 9 && selector;
3958
+
3959
+ // qSA works strangely on Element-rooted queries
3960
+ // We can work around this by specifying an extra ID on the root
3961
+ // and working up from there (Thanks to Andrew Dupont for the technique)
3962
+ // IE 8 doesn't work on object elements
3963
+ if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
3964
+ groups = tokenize( selector );
3965
+
3966
+ if ( (old = context.getAttribute("id")) ) {
3967
+ nid = old.replace( rescape, "\\$&" );
3968
+ } else {
3969
+ context.setAttribute( "id", nid );
3970
+ }
3971
+ nid = "[id='" + nid + "'] ";
3972
+
3973
+ i = groups.length;
3974
+ while ( i-- ) {
3975
+ groups[i] = nid + toSelector( groups[i] );
3976
+ }
3977
+ newContext = rsibling.test( selector ) && context.parentNode || context;
3978
+ newSelector = groups.join(",");
3979
+ }
3980
+
3981
+ if ( newSelector ) {
3982
+ try {
3983
+ push.apply( results, slice.call( newContext.querySelectorAll(
3984
+ newSelector
3985
+ ), 0 ) );
3986
+ return results;
3987
+ } catch(qsaError) {
3988
+ } finally {
3989
+ if ( !old ) {
3990
+ context.removeAttribute("id");
3991
+ }
3992
+ }
3993
+ }
3994
+ }
3995
+ }
3996
+
3997
+ // All others
3998
+ return select( selector.replace( rtrim, "$1" ), context, results, seed );
3999
+ }
4000
+
4001
+ /**
4002
+ * Detect xml
4003
+ * @param {Element|Object} elem An element or a document
4004
+ */
4005
+ isXML = Sizzle.isXML = function( elem ) {
4006
+ // documentElement is verified for cases where it doesn't yet exist
4007
+ // (such as loading iframes in IE - #4833)
4008
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
4009
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
4010
+ };
4011
+
4012
+ /**
4013
+ * Sets document-related variables once based on the current document
4014
+ * @param {Element|Object} [doc] An element or document object to use to set the document
4015
+ * @returns {Object} Returns the current document
4016
+ */
4017
+ setDocument = Sizzle.setDocument = function( node ) {
4018
+ var doc = node ? node.ownerDocument || node : preferredDoc;
4019
+
4020
+ // If no document and documentElement is available, return
4021
+ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
4022
+ return document;
4023
+ }
4024
+
4025
+ // Set our document
4026
+ document = doc;
4027
+ docElem = doc.documentElement;
4028
+
4029
+ // Support tests
4030
+ documentIsXML = isXML( doc );
4031
+
4032
+ // Check if getElementsByTagName("*") returns only elements
4033
+ support.tagNameNoComments = assert(function( div ) {
4034
+ div.appendChild( doc.createComment("") );
4035
+ return !div.getElementsByTagName("*").length;
4036
+ });
4037
+
4038
+ // Check if attributes should be retrieved by attribute nodes
4039
+ support.attributes = assert(function( div ) {
4040
+ div.innerHTML = "<select></select>";
4041
+ var type = typeof div.lastChild.getAttribute("multiple");
4042
+ // IE8 returns a string for some attributes even when not present
4043
+ return type !== "boolean" && type !== "string";
4044
+ });
4045
+
4046
+ // Check if getElementsByClassName can be trusted
4047
+ support.getByClassName = assert(function( div ) {
4048
+ // Opera can't find a second classname (in 9.6)
4049
+ div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
4050
+ if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
4051
+ return false;
4052
+ }
4053
+
4054
+ // Safari 3.2 caches class attributes and doesn't catch changes
4055
+ div.lastChild.className = "e";
4056
+ return div.getElementsByClassName("e").length === 2;
4057
+ });
4058
+
4059
+ // Check if getElementById returns elements by name
4060
+ // Check if getElementsByName privileges form controls or returns elements by ID
4061
+ support.getByName = assert(function( div ) {
4062
+ // Inject content
4063
+ div.id = expando + 0;
4064
+ div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
4065
+ docElem.insertBefore( div, docElem.firstChild );
4066
+
4067
+ // Test
4068
+ var pass = doc.getElementsByName &&
4069
+ // buggy browsers will return fewer than the correct 2
4070
+ doc.getElementsByName( expando ).length === 2 +
4071
+ // buggy browsers will return more than the correct 0
4072
+ doc.getElementsByName( expando + 0 ).length;
4073
+ support.getIdNotName = !doc.getElementById( expando );
4074
+
4075
+ // Cleanup
4076
+ docElem.removeChild( div );
4077
+
4078
+ return pass;
4079
+ });
4080
+
4081
+ // IE6/7 return modified attributes
4082
+ Expr.attrHandle = assert(function( div ) {
4083
+ div.innerHTML = "<a href='#'></a>";
4084
+ return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
4085
+ div.firstChild.getAttribute("href") === "#";
4086
+ }) ?
4087
+ {} :
4088
+ {
4089
+ "href": function( elem ) {
4090
+ return elem.getAttribute( "href", 2 );
4091
+ },
4092
+ "type": function( elem ) {
4093
+ return elem.getAttribute("type");
4094
+ }
4095
+ };
4096
+
4097
+ // ID find and filter
4098
+ if ( support.getIdNotName ) {
4099
+ Expr.find["ID"] = function( id, context ) {
4100
+ if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
4101
+ var m = context.getElementById( id );
4102
+ // Check parentNode to catch when Blackberry 4.6 returns
4103
+ // nodes that are no longer in the document #6963
4104
+ return m && m.parentNode ? [m] : [];
4105
+ }
4106
+ };
4107
+ Expr.filter["ID"] = function( id ) {
4108
+ var attrId = id.replace( runescape, funescape );
4109
+ return function( elem ) {
4110
+ return elem.getAttribute("id") === attrId;
4111
+ };
4112
+ };
4113
+ } else {
4114
+ Expr.find["ID"] = function( id, context ) {
4115
+ if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
4116
+ var m = context.getElementById( id );
4117
+
4118
+ return m ?
4119
+ m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
4120
+ [m] :
4121
+ undefined :
4122
+ [];
4123
+ }
4124
+ };
4125
+ Expr.filter["ID"] = function( id ) {
4126
+ var attrId = id.replace( runescape, funescape );
4127
+ return function( elem ) {
4128
+ var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
4129
+ return node && node.value === attrId;
4130
+ };
4131
+ };
4132
+ }
4133
+
4134
+ // Tag
4135
+ Expr.find["TAG"] = support.tagNameNoComments ?
4136
+ function( tag, context ) {
4137
+ if ( typeof context.getElementsByTagName !== strundefined ) {
4138
+ return context.getElementsByTagName( tag );
4139
+ }
4140
+ } :
4141
+ function( tag, context ) {
4142
+ var elem,
4143
+ tmp = [],
4144
+ i = 0,
4145
+ results = context.getElementsByTagName( tag );
4146
+
4147
+ // Filter out possible comments
4148
+ if ( tag === "*" ) {
4149
+ while ( (elem = results[i++]) ) {
4150
+ if ( elem.nodeType === 1 ) {
4151
+ tmp.push( elem );
4152
+ }
4153
+ }
4154
+
4155
+ return tmp;
4156
+ }
4157
+ return results;
4158
+ };
4159
+
4160
+ // Name
4161
+ Expr.find["NAME"] = support.getByName && function( tag, context ) {
4162
+ if ( typeof context.getElementsByName !== strundefined ) {
4163
+ return context.getElementsByName( name );
4164
+ }
4165
+ };
4166
+
4167
+ // Class
4168
+ Expr.find["CLASS"] = support.getByClassName && function( className, context ) {
4169
+ if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) {
4170
+ return context.getElementsByClassName( className );
4171
+ }
4172
+ };
4173
+
4174
+ // QSA and matchesSelector support
4175
+
4176
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
4177
+ rbuggyMatches = [];
4178
+
4179
+ // qSa(:focus) reports false when true (Chrome 21),
4180
+ // no need to also add to buggyMatches since matches checks buggyQSA
4181
+ // A support test would require too much code (would include document ready)
4182
+ rbuggyQSA = [ ":focus" ];
4183
+
4184
+ if ( (support.qsa = isNative(doc.querySelectorAll)) ) {
4185
+ // Build QSA regex
4186
+ // Regex strategy adopted from Diego Perini
4187
+ assert(function( div ) {
4188
+ // Select is set to empty string on purpose
4189
+ // This is to test IE's treatment of not explictly
4190
+ // setting a boolean content attribute,
4191
+ // since its presence should be enough
4192
+ // http://bugs.jquery.com/ticket/12359
4193
+ div.innerHTML = "<select><option selected=''></option></select>";
4194
+
4195
+ // IE8 - Some boolean attributes are not treated correctly
4196
+ if ( !div.querySelectorAll("[selected]").length ) {
4197
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
4198
+ }
4199
+
4200
+ // Webkit/Opera - :checked should return selected option elements
4201
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
4202
+ // IE8 throws error here and will not see later tests
4203
+ if ( !div.querySelectorAll(":checked").length ) {
4204
+ rbuggyQSA.push(":checked");
4205
+ }
4206
+ });
4207
+
4208
+ assert(function( div ) {
4209
+
4210
+ // Opera 10-12/IE8 - ^= $= *= and empty values
4211
+ // Should not select anything
4212
+ div.innerHTML = "<input type='hidden' i=''/>";
4213
+ if ( div.querySelectorAll("[i^='']").length ) {
4214
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
4215
+ }
4216
+
4217
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
4218
+ // IE8 throws error here and will not see later tests
4219
+ if ( !div.querySelectorAll(":enabled").length ) {
4220
+ rbuggyQSA.push( ":enabled", ":disabled" );
4221
+ }
4222
+
4223
+ // Opera 10-11 does not throw on post-comma invalid pseudos
4224
+ div.querySelectorAll("*,:x");
4225
+ rbuggyQSA.push(",.*:");
4226
+ });
4227
+ }
4228
+
4229
+ if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector ||
4230
+ docElem.mozMatchesSelector ||
4231
+ docElem.webkitMatchesSelector ||
4232
+ docElem.oMatchesSelector ||
4233
+ docElem.msMatchesSelector) )) ) {
4234
+
4235
+ assert(function( div ) {
4236
+ // Check to see if it's possible to do matchesSelector
4237
+ // on a disconnected node (IE 9)
4238
+ support.disconnectedMatch = matches.call( div, "div" );
4239
+
4240
+ // This should fail with an exception
4241
+ // Gecko does not error, returns false instead
4242
+ matches.call( div, "[s!='']:x" );
4243
+ rbuggyMatches.push( "!=", pseudos );
4244
+ });
4245
+ }
4246
+
4247
+ rbuggyQSA = new RegExp( rbuggyQSA.join("|") );
4248
+ rbuggyMatches = new RegExp( rbuggyMatches.join("|") );
4249
+
4250
+ // Element contains another
4251
+ // Purposefully does not implement inclusive descendent
4252
+ // As in, an element does not contain itself
4253
+ contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
4254
+ function( a, b ) {
4255
+ var adown = a.nodeType === 9 ? a.documentElement : a,
4256
+ bup = b && b.parentNode;
4257
+ return a === bup || !!( bup && bup.nodeType === 1 && (
4258
+ adown.contains ?
4259
+ adown.contains( bup ) :
4260
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
4261
+ ));
4262
+ } :
4263
+ function( a, b ) {
4264
+ if ( b ) {
4265
+ while ( (b = b.parentNode) ) {
4266
+ if ( b === a ) {
4267
+ return true;
4268
+ }
4269
+ }
4270
+ }
4271
+ return false;
4272
+ };
4273
+
4274
+ // Document order sorting
4275
+ sortOrder = docElem.compareDocumentPosition ?
4276
+ function( a, b ) {
4277
+ var compare;
4278
+
4279
+ if ( a === b ) {
4280
+ hasDuplicate = true;
4281
+ return 0;
4282
+ }
4283
+
4284
+ if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b )) ) {
4285
+ if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) {
4286
+ if ( a === doc || contains( preferredDoc, a ) ) {
4287
+ return -1;
4288
+ }
4289
+ if ( b === doc || contains( preferredDoc, b ) ) {
4290
+ return 1;
4291
+ }
4292
+ return 0;
4293
+ }
4294
+ return compare & 4 ? -1 : 1;
4295
+ }
4296
+
4297
+ return a.compareDocumentPosition ? -1 : 1;
4298
+ } :
4299
+ function( a, b ) {
4300
+ var cur,
4301
+ i = 0,
4302
+ aup = a.parentNode,
4303
+ bup = b.parentNode,
4304
+ ap = [ a ],
4305
+ bp = [ b ];
4306
+
4307
+ // Exit early if the nodes are identical
4308
+ if ( a === b ) {
4309
+ hasDuplicate = true;
4310
+ return 0;
4311
+
4312
+ // Parentless nodes are either documents or disconnected
4313
+ } else if ( !aup || !bup ) {
4314
+ return a === doc ? -1 :
4315
+ b === doc ? 1 :
4316
+ aup ? -1 :
4317
+ bup ? 1 :
4318
+ 0;
4319
+
4320
+ // If the nodes are siblings, we can do a quick check
4321
+ } else if ( aup === bup ) {
4322
+ return siblingCheck( a, b );
4323
+ }
4324
+
4325
+ // Otherwise we need full lists of their ancestors for comparison
4326
+ cur = a;
4327
+ while ( (cur = cur.parentNode) ) {
4328
+ ap.unshift( cur );
4329
+ }
4330
+ cur = b;
4331
+ while ( (cur = cur.parentNode) ) {
4332
+ bp.unshift( cur );
4333
+ }
4334
+
4335
+ // Walk down the tree looking for a discrepancy
4336
+ while ( ap[i] === bp[i] ) {
4337
+ i++;
4338
+ }
4339
+
4340
+ return i ?
4341
+ // Do a sibling check if the nodes have a common ancestor
4342
+ siblingCheck( ap[i], bp[i] ) :
4343
+
4344
+ // Otherwise nodes in our document sort first
4345
+ ap[i] === preferredDoc ? -1 :
4346
+ bp[i] === preferredDoc ? 1 :
4347
+ 0;
4348
+ };
4349
+
4350
+ // Always assume the presence of duplicates if sort doesn't
4351
+ // pass them to our comparison function (as in Google Chrome).
4352
+ hasDuplicate = false;
4353
+ [0, 0].sort( sortOrder );
4354
+ support.detectDuplicates = hasDuplicate;
4355
+
4356
+ return document;
4357
+ };
4358
+
4359
+ Sizzle.matches = function( expr, elements ) {
4360
+ return Sizzle( expr, null, null, elements );
4361
+ };
4362
+
4363
+ Sizzle.matchesSelector = function( elem, expr ) {
4364
+ // Set document vars if needed
4365
+ if ( ( elem.ownerDocument || elem ) !== document ) {
4366
+ setDocument( elem );
4367
+ }
4368
+
4369
+ // Make sure that attribute selectors are quoted
4370
+ expr = expr.replace( rattributeQuotes, "='$1']" );
4371
+
4372
+ // rbuggyQSA always contains :focus, so no need for an existence check
4373
+ if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) {
4374
+ try {
4375
+ var ret = matches.call( elem, expr );
4376
+
4377
+ // IE 9's matchesSelector returns false on disconnected nodes
4378
+ if ( ret || support.disconnectedMatch ||
4379
+ // As well, disconnected nodes are said to be in a document
4380
+ // fragment in IE 9
4381
+ elem.document && elem.document.nodeType !== 11 ) {
4382
+ return ret;
4383
+ }
4384
+ } catch(e) {}
4385
+ }
4386
+
4387
+ return Sizzle( expr, document, null, [elem] ).length > 0;
4388
+ };
4389
+
4390
+ Sizzle.contains = function( context, elem ) {
4391
+ // Set document vars if needed
4392
+ if ( ( context.ownerDocument || context ) !== document ) {
4393
+ setDocument( context );
4394
+ }
4395
+ return contains( context, elem );
4396
+ };
4397
+
4398
+ Sizzle.attr = function( elem, name ) {
4399
+ var val;
4400
+
4401
+ // Set document vars if needed
4402
+ if ( ( elem.ownerDocument || elem ) !== document ) {
4403
+ setDocument( elem );
4404
+ }
4405
+
4406
+ if ( !documentIsXML ) {
4407
+ name = name.toLowerCase();
4408
+ }
4409
+ if ( (val = Expr.attrHandle[ name ]) ) {
4410
+ return val( elem );
4411
+ }
4412
+ if ( documentIsXML || support.attributes ) {
4413
+ return elem.getAttribute( name );
4414
+ }
4415
+ return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ?
4416
+ name :
4417
+ val && val.specified ? val.value : null;
4418
+ };
4419
+
4420
+ Sizzle.error = function( msg ) {
4421
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
4422
+ };
4423
+
4424
+ // Document sorting and removing duplicates
4425
+ Sizzle.uniqueSort = function( results ) {
4426
+ var elem,
4427
+ duplicates = [],
4428
+ i = 1,
4429
+ j = 0;
4430
+
4431
+ // Unless we *know* we can detect duplicates, assume their presence
4432
+ hasDuplicate = !support.detectDuplicates;
4433
+ results.sort( sortOrder );
4434
+
4435
+ if ( hasDuplicate ) {
4436
+ for ( ; (elem = results[i]); i++ ) {
4437
+ if ( elem === results[ i - 1 ] ) {
4438
+ j = duplicates.push( i );
4439
+ }
4440
+ }
4441
+ while ( j-- ) {
4442
+ results.splice( duplicates[ j ], 1 );
4443
+ }
4444
+ }
4445
+
4446
+ return results;
4447
+ };
4448
+
4449
+ function siblingCheck( a, b ) {
4450
+ var cur = b && a,
4451
+ diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE );
4452
+
4453
+ // Use IE sourceIndex if available on both nodes
4454
+ if ( diff ) {
4455
+ return diff;
4456
+ }
4457
+
4458
+ // Check if b follows a
4459
+ if ( cur ) {
4460
+ while ( (cur = cur.nextSibling) ) {
4461
+ if ( cur === b ) {
4462
+ return -1;
4463
+ }
4464
+ }
4465
+ }
4466
+
4467
+ return a ? 1 : -1;
4468
+ }
4469
+
4470
+ // Returns a function to use in pseudos for input types
4471
+ function createInputPseudo( type ) {
4472
+ return function( elem ) {
4473
+ var name = elem.nodeName.toLowerCase();
4474
+ return name === "input" && elem.type === type;
4475
+ };
4476
+ }
4477
+
4478
+ // Returns a function to use in pseudos for buttons
4479
+ function createButtonPseudo( type ) {
4480
+ return function( elem ) {
4481
+ var name = elem.nodeName.toLowerCase();
4482
+ return (name === "input" || name === "button") && elem.type === type;
4483
+ };
4484
+ }
4485
+
4486
+ // Returns a function to use in pseudos for positionals
4487
+ function createPositionalPseudo( fn ) {
4488
+ return markFunction(function( argument ) {
4489
+ argument = +argument;
4490
+ return markFunction(function( seed, matches ) {
4491
+ var j,
4492
+ matchIndexes = fn( [], seed.length, argument ),
4493
+ i = matchIndexes.length;
4494
+
4495
+ // Match elements found at the specified indexes
4496
+ while ( i-- ) {
4497
+ if ( seed[ (j = matchIndexes[i]) ] ) {
4498
+ seed[j] = !(matches[j] = seed[j]);
4499
+ }
4500
+ }
4501
+ });
4502
+ });
4503
+ }
4504
+
4505
+ /**
4506
+ * Utility function for retrieving the text value of an array of DOM nodes
4507
+ * @param {Array|Element} elem
4508
+ */
4509
+ getText = Sizzle.getText = function( elem ) {
4510
+ var node,
4511
+ ret = "",
4512
+ i = 0,
4513
+ nodeType = elem.nodeType;
4514
+
4515
+ if ( !nodeType ) {
4516
+ // If no nodeType, this is expected to be an array
4517
+ for ( ; (node = elem[i]); i++ ) {
4518
+ // Do not traverse comment nodes
4519
+ ret += getText( node );
4520
+ }
4521
+ } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
4522
+ // Use textContent for elements
4523
+ // innerText usage removed for consistency of new lines (see #11153)
4524
+ if ( typeof elem.textContent === "string" ) {
4525
+ return elem.textContent;
4526
+ } else {
4527
+ // Traverse its children
4528
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
4529
+ ret += getText( elem );
4530
+ }
4531
+ }
4532
+ } else if ( nodeType === 3 || nodeType === 4 ) {
4533
+ return elem.nodeValue;
4534
+ }
4535
+ // Do not include comment or processing instruction nodes
4536
+
4537
+ return ret;
4538
+ };
4539
+
4540
+ Expr = Sizzle.selectors = {
4541
+
4542
+ // Can be adjusted by the user
4543
+ cacheLength: 50,
4544
+
4545
+ createPseudo: markFunction,
4546
+
4547
+ match: matchExpr,
4548
+
4549
+ find: {},
4550
+
4551
+ relative: {
4552
+ ">": { dir: "parentNode", first: true },
4553
+ " ": { dir: "parentNode" },
4554
+ "+": { dir: "previousSibling", first: true },
4555
+ "~": { dir: "previousSibling" }
4556
+ },
4557
+
4558
+ preFilter: {
4559
+ "ATTR": function( match ) {
4560
+ match[1] = match[1].replace( runescape, funescape );
4561
+
4562
+ // Move the given value to match[3] whether quoted or unquoted
4563
+ match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
4564
+
4565
+ if ( match[2] === "~=" ) {
4566
+ match[3] = " " + match[3] + " ";
4567
+ }
4568
+
4569
+ return match.slice( 0, 4 );
4570
+ },
4571
+
4572
+ "CHILD": function( match ) {
4573
+ /* matches from matchExpr["CHILD"]
4574
+ 1 type (only|nth|...)
4575
+ 2 what (child|of-type)
4576
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
4577
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
4578
+ 5 sign of xn-component
4579
+ 6 x of xn-component
4580
+ 7 sign of y-component
4581
+ 8 y of y-component
4582
+ */
4583
+ match[1] = match[1].toLowerCase();
4584
+
4585
+ if ( match[1].slice( 0, 3 ) === "nth" ) {
4586
+ // nth-* requires argument
4587
+ if ( !match[3] ) {
4588
+ Sizzle.error( match[0] );
4589
+ }
4590
+
4591
+ // numeric x and y parameters for Expr.filter.CHILD
4592
+ // remember that false/true cast respectively to 0/1
4593
+ match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
4594
+ match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
4595
+
4596
+ // other types prohibit arguments
4597
+ } else if ( match[3] ) {
4598
+ Sizzle.error( match[0] );
4599
+ }
4600
+
4601
+ return match;
4602
+ },
4603
+
4604
+ "PSEUDO": function( match ) {
4605
+ var excess,
4606
+ unquoted = !match[5] && match[2];
4607
+
4608
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
4609
+ return null;
4610
+ }
4611
+
4612
+ // Accept quoted arguments as-is
4613
+ if ( match[4] ) {
4614
+ match[2] = match[4];
4615
+
4616
+ // Strip excess characters from unquoted arguments
4617
+ } else if ( unquoted && rpseudo.test( unquoted ) &&
4618
+ // Get excess from tokenize (recursively)
4619
+ (excess = tokenize( unquoted, true )) &&
4620
+ // advance to the next closing parenthesis
4621
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
4622
+
4623
+ // excess is a negative index
4624
+ match[0] = match[0].slice( 0, excess );
4625
+ match[2] = unquoted.slice( 0, excess );
4626
+ }
4627
+
4628
+ // Return only captures needed by the pseudo filter method (type and argument)
4629
+ return match.slice( 0, 3 );
4630
+ }
4631
+ },
4632
+
4633
+ filter: {
4634
+
4635
+ "TAG": function( nodeName ) {
4636
+ if ( nodeName === "*" ) {
4637
+ return function() { return true; };
4638
+ }
4639
+
4640
+ nodeName = nodeName.replace( runescape, funescape ).toLowerCase();
4641
+ return function( elem ) {
4642
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
4643
+ };
4644
+ },
4645
+
4646
+ "CLASS": function( className ) {
4647
+ var pattern = classCache[ className + " " ];
4648
+
4649
+ return pattern ||
4650
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
4651
+ classCache( className, function( elem ) {
4652
+ return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
4653
+ });
4654
+ },
4655
+
4656
+ "ATTR": function( name, operator, check ) {
4657
+ return function( elem ) {
4658
+ var result = Sizzle.attr( elem, name );
4659
+
4660
+ if ( result == null ) {
4661
+ return operator === "!=";
4662
+ }
4663
+ if ( !operator ) {
4664
+ return true;
4665
+ }
4666
+
4667
+ result += "";
4668
+
4669
+ return operator === "=" ? result === check :
4670
+ operator === "!=" ? result !== check :
4671
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
4672
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
4673
+ operator === "$=" ? check && result.slice( -check.length ) === check :
4674
+ operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
4675
+ operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
4676
+ false;
4677
+ };
4678
+ },
4679
+
4680
+ "CHILD": function( type, what, argument, first, last ) {
4681
+ var simple = type.slice( 0, 3 ) !== "nth",
4682
+ forward = type.slice( -4 ) !== "last",
4683
+ ofType = what === "of-type";
4684
+
4685
+ return first === 1 && last === 0 ?
4686
+
4687
+ // Shortcut for :nth-*(n)
4688
+ function( elem ) {
4689
+ return !!elem.parentNode;
4690
+ } :
4691
+
4692
+ function( elem, context, xml ) {
4693
+ var cache, outerCache, node, diff, nodeIndex, start,
4694
+ dir = simple !== forward ? "nextSibling" : "previousSibling",
4695
+ parent = elem.parentNode,
4696
+ name = ofType && elem.nodeName.toLowerCase(),
4697
+ useCache = !xml && !ofType;
4698
+
4699
+ if ( parent ) {
4700
+
4701
+ // :(first|last|only)-(child|of-type)
4702
+ if ( simple ) {
4703
+ while ( dir ) {
4704
+ node = elem;
4705
+ while ( (node = node[ dir ]) ) {
4706
+ if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
4707
+ return false;
4708
+ }
4709
+ }
4710
+ // Reverse direction for :only-* (if we haven't yet done so)
4711
+ start = dir = type === "only" && !start && "nextSibling";
4712
+ }
4713
+ return true;
4714
+ }
4715
+
4716
+ start = [ forward ? parent.firstChild : parent.lastChild ];
4717
+
4718
+ // non-xml :nth-child(...) stores cache data on `parent`
4719
+ if ( forward && useCache ) {
4720
+ // Seek `elem` from a previously-cached index
4721
+ outerCache = parent[ expando ] || (parent[ expando ] = {});
4722
+ cache = outerCache[ type ] || [];
4723
+ nodeIndex = cache[0] === dirruns && cache[1];
4724
+ diff = cache[0] === dirruns && cache[2];
4725
+ node = nodeIndex && parent.childNodes[ nodeIndex ];
4726
+
4727
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
4728
+
4729
+ // Fallback to seeking `elem` from the start
4730
+ (diff = nodeIndex = 0) || start.pop()) ) {
4731
+
4732
+ // When found, cache indexes on `parent` and break
4733
+ if ( node.nodeType === 1 && ++diff && node === elem ) {
4734
+ outerCache[ type ] = [ dirruns, nodeIndex, diff ];
4735
+ break;
4736
+ }
4737
+ }
4738
+
4739
+ // Use previously-cached element index if available
4740
+ } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
4741
+ diff = cache[1];
4742
+
4743
+ // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
4744
+ } else {
4745
+ // Use the same loop as above to seek `elem` from the start
4746
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
4747
+ (diff = nodeIndex = 0) || start.pop()) ) {
4748
+
4749
+ if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
4750
+ // Cache the index of each encountered element
4751
+ if ( useCache ) {
4752
+ (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
4753
+ }
4754
+
4755
+ if ( node === elem ) {
4756
+ break;
4757
+ }
4758
+ }
4759
+ }
4760
+ }
4761
+
4762
+ // Incorporate the offset, then check against cycle size
4763
+ diff -= last;
4764
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
4765
+ }
4766
+ };
4767
+ },
4768
+
4769
+ "PSEUDO": function( pseudo, argument ) {
4770
+ // pseudo-class names are case-insensitive
4771
+ // http://www.w3.org/TR/selectors/#pseudo-classes
4772
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
4773
+ // Remember that setFilters inherits from pseudos
4774
+ var args,
4775
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
4776
+ Sizzle.error( "unsupported pseudo: " + pseudo );
4777
+
4778
+ // The user may use createPseudo to indicate that
4779
+ // arguments are needed to create the filter function
4780
+ // just as Sizzle does
4781
+ if ( fn[ expando ] ) {
4782
+ return fn( argument );
4783
+ }
4784
+
4785
+ // But maintain support for old signatures
4786
+ if ( fn.length > 1 ) {
4787
+ args = [ pseudo, pseudo, "", argument ];
4788
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
4789
+ markFunction(function( seed, matches ) {
4790
+ var idx,
4791
+ matched = fn( seed, argument ),
4792
+ i = matched.length;
4793
+ while ( i-- ) {
4794
+ idx = indexOf.call( seed, matched[i] );
4795
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
4796
+ }
4797
+ }) :
4798
+ function( elem ) {
4799
+ return fn( elem, 0, args );
4800
+ };
4801
+ }
4802
+
4803
+ return fn;
4804
+ }
4805
+ },
4806
+
4807
+ pseudos: {
4808
+ // Potentially complex pseudos
4809
+ "not": markFunction(function( selector ) {
4810
+ // Trim the selector passed to compile
4811
+ // to avoid treating leading and trailing
4812
+ // spaces as combinators
4813
+ var input = [],
4814
+ results = [],
4815
+ matcher = compile( selector.replace( rtrim, "$1" ) );
4816
+
4817
+ return matcher[ expando ] ?
4818
+ markFunction(function( seed, matches, context, xml ) {
4819
+ var elem,
4820
+ unmatched = matcher( seed, null, xml, [] ),
4821
+ i = seed.length;
4822
+
4823
+ // Match elements unmatched by `matcher`
4824
+ while ( i-- ) {
4825
+ if ( (elem = unmatched[i]) ) {
4826
+ seed[i] = !(matches[i] = elem);
4827
+ }
4828
+ }
4829
+ }) :
4830
+ function( elem, context, xml ) {
4831
+ input[0] = elem;
4832
+ matcher( input, null, xml, results );
4833
+ return !results.pop();
4834
+ };
4835
+ }),
4836
+
4837
+ "has": markFunction(function( selector ) {
4838
+ return function( elem ) {
4839
+ return Sizzle( selector, elem ).length > 0;
4840
+ };
4841
+ }),
4842
+
4843
+ "contains": markFunction(function( text ) {
4844
+ return function( elem ) {
4845
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
4846
+ };
4847
+ }),
4848
+
4849
+ // "Whether an element is represented by a :lang() selector
4850
+ // is based solely on the element's language value
4851
+ // being equal to the identifier C,
4852
+ // or beginning with the identifier C immediately followed by "-".
4853
+ // The matching of C against the element's language value is performed case-insensitively.
4854
+ // The identifier C does not have to be a valid language name."
4855
+ // http://www.w3.org/TR/selectors/#lang-pseudo
4856
+ "lang": markFunction( function( lang ) {
4857
+ // lang value must be a valid identifider
4858
+ if ( !ridentifier.test(lang || "") ) {
4859
+ Sizzle.error( "unsupported lang: " + lang );
4860
+ }
4861
+ lang = lang.replace( runescape, funescape ).toLowerCase();
4862
+ return function( elem ) {
4863
+ var elemLang;
4864
+ do {
4865
+ if ( (elemLang = documentIsXML ?
4866
+ elem.getAttribute("xml:lang") || elem.getAttribute("lang") :
4867
+ elem.lang) ) {
4868
+
4869
+ elemLang = elemLang.toLowerCase();
4870
+ return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
4871
+ }
4872
+ } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
4873
+ return false;
4874
+ };
4875
+ }),
4876
+
4877
+ // Miscellaneous
4878
+ "target": function( elem ) {
4879
+ var hash = window.location && window.location.hash;
4880
+ return hash && hash.slice( 1 ) === elem.id;
4881
+ },
4882
+
4883
+ "root": function( elem ) {
4884
+ return elem === docElem;
4885
+ },
4886
+
4887
+ "focus": function( elem ) {
4888
+ return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
4889
+ },
4890
+
4891
+ // Boolean properties
4892
+ "enabled": function( elem ) {
4893
+ return elem.disabled === false;
4894
+ },
4895
+
4896
+ "disabled": function( elem ) {
4897
+ return elem.disabled === true;
4898
+ },
4899
+
4900
+ "checked": function( elem ) {
4901
+ // In CSS3, :checked should return both checked and selected elements
4902
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
4903
+ var nodeName = elem.nodeName.toLowerCase();
4904
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
4905
+ },
4906
+
4907
+ "selected": function( elem ) {
4908
+ // Accessing this property makes selected-by-default
4909
+ // options in Safari work properly
4910
+ if ( elem.parentNode ) {
4911
+ elem.parentNode.selectedIndex;
4912
+ }
4913
+
4914
+ return elem.selected === true;
4915
+ },
4916
+
4917
+ // Contents
4918
+ "empty": function( elem ) {
4919
+ // http://www.w3.org/TR/selectors/#empty-pseudo
4920
+ // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
4921
+ // not comment, processing instructions, or others
4922
+ // Thanks to Diego Perini for the nodeName shortcut
4923
+ // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
4924
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
4925
+ if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
4926
+ return false;
4927
+ }
4928
+ }
4929
+ return true;
4930
+ },
4931
+
4932
+ "parent": function( elem ) {
4933
+ return !Expr.pseudos["empty"]( elem );
4934
+ },
4935
+
4936
+ // Element/input types
4937
+ "header": function( elem ) {
4938
+ return rheader.test( elem.nodeName );
4939
+ },
4940
+
4941
+ "input": function( elem ) {
4942
+ return rinputs.test( elem.nodeName );
4943
+ },
4944
+
4945
+ "button": function( elem ) {
4946
+ var name = elem.nodeName.toLowerCase();
4947
+ return name === "input" && elem.type === "button" || name === "button";
4948
+ },
4949
+
4950
+ "text": function( elem ) {
4951
+ var attr;
4952
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
4953
+ // use getAttribute instead to test this case
4954
+ return elem.nodeName.toLowerCase() === "input" &&
4955
+ elem.type === "text" &&
4956
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
4957
+ },
4958
+
4959
+ // Position-in-collection
4960
+ "first": createPositionalPseudo(function() {
4961
+ return [ 0 ];
4962
+ }),
4963
+
4964
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
4965
+ return [ length - 1 ];
4966
+ }),
4967
+
4968
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
4969
+ return [ argument < 0 ? argument + length : argument ];
4970
+ }),
4971
+
4972
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
4973
+ var i = 0;
4974
+ for ( ; i < length; i += 2 ) {
4975
+ matchIndexes.push( i );
4976
+ }
4977
+ return matchIndexes;
4978
+ }),
4979
+
4980
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
4981
+ var i = 1;
4982
+ for ( ; i < length; i += 2 ) {
4983
+ matchIndexes.push( i );
4984
+ }
4985
+ return matchIndexes;
4986
+ }),
4987
+
4988
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
4989
+ var i = argument < 0 ? argument + length : argument;
4990
+ for ( ; --i >= 0; ) {
4991
+ matchIndexes.push( i );
4992
+ }
4993
+ return matchIndexes;
4994
+ }),
4995
+
4996
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
4997
+ var i = argument < 0 ? argument + length : argument;
4998
+ for ( ; ++i < length; ) {
4999
+ matchIndexes.push( i );
5000
+ }
5001
+ return matchIndexes;
5002
+ })
5003
+ }
5004
+ };
5005
+
5006
+ // Add button/input type pseudos
5007
+ for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
5008
+ Expr.pseudos[ i ] = createInputPseudo( i );
5009
+ }
5010
+ for ( i in { submit: true, reset: true } ) {
5011
+ Expr.pseudos[ i ] = createButtonPseudo( i );
5012
+ }
5013
+
5014
+ function tokenize( selector, parseOnly ) {
5015
+ var matched, match, tokens, type,
5016
+ soFar, groups, preFilters,
5017
+ cached = tokenCache[ selector + " " ];
5018
+
5019
+ if ( cached ) {
5020
+ return parseOnly ? 0 : cached.slice( 0 );
5021
+ }
5022
+
5023
+ soFar = selector;
5024
+ groups = [];
5025
+ preFilters = Expr.preFilter;
5026
+
5027
+ while ( soFar ) {
5028
+
5029
+ // Comma and first run
5030
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
5031
+ if ( match ) {
5032
+ // Don't consume trailing commas as valid
5033
+ soFar = soFar.slice( match[0].length ) || soFar;
5034
+ }
5035
+ groups.push( tokens = [] );
5036
+ }
5037
+
5038
+ matched = false;
5039
+
5040
+ // Combinators
5041
+ if ( (match = rcombinators.exec( soFar )) ) {
5042
+ matched = match.shift();
5043
+ tokens.push( {
5044
+ value: matched,
5045
+ // Cast descendant combinators to space
5046
+ type: match[0].replace( rtrim, " " )
5047
+ } );
5048
+ soFar = soFar.slice( matched.length );
5049
+ }
5050
+
5051
+ // Filters
5052
+ for ( type in Expr.filter ) {
5053
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
5054
+ (match = preFilters[ type ]( match ))) ) {
5055
+ matched = match.shift();
5056
+ tokens.push( {
5057
+ value: matched,
5058
+ type: type,
5059
+ matches: match
5060
+ } );
5061
+ soFar = soFar.slice( matched.length );
5062
+ }
5063
+ }
5064
+
5065
+ if ( !matched ) {
5066
+ break;
5067
+ }
5068
+ }
5069
+
5070
+ // Return the length of the invalid excess
5071
+ // if we're just parsing
5072
+ // Otherwise, throw an error or return tokens
5073
+ return parseOnly ?
5074
+ soFar.length :
5075
+ soFar ?
5076
+ Sizzle.error( selector ) :
5077
+ // Cache the tokens
5078
+ tokenCache( selector, groups ).slice( 0 );
5079
+ }
5080
+
5081
+ function toSelector( tokens ) {
5082
+ var i = 0,
5083
+ len = tokens.length,
5084
+ selector = "";
5085
+ for ( ; i < len; i++ ) {
5086
+ selector += tokens[i].value;
5087
+ }
5088
+ return selector;
5089
+ }
5090
+
5091
+ function addCombinator( matcher, combinator, base ) {
5092
+ var dir = combinator.dir,
5093
+ checkNonElements = base && dir === "parentNode",
5094
+ doneName = done++;
5095
+
5096
+ return combinator.first ?
5097
+ // Check against closest ancestor/preceding element
5098
+ function( elem, context, xml ) {
5099
+ while ( (elem = elem[ dir ]) ) {
5100
+ if ( elem.nodeType === 1 || checkNonElements ) {
5101
+ return matcher( elem, context, xml );
5102
+ }
5103
+ }
5104
+ } :
5105
+
5106
+ // Check against all ancestor/preceding elements
5107
+ function( elem, context, xml ) {
5108
+ var data, cache, outerCache,
5109
+ dirkey = dirruns + " " + doneName;
5110
+
5111
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
5112
+ if ( xml ) {
5113
+ while ( (elem = elem[ dir ]) ) {
5114
+ if ( elem.nodeType === 1 || checkNonElements ) {
5115
+ if ( matcher( elem, context, xml ) ) {
5116
+ return true;
5117
+ }
5118
+ }
5119
+ }
5120
+ } else {
5121
+ while ( (elem = elem[ dir ]) ) {
5122
+ if ( elem.nodeType === 1 || checkNonElements ) {
5123
+ outerCache = elem[ expando ] || (elem[ expando ] = {});
5124
+ if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
5125
+ if ( (data = cache[1]) === true || data === cachedruns ) {
5126
+ return data === true;
5127
+ }
5128
+ } else {
5129
+ cache = outerCache[ dir ] = [ dirkey ];
5130
+ cache[1] = matcher( elem, context, xml ) || cachedruns;
5131
+ if ( cache[1] === true ) {
5132
+ return true;
5133
+ }
5134
+ }
5135
+ }
5136
+ }
5137
+ }
5138
+ };
5139
+ }
5140
+
5141
+ function elementMatcher( matchers ) {
5142
+ return matchers.length > 1 ?
5143
+ function( elem, context, xml ) {
5144
+ var i = matchers.length;
5145
+ while ( i-- ) {
5146
+ if ( !matchers[i]( elem, context, xml ) ) {
5147
+ return false;
5148
+ }
5149
+ }
5150
+ return true;
5151
+ } :
5152
+ matchers[0];
5153
+ }
5154
+
5155
+ function condense( unmatched, map, filter, context, xml ) {
5156
+ var elem,
5157
+ newUnmatched = [],
5158
+ i = 0,
5159
+ len = unmatched.length,
5160
+ mapped = map != null;
5161
+
5162
+ for ( ; i < len; i++ ) {
5163
+ if ( (elem = unmatched[i]) ) {
5164
+ if ( !filter || filter( elem, context, xml ) ) {
5165
+ newUnmatched.push( elem );
5166
+ if ( mapped ) {
5167
+ map.push( i );
5168
+ }
5169
+ }
5170
+ }
5171
+ }
5172
+
5173
+ return newUnmatched;
5174
+ }
5175
+
5176
+ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
5177
+ if ( postFilter && !postFilter[ expando ] ) {
5178
+ postFilter = setMatcher( postFilter );
5179
+ }
5180
+ if ( postFinder && !postFinder[ expando ] ) {
5181
+ postFinder = setMatcher( postFinder, postSelector );
5182
+ }
5183
+ return markFunction(function( seed, results, context, xml ) {
5184
+ var temp, i, elem,
5185
+ preMap = [],
5186
+ postMap = [],
5187
+ preexisting = results.length,
5188
+
5189
+ // Get initial elements from seed or context
5190
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
5191
+
5192
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
5193
+ matcherIn = preFilter && ( seed || !selector ) ?
5194
+ condense( elems, preMap, preFilter, context, xml ) :
5195
+ elems,
5196
+
5197
+ matcherOut = matcher ?
5198
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
5199
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
5200
+
5201
+ // ...intermediate processing is necessary
5202
+ [] :
5203
+
5204
+ // ...otherwise use results directly
5205
+ results :
5206
+ matcherIn;
5207
+
5208
+ // Find primary matches
5209
+ if ( matcher ) {
5210
+ matcher( matcherIn, matcherOut, context, xml );
5211
+ }
5212
+
5213
+ // Apply postFilter
5214
+ if ( postFilter ) {
5215
+ temp = condense( matcherOut, postMap );
5216
+ postFilter( temp, [], context, xml );
5217
+
5218
+ // Un-match failing elements by moving them back to matcherIn
5219
+ i = temp.length;
5220
+ while ( i-- ) {
5221
+ if ( (elem = temp[i]) ) {
5222
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
5223
+ }
5224
+ }
5225
+ }
5226
+
5227
+ if ( seed ) {
5228
+ if ( postFinder || preFilter ) {
5229
+ if ( postFinder ) {
5230
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
5231
+ temp = [];
5232
+ i = matcherOut.length;
5233
+ while ( i-- ) {
5234
+ if ( (elem = matcherOut[i]) ) {
5235
+ // Restore matcherIn since elem is not yet a final match
5236
+ temp.push( (matcherIn[i] = elem) );
5237
+ }
5238
+ }
5239
+ postFinder( null, (matcherOut = []), temp, xml );
5240
+ }
5241
+
5242
+ // Move matched elements from seed to results to keep them synchronized
5243
+ i = matcherOut.length;
5244
+ while ( i-- ) {
5245
+ if ( (elem = matcherOut[i]) &&
5246
+ (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
5247
+
5248
+ seed[temp] = !(results[temp] = elem);
5249
+ }
5250
+ }
5251
+ }
5252
+
5253
+ // Add elements to results, through postFinder if defined
5254
+ } else {
5255
+ matcherOut = condense(
5256
+ matcherOut === results ?
5257
+ matcherOut.splice( preexisting, matcherOut.length ) :
5258
+ matcherOut
5259
+ );
5260
+ if ( postFinder ) {
5261
+ postFinder( null, results, matcherOut, xml );
5262
+ } else {
5263
+ push.apply( results, matcherOut );
5264
+ }
5265
+ }
5266
+ });
5267
+ }
5268
+
5269
+ function matcherFromTokens( tokens ) {
5270
+ var checkContext, matcher, j,
5271
+ len = tokens.length,
5272
+ leadingRelative = Expr.relative[ tokens[0].type ],
5273
+ implicitRelative = leadingRelative || Expr.relative[" "],
5274
+ i = leadingRelative ? 1 : 0,
5275
+
5276
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
5277
+ matchContext = addCombinator( function( elem ) {
5278
+ return elem === checkContext;
5279
+ }, implicitRelative, true ),
5280
+ matchAnyContext = addCombinator( function( elem ) {
5281
+ return indexOf.call( checkContext, elem ) > -1;
5282
+ }, implicitRelative, true ),
5283
+ matchers = [ function( elem, context, xml ) {
5284
+ return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
5285
+ (checkContext = context).nodeType ?
5286
+ matchContext( elem, context, xml ) :
5287
+ matchAnyContext( elem, context, xml ) );
5288
+ } ];
5289
+
5290
+ for ( ; i < len; i++ ) {
5291
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
5292
+ matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
5293
+ } else {
5294
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
5295
+
5296
+ // Return special upon seeing a positional matcher
5297
+ if ( matcher[ expando ] ) {
5298
+ // Find the next relative operator (if any) for proper handling
5299
+ j = ++i;
5300
+ for ( ; j < len; j++ ) {
5301
+ if ( Expr.relative[ tokens[j].type ] ) {
5302
+ break;
5303
+ }
5304
+ }
5305
+ return setMatcher(
5306
+ i > 1 && elementMatcher( matchers ),
5307
+ i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ),
5308
+ matcher,
5309
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
5310
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
5311
+ j < len && toSelector( tokens )
5312
+ );
5313
+ }
5314
+ matchers.push( matcher );
5315
+ }
5316
+ }
5317
+
5318
+ return elementMatcher( matchers );
5319
+ }
5320
+
5321
+ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
5322
+ // A counter to specify which element is currently being matched
5323
+ var matcherCachedRuns = 0,
5324
+ bySet = setMatchers.length > 0,
5325
+ byElement = elementMatchers.length > 0,
5326
+ superMatcher = function( seed, context, xml, results, expandContext ) {
5327
+ var elem, j, matcher,
5328
+ setMatched = [],
5329
+ matchedCount = 0,
5330
+ i = "0",
5331
+ unmatched = seed && [],
5332
+ outermost = expandContext != null,
5333
+ contextBackup = outermostContext,
5334
+ // We must always have either seed elements or context
5335
+ elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
5336
+ // Use integer dirruns iff this is the outermost matcher
5337
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
5338
+
5339
+ if ( outermost ) {
5340
+ outermostContext = context !== document && context;
5341
+ cachedruns = matcherCachedRuns;
5342
+ }
5343
+
5344
+ // Add elements passing elementMatchers directly to results
5345
+ // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
5346
+ for ( ; (elem = elems[i]) != null; i++ ) {
5347
+ if ( byElement && elem ) {
5348
+ j = 0;
5349
+ while ( (matcher = elementMatchers[j++]) ) {
5350
+ if ( matcher( elem, context, xml ) ) {
5351
+ results.push( elem );
5352
+ break;
5353
+ }
5354
+ }
5355
+ if ( outermost ) {
5356
+ dirruns = dirrunsUnique;
5357
+ cachedruns = ++matcherCachedRuns;
5358
+ }
5359
+ }
5360
+
5361
+ // Track unmatched elements for set filters
5362
+ if ( bySet ) {
5363
+ // They will have gone through all possible matchers
5364
+ if ( (elem = !matcher && elem) ) {
5365
+ matchedCount--;
5366
+ }
5367
+
5368
+ // Lengthen the array for every element, matched or not
5369
+ if ( seed ) {
5370
+ unmatched.push( elem );
5371
+ }
5372
+ }
5373
+ }
5374
+
5375
+ // Apply set filters to unmatched elements
5376
+ matchedCount += i;
5377
+ if ( bySet && i !== matchedCount ) {
5378
+ j = 0;
5379
+ while ( (matcher = setMatchers[j++]) ) {
5380
+ matcher( unmatched, setMatched, context, xml );
5381
+ }
5382
+
5383
+ if ( seed ) {
5384
+ // Reintegrate element matches to eliminate the need for sorting
5385
+ if ( matchedCount > 0 ) {
5386
+ while ( i-- ) {
5387
+ if ( !(unmatched[i] || setMatched[i]) ) {
5388
+ setMatched[i] = pop.call( results );
5389
+ }
5390
+ }
5391
+ }
5392
+
5393
+ // Discard index placeholder values to get only actual matches
5394
+ setMatched = condense( setMatched );
5395
+ }
5396
+
5397
+ // Add matches to results
5398
+ push.apply( results, setMatched );
5399
+
5400
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
5401
+ if ( outermost && !seed && setMatched.length > 0 &&
5402
+ ( matchedCount + setMatchers.length ) > 1 ) {
5403
+
5404
+ Sizzle.uniqueSort( results );
5405
+ }
5406
+ }
5407
+
5408
+ // Override manipulation of globals by nested matchers
5409
+ if ( outermost ) {
5410
+ dirruns = dirrunsUnique;
5411
+ outermostContext = contextBackup;
5412
+ }
5413
+
5414
+ return unmatched;
5415
+ };
5416
+
5417
+ return bySet ?
5418
+ markFunction( superMatcher ) :
5419
+ superMatcher;
5420
+ }
5421
+
5422
+ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
5423
+ var i,
5424
+ setMatchers = [],
5425
+ elementMatchers = [],
5426
+ cached = compilerCache[ selector + " " ];
5427
+
5428
+ if ( !cached ) {
5429
+ // Generate a function of recursive functions that can be used to check each element
5430
+ if ( !group ) {
5431
+ group = tokenize( selector );
5432
+ }
5433
+ i = group.length;
5434
+ while ( i-- ) {
5435
+ cached = matcherFromTokens( group[i] );
5436
+ if ( cached[ expando ] ) {
5437
+ setMatchers.push( cached );
5438
+ } else {
5439
+ elementMatchers.push( cached );
5440
+ }
5441
+ }
5442
+
5443
+ // Cache the compiled function
5444
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
5445
+ }
5446
+ return cached;
5447
+ };
5448
+
5449
+ function multipleContexts( selector, contexts, results ) {
5450
+ var i = 0,
5451
+ len = contexts.length;
5452
+ for ( ; i < len; i++ ) {
5453
+ Sizzle( selector, contexts[i], results );
5454
+ }
5455
+ return results;
5456
+ }
5457
+
5458
+ function select( selector, context, results, seed ) {
5459
+ var i, tokens, token, type, find,
5460
+ match = tokenize( selector );
5461
+
5462
+ if ( !seed ) {
5463
+ // Try to minimize operations if there is only one group
5464
+ if ( match.length === 1 ) {
5465
+
5466
+ // Take a shortcut and set the context if the root selector is an ID
5467
+ tokens = match[0] = match[0].slice( 0 );
5468
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
5469
+ context.nodeType === 9 && !documentIsXML &&
5470
+ Expr.relative[ tokens[1].type ] ) {
5471
+
5472
+ context = Expr.find["ID"]( token.matches[0].replace( runescape, funescape ), context )[0];
5473
+ if ( !context ) {
5474
+ return results;
5475
+ }
5476
+
5477
+ selector = selector.slice( tokens.shift().value.length );
5478
+ }
5479
+
5480
+ // Fetch a seed set for right-to-left matching
5481
+ i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
5482
+ while ( i-- ) {
5483
+ token = tokens[i];
5484
+
5485
+ // Abort if we hit a combinator
5486
+ if ( Expr.relative[ (type = token.type) ] ) {
5487
+ break;
5488
+ }
5489
+ if ( (find = Expr.find[ type ]) ) {
5490
+ // Search, expanding context for leading sibling combinators
5491
+ if ( (seed = find(
5492
+ token.matches[0].replace( runescape, funescape ),
5493
+ rsibling.test( tokens[0].type ) && context.parentNode || context
5494
+ )) ) {
5495
+
5496
+ // If seed is empty or no tokens remain, we can return early
5497
+ tokens.splice( i, 1 );
5498
+ selector = seed.length && toSelector( tokens );
5499
+ if ( !selector ) {
5500
+ push.apply( results, slice.call( seed, 0 ) );
5501
+ return results;
5502
+ }
5503
+
5504
+ break;
5505
+ }
5506
+ }
5507
+ }
5508
+ }
5509
+ }
5510
+
5511
+ // Compile and execute a filtering function
5512
+ // Provide `match` to avoid retokenization if we modified the selector above
5513
+ compile( selector, match )(
5514
+ seed,
5515
+ context,
5516
+ documentIsXML,
5517
+ results,
5518
+ rsibling.test( selector )
5519
+ );
5520
+ return results;
5521
+ }
5522
+
5523
+ // Deprecated
5524
+ Expr.pseudos["nth"] = Expr.pseudos["eq"];
5525
+
5526
+ // Easy API for creating new setFilters
5527
+ function setFilters() {}
5528
+ Expr.filters = setFilters.prototype = Expr.pseudos;
5529
+ Expr.setFilters = new setFilters();
5530
+
5531
+ // Initialize with the default document
5532
+ setDocument();
5533
+
5534
+ // Override sizzle attribute retrieval
5535
+ Sizzle.attr = jQuery.attr;
5536
+ jQuery.find = Sizzle;
5537
+ jQuery.expr = Sizzle.selectors;
5538
+ jQuery.expr[":"] = jQuery.expr.pseudos;
5539
+ jQuery.unique = Sizzle.uniqueSort;
5540
+ jQuery.text = Sizzle.getText;
5541
+ jQuery.isXMLDoc = Sizzle.isXML;
5542
+ jQuery.contains = Sizzle.contains;
5543
+
5544
+
5545
+ })( window );
5546
+ var runtil = /Until$/,
5547
+ rparentsprev = /^(?:parents|prev(?:Until|All))/,
5548
+ isSimple = /^.[^:#\[\.,]*$/,
5549
+ rneedsContext = jQuery.expr.match.needsContext,
5550
+ // methods guaranteed to produce a unique set when starting from a unique set
5551
+ guaranteedUnique = {
5552
+ children: true,
5553
+ contents: true,
5554
+ next: true,
5555
+ prev: true
5556
+ };
5557
+
5558
+ jQuery.fn.extend({
5559
+ find: function( selector ) {
5560
+ var i, ret, self,
5561
+ len = this.length;
5562
+
5563
+ if ( typeof selector !== "string" ) {
5564
+ self = this;
5565
+ return this.pushStack( jQuery( selector ).filter(function() {
5566
+ for ( i = 0; i < len; i++ ) {
5567
+ if ( jQuery.contains( self[ i ], this ) ) {
5568
+ return true;
5569
+ }
5570
+ }
5571
+ }) );
5572
+ }
5573
+
5574
+ ret = [];
5575
+ for ( i = 0; i < len; i++ ) {
5576
+ jQuery.find( selector, this[ i ], ret );
5577
+ }
5578
+
5579
+ // Needed because $( selector, context ) becomes $( context ).find( selector )
5580
+ ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
5581
+ ret.selector = ( this.selector ? this.selector + " " : "" ) + selector;
5582
+ return ret;
5583
+ },
5584
+
5585
+ has: function( target ) {
5586
+ var i,
5587
+ targets = jQuery( target, this ),
5588
+ len = targets.length;
5589
+
5590
+ return this.filter(function() {
5591
+ for ( i = 0; i < len; i++ ) {
5592
+ if ( jQuery.contains( this, targets[i] ) ) {
5593
+ return true;
5594
+ }
5595
+ }
5596
+ });
5597
+ },
5598
+
5599
+ not: function( selector ) {
5600
+ return this.pushStack( winnow(this, selector, false) );
5601
+ },
5602
+
5603
+ filter: function( selector ) {
5604
+ return this.pushStack( winnow(this, selector, true) );
5605
+ },
5606
+
5607
+ is: function( selector ) {
5608
+ return !!selector && (
5609
+ typeof selector === "string" ?
5610
+ // If this is a positional/relative selector, check membership in the returned set
5611
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
5612
+ rneedsContext.test( selector ) ?
5613
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
5614
+ jQuery.filter( selector, this ).length > 0 :
5615
+ this.filter( selector ).length > 0 );
5616
+ },
5617
+
5618
+ closest: function( selectors, context ) {
5619
+ var cur,
5620
+ i = 0,
5621
+ l = this.length,
5622
+ ret = [],
5623
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
5624
+ jQuery( selectors, context || this.context ) :
5625
+ 0;
5626
+
5627
+ for ( ; i < l; i++ ) {
5628
+ cur = this[i];
5629
+
5630
+ while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
5631
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
5632
+ ret.push( cur );
5633
+ break;
5634
+ }
5635
+ cur = cur.parentNode;
5636
+ }
5637
+ }
5638
+
5639
+ return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );
5640
+ },
5641
+
5642
+ // Determine the position of an element within
5643
+ // the matched set of elements
5644
+ index: function( elem ) {
5645
+
5646
+ // No argument, return index in parent
5647
+ if ( !elem ) {
5648
+ return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
5649
+ }
5650
+
5651
+ // index in selector
5652
+ if ( typeof elem === "string" ) {
5653
+ return jQuery.inArray( this[0], jQuery( elem ) );
5654
+ }
5655
+
5656
+ // Locate the position of the desired element
5657
+ return jQuery.inArray(
5658
+ // If it receives a jQuery object, the first element is used
5659
+ elem.jquery ? elem[0] : elem, this );
5660
+ },
5661
+
5662
+ add: function( selector, context ) {
5663
+ var set = typeof selector === "string" ?
5664
+ jQuery( selector, context ) :
5665
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
5666
+ all = jQuery.merge( this.get(), set );
5667
+
5668
+ return this.pushStack( jQuery.unique(all) );
5669
+ },
5670
+
5671
+ addBack: function( selector ) {
5672
+ return this.add( selector == null ?
5673
+ this.prevObject : this.prevObject.filter(selector)
5674
+ );
5675
+ }
5676
+ });
5677
+
5678
+ jQuery.fn.andSelf = jQuery.fn.addBack;
5679
+
5680
+ function sibling( cur, dir ) {
5681
+ do {
5682
+ cur = cur[ dir ];
5683
+ } while ( cur && cur.nodeType !== 1 );
5684
+
5685
+ return cur;
5686
+ }
5687
+
5688
+ jQuery.each({
5689
+ parent: function( elem ) {
5690
+ var parent = elem.parentNode;
5691
+ return parent && parent.nodeType !== 11 ? parent : null;
5692
+ },
5693
+ parents: function( elem ) {
5694
+ return jQuery.dir( elem, "parentNode" );
5695
+ },
5696
+ parentsUntil: function( elem, i, until ) {
5697
+ return jQuery.dir( elem, "parentNode", until );
5698
+ },
5699
+ next: function( elem ) {
5700
+ return sibling( elem, "nextSibling" );
5701
+ },
5702
+ prev: function( elem ) {
5703
+ return sibling( elem, "previousSibling" );
5704
+ },
5705
+ nextAll: function( elem ) {
5706
+ return jQuery.dir( elem, "nextSibling" );
5707
+ },
5708
+ prevAll: function( elem ) {
5709
+ return jQuery.dir( elem, "previousSibling" );
5710
+ },
5711
+ nextUntil: function( elem, i, until ) {
5712
+ return jQuery.dir( elem, "nextSibling", until );
5713
+ },
5714
+ prevUntil: function( elem, i, until ) {
5715
+ return jQuery.dir( elem, "previousSibling", until );
5716
+ },
5717
+ siblings: function( elem ) {
5718
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
5719
+ },
5720
+ children: function( elem ) {
5721
+ return jQuery.sibling( elem.firstChild );
5722
+ },
5723
+ contents: function( elem ) {
5724
+ return jQuery.nodeName( elem, "iframe" ) ?
5725
+ elem.contentDocument || elem.contentWindow.document :
5726
+ jQuery.merge( [], elem.childNodes );
5727
+ }
5728
+ }, function( name, fn ) {
5729
+ jQuery.fn[ name ] = function( until, selector ) {
5730
+ var ret = jQuery.map( this, fn, until );
5731
+
5732
+ if ( !runtil.test( name ) ) {
5733
+ selector = until;
5734
+ }
5735
+
5736
+ if ( selector && typeof selector === "string" ) {
5737
+ ret = jQuery.filter( selector, ret );
5738
+ }
5739
+
5740
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
5741
+
5742
+ if ( this.length > 1 && rparentsprev.test( name ) ) {
5743
+ ret = ret.reverse();
5744
+ }
5745
+
5746
+ return this.pushStack( ret );
5747
+ };
5748
+ });
5749
+
5750
+ jQuery.extend({
5751
+ filter: function( expr, elems, not ) {
5752
+ if ( not ) {
5753
+ expr = ":not(" + expr + ")";
5754
+ }
5755
+
5756
+ return elems.length === 1 ?
5757
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
5758
+ jQuery.find.matches(expr, elems);
5759
+ },
5760
+
5761
+ dir: function( elem, dir, until ) {
5762
+ var matched = [],
5763
+ cur = elem[ dir ];
5764
+
5765
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
5766
+ if ( cur.nodeType === 1 ) {
5767
+ matched.push( cur );
5768
+ }
5769
+ cur = cur[dir];
5770
+ }
5771
+ return matched;
5772
+ },
5773
+
5774
+ sibling: function( n, elem ) {
5775
+ var r = [];
5776
+
5777
+ for ( ; n; n = n.nextSibling ) {
5778
+ if ( n.nodeType === 1 && n !== elem ) {
5779
+ r.push( n );
5780
+ }
5781
+ }
5782
+
5783
+ return r;
5784
+ }
5785
+ });
5786
+
5787
+ // Implement the identical functionality for filter and not
5788
+ function winnow( elements, qualifier, keep ) {
5789
+
5790
+ // Can't pass null or undefined to indexOf in Firefox 4
5791
+ // Set to 0 to skip string check
5792
+ qualifier = qualifier || 0;
5793
+
5794
+ if ( jQuery.isFunction( qualifier ) ) {
5795
+ return jQuery.grep(elements, function( elem, i ) {
5796
+ var retVal = !!qualifier.call( elem, i, elem );
5797
+ return retVal === keep;
5798
+ });
5799
+
5800
+ } else if ( qualifier.nodeType ) {
5801
+ return jQuery.grep(elements, function( elem ) {
5802
+ return ( elem === qualifier ) === keep;
5803
+ });
5804
+
5805
+ } else if ( typeof qualifier === "string" ) {
5806
+ var filtered = jQuery.grep(elements, function( elem ) {
5807
+ return elem.nodeType === 1;
5808
+ });
5809
+
5810
+ if ( isSimple.test( qualifier ) ) {
5811
+ return jQuery.filter(qualifier, filtered, !keep);
5812
+ } else {
5813
+ qualifier = jQuery.filter( qualifier, filtered );
5814
+ }
5815
+ }
5816
+
5817
+ return jQuery.grep(elements, function( elem ) {
5818
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
5819
+ });
5820
+ }
5821
+ function createSafeFragment( document ) {
5822
+ var list = nodeNames.split( "|" ),
5823
+ safeFrag = document.createDocumentFragment();
5824
+
5825
+ if ( safeFrag.createElement ) {
5826
+ while ( list.length ) {
5827
+ safeFrag.createElement(
5828
+ list.pop()
5829
+ );
5830
+ }
5831
+ }
5832
+ return safeFrag;
5833
+ }
5834
+
5835
+ var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
5836
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
5837
+ rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
5838
+ rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
5839
+ rleadingWhitespace = /^\s+/,
5840
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
5841
+ rtagName = /<([\w:]+)/,
5842
+ rtbody = /<tbody/i,
5843
+ rhtml = /<|&#?\w+;/,
5844
+ rnoInnerhtml = /<(?:script|style|link)/i,
5845
+ manipulation_rcheckableType = /^(?:checkbox|radio)$/i,
5846
+ // checked="checked" or checked
5847
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
5848
+ rscriptType = /^$|\/(?:java|ecma)script/i,
5849
+ rscriptTypeMasked = /^true\/(.*)/,
5850
+ rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
5851
+
5852
+ // We have to close these tags to support XHTML (#13200)
5853
+ wrapMap = {
5854
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
5855
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
5856
+ area: [ 1, "<map>", "</map>" ],
5857
+ param: [ 1, "<object>", "</object>" ],
5858
+ thead: [ 1, "<table>", "</table>" ],
5859
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
5860
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
5861
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
5862
+
5863
+ // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
5864
+ // unless wrapped in a div with non-breaking characters in front of it.
5865
+ _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
5866
+ },
5867
+ safeFragment = createSafeFragment( document ),
5868
+ fragmentDiv = safeFragment.appendChild( document.createElement("div") );
5869
+
5870
+ wrapMap.optgroup = wrapMap.option;
5871
+ wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
5872
+ wrapMap.th = wrapMap.td;
5873
+
5874
+ jQuery.fn.extend({
5875
+ text: function( value ) {
5876
+ return jQuery.access( this, function( value ) {
5877
+ return value === undefined ?
5878
+ jQuery.text( this ) :
5879
+ this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
5880
+ }, null, value, arguments.length );
5881
+ },
5882
+
5883
+ wrapAll: function( html ) {
5884
+ if ( jQuery.isFunction( html ) ) {
5885
+ return this.each(function(i) {
5886
+ jQuery(this).wrapAll( html.call(this, i) );
5887
+ });
5888
+ }
5889
+
5890
+ if ( this[0] ) {
5891
+ // The elements to wrap the target around
5892
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
5893
+
5894
+ if ( this[0].parentNode ) {
5895
+ wrap.insertBefore( this[0] );
5896
+ }
5897
+
5898
+ wrap.map(function() {
5899
+ var elem = this;
5900
+
5901
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
5902
+ elem = elem.firstChild;
5903
+ }
5904
+
5905
+ return elem;
5906
+ }).append( this );
5907
+ }
5908
+
5909
+ return this;
5910
+ },
5911
+
5912
+ wrapInner: function( html ) {
5913
+ if ( jQuery.isFunction( html ) ) {
5914
+ return this.each(function(i) {
5915
+ jQuery(this).wrapInner( html.call(this, i) );
5916
+ });
5917
+ }
5918
+
5919
+ return this.each(function() {
5920
+ var self = jQuery( this ),
5921
+ contents = self.contents();
5922
+
5923
+ if ( contents.length ) {
5924
+ contents.wrapAll( html );
5925
+
5926
+ } else {
5927
+ self.append( html );
5928
+ }
5929
+ });
5930
+ },
5931
+
5932
+ wrap: function( html ) {
5933
+ var isFunction = jQuery.isFunction( html );
5934
+
5935
+ return this.each(function(i) {
5936
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
5937
+ });
5938
+ },
5939
+
5940
+ unwrap: function() {
5941
+ return this.parent().each(function() {
5942
+ if ( !jQuery.nodeName( this, "body" ) ) {
5943
+ jQuery( this ).replaceWith( this.childNodes );
5944
+ }
5945
+ }).end();
5946
+ },
5947
+
5948
+ append: function() {
5949
+ return this.domManip(arguments, true, function( elem ) {
5950
+ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5951
+ this.appendChild( elem );
5952
+ }
5953
+ });
5954
+ },
5955
+
5956
+ prepend: function() {
5957
+ return this.domManip(arguments, true, function( elem ) {
5958
+ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5959
+ this.insertBefore( elem, this.firstChild );
5960
+ }
5961
+ });
5962
+ },
5963
+
5964
+ before: function() {
5965
+ return this.domManip( arguments, false, function( elem ) {
5966
+ if ( this.parentNode ) {
5967
+ this.parentNode.insertBefore( elem, this );
5968
+ }
5969
+ });
5970
+ },
5971
+
5972
+ after: function() {
5973
+ return this.domManip( arguments, false, function( elem ) {
5974
+ if ( this.parentNode ) {
5975
+ this.parentNode.insertBefore( elem, this.nextSibling );
5976
+ }
5977
+ });
5978
+ },
5979
+
5980
+ // keepData is for internal use only--do not document
5981
+ remove: function( selector, keepData ) {
5982
+ var elem,
5983
+ i = 0;
5984
+
5985
+ for ( ; (elem = this[i]) != null; i++ ) {
5986
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) {
5987
+ if ( !keepData && elem.nodeType === 1 ) {
5988
+ jQuery.cleanData( getAll( elem ) );
5989
+ }
5990
+
5991
+ if ( elem.parentNode ) {
5992
+ if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
5993
+ setGlobalEval( getAll( elem, "script" ) );
5994
+ }
5995
+ elem.parentNode.removeChild( elem );
5996
+ }
5997
+ }
5998
+ }
5999
+
6000
+ return this;
6001
+ },
6002
+
6003
+ empty: function() {
6004
+ var elem,
6005
+ i = 0;
6006
+
6007
+ for ( ; (elem = this[i]) != null; i++ ) {
6008
+ // Remove element nodes and prevent memory leaks
6009
+ if ( elem.nodeType === 1 ) {
6010
+ jQuery.cleanData( getAll( elem, false ) );
6011
+ }
6012
+
6013
+ // Remove any remaining nodes
6014
+ while ( elem.firstChild ) {
6015
+ elem.removeChild( elem.firstChild );
6016
+ }
6017
+
6018
+ // If this is a select, ensure that it displays empty (#12336)
6019
+ // Support: IE<9
6020
+ if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
6021
+ elem.options.length = 0;
6022
+ }
6023
+ }
6024
+
6025
+ return this;
6026
+ },
6027
+
6028
+ clone: function( dataAndEvents, deepDataAndEvents ) {
6029
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
6030
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
6031
+
6032
+ return this.map( function () {
6033
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
6034
+ });
6035
+ },
6036
+
6037
+ html: function( value ) {
6038
+ return jQuery.access( this, function( value ) {
6039
+ var elem = this[0] || {},
6040
+ i = 0,
6041
+ l = this.length;
6042
+
6043
+ if ( value === undefined ) {
6044
+ return elem.nodeType === 1 ?
6045
+ elem.innerHTML.replace( rinlinejQuery, "" ) :
6046
+ undefined;
6047
+ }
6048
+
6049
+ // See if we can take a shortcut and just use innerHTML
6050
+ if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
6051
+ ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
6052
+ ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
6053
+ !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
6054
+
6055
+ value = value.replace( rxhtmlTag, "<$1></$2>" );
6056
+
6057
+ try {
6058
+ for (; i < l; i++ ) {
6059
+ // Remove element nodes and prevent memory leaks
6060
+ elem = this[i] || {};
6061
+ if ( elem.nodeType === 1 ) {
6062
+ jQuery.cleanData( getAll( elem, false ) );
6063
+ elem.innerHTML = value;
6064
+ }
6065
+ }
6066
+
6067
+ elem = 0;
6068
+
6069
+ // If using innerHTML throws an exception, use the fallback method
6070
+ } catch(e) {}
6071
+ }
6072
+
6073
+ if ( elem ) {
6074
+ this.empty().append( value );
6075
+ }
6076
+ }, null, value, arguments.length );
6077
+ },
6078
+
6079
+ replaceWith: function( value ) {
6080
+ var isFunc = jQuery.isFunction( value );
6081
+
6082
+ // Make sure that the elements are removed from the DOM before they are inserted
6083
+ // this can help fix replacing a parent with child elements
6084
+ if ( !isFunc && typeof value !== "string" ) {
6085
+ value = jQuery( value ).not( this ).detach();
6086
+ }
6087
+
6088
+ return this.domManip( [ value ], true, function( elem ) {
6089
+ var next = this.nextSibling,
6090
+ parent = this.parentNode;
6091
+
6092
+ if ( parent ) {
6093
+ jQuery( this ).remove();
6094
+ parent.insertBefore( elem, next );
6095
+ }
6096
+ });
6097
+ },
6098
+
6099
+ detach: function( selector ) {
6100
+ return this.remove( selector, true );
6101
+ },
6102
+
6103
+ domManip: function( args, table, callback ) {
6104
+
6105
+ // Flatten any nested arrays
6106
+ args = core_concat.apply( [], args );
6107
+
6108
+ var first, node, hasScripts,
6109
+ scripts, doc, fragment,
6110
+ i = 0,
6111
+ l = this.length,
6112
+ set = this,
6113
+ iNoClone = l - 1,
6114
+ value = args[0],
6115
+ isFunction = jQuery.isFunction( value );
6116
+
6117
+ // We can't cloneNode fragments that contain checked, in WebKit
6118
+ if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
6119
+ return this.each(function( index ) {
6120
+ var self = set.eq( index );
6121
+ if ( isFunction ) {
6122
+ args[0] = value.call( this, index, table ? self.html() : undefined );
6123
+ }
6124
+ self.domManip( args, table, callback );
6125
+ });
6126
+ }
6127
+
6128
+ if ( l ) {
6129
+ fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
6130
+ first = fragment.firstChild;
6131
+
6132
+ if ( fragment.childNodes.length === 1 ) {
6133
+ fragment = first;
6134
+ }
6135
+
6136
+ if ( first ) {
6137
+ table = table && jQuery.nodeName( first, "tr" );
6138
+ scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
6139
+ hasScripts = scripts.length;
6140
+
6141
+ // Use the original fragment for the last item instead of the first because it can end up
6142
+ // being emptied incorrectly in certain situations (#8070).
6143
+ for ( ; i < l; i++ ) {
6144
+ node = fragment;
6145
+
6146
+ if ( i !== iNoClone ) {
6147
+ node = jQuery.clone( node, true, true );
6148
+
6149
+ // Keep references to cloned scripts for later restoration
6150
+ if ( hasScripts ) {
6151
+ jQuery.merge( scripts, getAll( node, "script" ) );
6152
+ }
6153
+ }
6154
+
6155
+ callback.call(
6156
+ table && jQuery.nodeName( this[i], "table" ) ?
6157
+ findOrAppend( this[i], "tbody" ) :
6158
+ this[i],
6159
+ node,
6160
+ i
6161
+ );
6162
+ }
6163
+
6164
+ if ( hasScripts ) {
6165
+ doc = scripts[ scripts.length - 1 ].ownerDocument;
6166
+
6167
+ // Reenable scripts
6168
+ jQuery.map( scripts, restoreScript );
6169
+
6170
+ // Evaluate executable scripts on first document insertion
6171
+ for ( i = 0; i < hasScripts; i++ ) {
6172
+ node = scripts[ i ];
6173
+ if ( rscriptType.test( node.type || "" ) &&
6174
+ !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
6175
+
6176
+ if ( node.src ) {
6177
+ // Hope ajax is available...
6178
+ jQuery.ajax({
6179
+ url: node.src,
6180
+ type: "GET",
6181
+ dataType: "script",
6182
+ async: false,
6183
+ global: false,
6184
+ "throws": true
6185
+ });
6186
+ } else {
6187
+ jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
6188
+ }
6189
+ }
6190
+ }
6191
+ }
6192
+
6193
+ // Fix #11809: Avoid leaking memory
6194
+ fragment = first = null;
6195
+ }
6196
+ }
6197
+
6198
+ return this;
6199
+ }
6200
+ });
6201
+
6202
+ function findOrAppend( elem, tag ) {
6203
+ return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
6204
+ }
6205
+
6206
+ // Replace/restore the type attribute of script elements for safe DOM manipulation
6207
+ function disableScript( elem ) {
6208
+ var attr = elem.getAttributeNode("type");
6209
+ elem.type = ( attr && attr.specified ) + "/" + elem.type;
6210
+ return elem;
6211
+ }
6212
+ function restoreScript( elem ) {
6213
+ var match = rscriptTypeMasked.exec( elem.type );
6214
+ if ( match ) {
6215
+ elem.type = match[1];
6216
+ } else {
6217
+ elem.removeAttribute("type");
6218
+ }
6219
+ return elem;
6220
+ }
6221
+
6222
+ // Mark scripts as having already been evaluated
6223
+ function setGlobalEval( elems, refElements ) {
6224
+ var elem,
6225
+ i = 0;
6226
+ for ( ; (elem = elems[i]) != null; i++ ) {
6227
+ jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
6228
+ }
6229
+ }
6230
+
6231
+ function cloneCopyEvent( src, dest ) {
6232
+
6233
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
6234
+ return;
6235
+ }
6236
+
6237
+ var type, i, l,
6238
+ oldData = jQuery._data( src ),
6239
+ curData = jQuery._data( dest, oldData ),
6240
+ events = oldData.events;
6241
+
6242
+ if ( events ) {
6243
+ delete curData.handle;
6244
+ curData.events = {};
6245
+
6246
+ for ( type in events ) {
6247
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
6248
+ jQuery.event.add( dest, type, events[ type ][ i ] );
6249
+ }
6250
+ }
6251
+ }
6252
+
6253
+ // make the cloned public data object a copy from the original
6254
+ if ( curData.data ) {
6255
+ curData.data = jQuery.extend( {}, curData.data );
6256
+ }
6257
+ }
6258
+
6259
+ function fixCloneNodeIssues( src, dest ) {
6260
+ var nodeName, e, data;
6261
+
6262
+ // We do not need to do anything for non-Elements
6263
+ if ( dest.nodeType !== 1 ) {
6264
+ return;
6265
+ }
6266
+
6267
+ nodeName = dest.nodeName.toLowerCase();
6268
+
6269
+ // IE6-8 copies events bound via attachEvent when using cloneNode.
6270
+ if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {
6271
+ data = jQuery._data( dest );
6272
+
6273
+ for ( e in data.events ) {
6274
+ jQuery.removeEvent( dest, e, data.handle );
6275
+ }
6276
+
6277
+ // Event data gets referenced instead of copied if the expando gets copied too
6278
+ dest.removeAttribute( jQuery.expando );
6279
+ }
6280
+
6281
+ // IE blanks contents when cloning scripts, and tries to evaluate newly-set text
6282
+ if ( nodeName === "script" && dest.text !== src.text ) {
6283
+ disableScript( dest ).text = src.text;
6284
+ restoreScript( dest );
6285
+
6286
+ // IE6-10 improperly clones children of object elements using classid.
6287
+ // IE10 throws NoModificationAllowedError if parent is null, #12132.
6288
+ } else if ( nodeName === "object" ) {
6289
+ if ( dest.parentNode ) {
6290
+ dest.outerHTML = src.outerHTML;
6291
+ }
6292
+
6293
+ // This path appears unavoidable for IE9. When cloning an object
6294
+ // element in IE9, the outerHTML strategy above is not sufficient.
6295
+ // If the src has innerHTML and the destination does not,
6296
+ // copy the src.innerHTML into the dest.innerHTML. #10324
6297
+ if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
6298
+ dest.innerHTML = src.innerHTML;
6299
+ }
6300
+
6301
+ } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
6302
+ // IE6-8 fails to persist the checked state of a cloned checkbox
6303
+ // or radio button. Worse, IE6-7 fail to give the cloned element
6304
+ // a checked appearance if the defaultChecked value isn't also set
6305
+
6306
+ dest.defaultChecked = dest.checked = src.checked;
6307
+
6308
+ // IE6-7 get confused and end up setting the value of a cloned
6309
+ // checkbox/radio button to an empty string instead of "on"
6310
+ if ( dest.value !== src.value ) {
6311
+ dest.value = src.value;
6312
+ }
6313
+
6314
+ // IE6-8 fails to return the selected option to the default selected
6315
+ // state when cloning options
6316
+ } else if ( nodeName === "option" ) {
6317
+ dest.defaultSelected = dest.selected = src.defaultSelected;
6318
+
6319
+ // IE6-8 fails to set the defaultValue to the correct value when
6320
+ // cloning other types of input fields
6321
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
6322
+ dest.defaultValue = src.defaultValue;
6323
+ }
6324
+ }
6325
+
6326
+ jQuery.each({
6327
+ appendTo: "append",
6328
+ prependTo: "prepend",
6329
+ insertBefore: "before",
6330
+ insertAfter: "after",
6331
+ replaceAll: "replaceWith"
6332
+ }, function( name, original ) {
6333
+ jQuery.fn[ name ] = function( selector ) {
6334
+ var elems,
6335
+ i = 0,
6336
+ ret = [],
6337
+ insert = jQuery( selector ),
6338
+ last = insert.length - 1;
6339
+
6340
+ for ( ; i <= last; i++ ) {
6341
+ elems = i === last ? this : this.clone(true);
6342
+ jQuery( insert[i] )[ original ]( elems );
6343
+
6344
+ // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
6345
+ core_push.apply( ret, elems.get() );
6346
+ }
6347
+
6348
+ return this.pushStack( ret );
6349
+ };
6350
+ });
6351
+
6352
+ function getAll( context, tag ) {
6353
+ var elems, elem,
6354
+ i = 0,
6355
+ found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) :
6356
+ typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) :
6357
+ undefined;
6358
+
6359
+ if ( !found ) {
6360
+ for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
6361
+ if ( !tag || jQuery.nodeName( elem, tag ) ) {
6362
+ found.push( elem );
6363
+ } else {
6364
+ jQuery.merge( found, getAll( elem, tag ) );
6365
+ }
6366
+ }
6367
+ }
6368
+
6369
+ return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
6370
+ jQuery.merge( [ context ], found ) :
6371
+ found;
6372
+ }
6373
+
6374
+ // Used in buildFragment, fixes the defaultChecked property
6375
+ function fixDefaultChecked( elem ) {
6376
+ if ( manipulation_rcheckableType.test( elem.type ) ) {
6377
+ elem.defaultChecked = elem.checked;
6378
+ }
6379
+ }
6380
+
6381
+ jQuery.extend({
6382
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
6383
+ var destElements, node, clone, i, srcElements,
6384
+ inPage = jQuery.contains( elem.ownerDocument, elem );
6385
+
6386
+ if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
6387
+ clone = elem.cloneNode( true );
6388
+
6389
+ // IE<=8 does not properly clone detached, unknown element nodes
6390
+ } else {
6391
+ fragmentDiv.innerHTML = elem.outerHTML;
6392
+ fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
6393
+ }
6394
+
6395
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
6396
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
6397
+
6398
+ // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
6399
+ destElements = getAll( clone );
6400
+ srcElements = getAll( elem );
6401
+
6402
+ // Fix all IE cloning issues
6403
+ for ( i = 0; (node = srcElements[i]) != null; ++i ) {
6404
+ // Ensure that the destination node is not null; Fixes #9587
6405
+ if ( destElements[i] ) {
6406
+ fixCloneNodeIssues( node, destElements[i] );
6407
+ }
6408
+ }
6409
+ }
6410
+
6411
+ // Copy the events from the original to the clone
6412
+ if ( dataAndEvents ) {
6413
+ if ( deepDataAndEvents ) {
6414
+ srcElements = srcElements || getAll( elem );
6415
+ destElements = destElements || getAll( clone );
6416
+
6417
+ for ( i = 0; (node = srcElements[i]) != null; i++ ) {
6418
+ cloneCopyEvent( node, destElements[i] );
6419
+ }
6420
+ } else {
6421
+ cloneCopyEvent( elem, clone );
6422
+ }
6423
+ }
6424
+
6425
+ // Preserve script evaluation history
6426
+ destElements = getAll( clone, "script" );
6427
+ if ( destElements.length > 0 ) {
6428
+ setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
6429
+ }
6430
+
6431
+ destElements = srcElements = node = null;
6432
+
6433
+ // Return the cloned set
6434
+ return clone;
6435
+ },
6436
+
6437
+ buildFragment: function( elems, context, scripts, selection ) {
6438
+ var j, elem, contains,
6439
+ tmp, tag, tbody, wrap,
6440
+ l = elems.length,
6441
+
6442
+ // Ensure a safe fragment
6443
+ safe = createSafeFragment( context ),
6444
+
6445
+ nodes = [],
6446
+ i = 0;
6447
+
6448
+ for ( ; i < l; i++ ) {
6449
+ elem = elems[ i ];
6450
+
6451
+ if ( elem || elem === 0 ) {
6452
+
6453
+ // Add nodes directly
6454
+ if ( jQuery.type( elem ) === "object" ) {
6455
+ jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
6456
+
6457
+ // Convert non-html into a text node
6458
+ } else if ( !rhtml.test( elem ) ) {
6459
+ nodes.push( context.createTextNode( elem ) );
6460
+
6461
+ // Convert html into DOM nodes
6462
+ } else {
6463
+ tmp = tmp || safe.appendChild( context.createElement("div") );
6464
+
6465
+ // Deserialize a standard representation
6466
+ tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
6467
+ wrap = wrapMap[ tag ] || wrapMap._default;
6468
+
6469
+ tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
6470
+
6471
+ // Descend through wrappers to the right content
6472
+ j = wrap[0];
6473
+ while ( j-- ) {
6474
+ tmp = tmp.lastChild;
6475
+ }
6476
+
6477
+ // Manually add leading whitespace removed by IE
6478
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
6479
+ nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
6480
+ }
6481
+
6482
+ // Remove IE's autoinserted <tbody> from table fragments
6483
+ if ( !jQuery.support.tbody ) {
6484
+
6485
+ // String was a <table>, *may* have spurious <tbody>
6486
+ elem = tag === "table" && !rtbody.test( elem ) ?
6487
+ tmp.firstChild :
6488
+
6489
+ // String was a bare <thead> or <tfoot>
6490
+ wrap[1] === "<table>" && !rtbody.test( elem ) ?
6491
+ tmp :
6492
+ 0;
6493
+
6494
+ j = elem && elem.childNodes.length;
6495
+ while ( j-- ) {
6496
+ if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
6497
+ elem.removeChild( tbody );
6498
+ }
6499
+ }
6500
+ }
6501
+
6502
+ jQuery.merge( nodes, tmp.childNodes );
6503
+
6504
+ // Fix #12392 for WebKit and IE > 9
6505
+ tmp.textContent = "";
6506
+
6507
+ // Fix #12392 for oldIE
6508
+ while ( tmp.firstChild ) {
6509
+ tmp.removeChild( tmp.firstChild );
6510
+ }
6511
+
6512
+ // Remember the top-level container for proper cleanup
6513
+ tmp = safe.lastChild;
6514
+ }
6515
+ }
6516
+ }
6517
+
6518
+ // Fix #11356: Clear elements from fragment
6519
+ if ( tmp ) {
6520
+ safe.removeChild( tmp );
6521
+ }
6522
+
6523
+ // Reset defaultChecked for any radios and checkboxes
6524
+ // about to be appended to the DOM in IE 6/7 (#8060)
6525
+ if ( !jQuery.support.appendChecked ) {
6526
+ jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
6527
+ }
6528
+
6529
+ i = 0;
6530
+ while ( (elem = nodes[ i++ ]) ) {
6531
+
6532
+ // #4087 - If origin and destination elements are the same, and this is
6533
+ // that element, do not do anything
6534
+ if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
6535
+ continue;
6536
+ }
6537
+
6538
+ contains = jQuery.contains( elem.ownerDocument, elem );
6539
+
6540
+ // Append to fragment
6541
+ tmp = getAll( safe.appendChild( elem ), "script" );
6542
+
6543
+ // Preserve script evaluation history
6544
+ if ( contains ) {
6545
+ setGlobalEval( tmp );
6546
+ }
6547
+
6548
+ // Capture executables
6549
+ if ( scripts ) {
6550
+ j = 0;
6551
+ while ( (elem = tmp[ j++ ]) ) {
6552
+ if ( rscriptType.test( elem.type || "" ) ) {
6553
+ scripts.push( elem );
6554
+ }
6555
+ }
6556
+ }
6557
+ }
6558
+
6559
+ tmp = null;
6560
+
6561
+ return safe;
6562
+ },
6563
+
6564
+ cleanData: function( elems, /* internal */ acceptData ) {
6565
+ var elem, type, id, data,
6566
+ i = 0,
6567
+ internalKey = jQuery.expando,
6568
+ cache = jQuery.cache,
6569
+ deleteExpando = jQuery.support.deleteExpando,
6570
+ special = jQuery.event.special;
6571
+
6572
+ for ( ; (elem = elems[i]) != null; i++ ) {
6573
+
6574
+ if ( acceptData || jQuery.acceptData( elem ) ) {
6575
+
6576
+ id = elem[ internalKey ];
6577
+ data = id && cache[ id ];
6578
+
6579
+ if ( data ) {
6580
+ if ( data.events ) {
6581
+ for ( type in data.events ) {
6582
+ if ( special[ type ] ) {
6583
+ jQuery.event.remove( elem, type );
6584
+
6585
+ // This is a shortcut to avoid jQuery.event.remove's overhead
6586
+ } else {
6587
+ jQuery.removeEvent( elem, type, data.handle );
6588
+ }
6589
+ }
6590
+ }
6591
+
6592
+ // Remove cache only if it was not already removed by jQuery.event.remove
6593
+ if ( cache[ id ] ) {
6594
+
6595
+ delete cache[ id ];
6596
+
6597
+ // IE does not allow us to delete expando properties from nodes,
6598
+ // nor does it have a removeAttribute function on Document nodes;
6599
+ // we must handle all of these cases
6600
+ if ( deleteExpando ) {
6601
+ delete elem[ internalKey ];
6602
+
6603
+ } else if ( typeof elem.removeAttribute !== core_strundefined ) {
6604
+ elem.removeAttribute( internalKey );
6605
+
6606
+ } else {
6607
+ elem[ internalKey ] = null;
6608
+ }
6609
+
6610
+ core_deletedIds.push( id );
6611
+ }
6612
+ }
6613
+ }
6614
+ }
6615
+ }
6616
+ });
6617
+ var iframe, getStyles, curCSS,
6618
+ ralpha = /alpha\([^)]*\)/i,
6619
+ ropacity = /opacity\s*=\s*([^)]*)/,
6620
+ rposition = /^(top|right|bottom|left)$/,
6621
+ // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
6622
+ // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
6623
+ rdisplayswap = /^(none|table(?!-c[ea]).+)/,
6624
+ rmargin = /^margin/,
6625
+ rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
6626
+ rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
6627
+ rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
6628
+ elemdisplay = { BODY: "block" },
6629
+
6630
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
6631
+ cssNormalTransform = {
6632
+ letterSpacing: 0,
6633
+ fontWeight: 400
6634
+ },
6635
+
6636
+ cssExpand = [ "Top", "Right", "Bottom", "Left" ],
6637
+ cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
6638
+
6639
+ // return a css property mapped to a potentially vendor prefixed property
6640
+ function vendorPropName( style, name ) {
6641
+
6642
+ // shortcut for names that are not vendor prefixed
6643
+ if ( name in style ) {
6644
+ return name;
6645
+ }
6646
+
6647
+ // check for vendor prefixed names
6648
+ var capName = name.charAt(0).toUpperCase() + name.slice(1),
6649
+ origName = name,
6650
+ i = cssPrefixes.length;
6651
+
6652
+ while ( i-- ) {
6653
+ name = cssPrefixes[ i ] + capName;
6654
+ if ( name in style ) {
6655
+ return name;
6656
+ }
6657
+ }
6658
+
6659
+ return origName;
6660
+ }
6661
+
6662
+ function isHidden( elem, el ) {
6663
+ // isHidden might be called from jQuery#filter function;
6664
+ // in that case, element will be second argument
6665
+ elem = el || elem;
6666
+ return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
6667
+ }
6668
+
6669
+ function showHide( elements, show ) {
6670
+ var display, elem, hidden,
6671
+ values = [],
6672
+ index = 0,
6673
+ length = elements.length;
6674
+
6675
+ for ( ; index < length; index++ ) {
6676
+ elem = elements[ index ];
6677
+ if ( !elem.style ) {
6678
+ continue;
6679
+ }
6680
+
6681
+ values[ index ] = jQuery._data( elem, "olddisplay" );
6682
+ display = elem.style.display;
6683
+ if ( show ) {
6684
+ // Reset the inline display of this element to learn if it is
6685
+ // being hidden by cascaded rules or not
6686
+ if ( !values[ index ] && display === "none" ) {
6687
+ elem.style.display = "";
6688
+ }
6689
+
6690
+ // Set elements which have been overridden with display: none
6691
+ // in a stylesheet to whatever the default browser style is
6692
+ // for such an element
6693
+ if ( elem.style.display === "" && isHidden( elem ) ) {
6694
+ values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
6695
+ }
6696
+ } else {
6697
+
6698
+ if ( !values[ index ] ) {
6699
+ hidden = isHidden( elem );
6700
+
6701
+ if ( display && display !== "none" || !hidden ) {
6702
+ jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
6703
+ }
6704
+ }
6705
+ }
6706
+ }
6707
+
6708
+ // Set the display of most of the elements in a second loop
6709
+ // to avoid the constant reflow
6710
+ for ( index = 0; index < length; index++ ) {
6711
+ elem = elements[ index ];
6712
+ if ( !elem.style ) {
6713
+ continue;
6714
+ }
6715
+ if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
6716
+ elem.style.display = show ? values[ index ] || "" : "none";
6717
+ }
6718
+ }
6719
+
6720
+ return elements;
6721
+ }
6722
+
6723
+ jQuery.fn.extend({
6724
+ css: function( name, value ) {
6725
+ return jQuery.access( this, function( elem, name, value ) {
6726
+ var len, styles,
6727
+ map = {},
6728
+ i = 0;
6729
+
6730
+ if ( jQuery.isArray( name ) ) {
6731
+ styles = getStyles( elem );
6732
+ len = name.length;
6733
+
6734
+ for ( ; i < len; i++ ) {
6735
+ map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
6736
+ }
6737
+
6738
+ return map;
6739
+ }
6740
+
6741
+ return value !== undefined ?
6742
+ jQuery.style( elem, name, value ) :
6743
+ jQuery.css( elem, name );
6744
+ }, name, value, arguments.length > 1 );
6745
+ },
6746
+ show: function() {
6747
+ return showHide( this, true );
6748
+ },
6749
+ hide: function() {
6750
+ return showHide( this );
6751
+ },
6752
+ toggle: function( state ) {
6753
+ var bool = typeof state === "boolean";
6754
+
6755
+ return this.each(function() {
6756
+ if ( bool ? state : isHidden( this ) ) {
6757
+ jQuery( this ).show();
6758
+ } else {
6759
+ jQuery( this ).hide();
6760
+ }
6761
+ });
6762
+ }
6763
+ });
6764
+
6765
+ jQuery.extend({
6766
+ // Add in style property hooks for overriding the default
6767
+ // behavior of getting and setting a style property
6768
+ cssHooks: {
6769
+ opacity: {
6770
+ get: function( elem, computed ) {
6771
+ if ( computed ) {
6772
+ // We should always get a number back from opacity
6773
+ var ret = curCSS( elem, "opacity" );
6774
+ return ret === "" ? "1" : ret;
6775
+ }
6776
+ }
6777
+ }
6778
+ },
6779
+
6780
+ // Exclude the following css properties to add px
6781
+ cssNumber: {
6782
+ "columnCount": true,
6783
+ "fillOpacity": true,
6784
+ "fontWeight": true,
6785
+ "lineHeight": true,
6786
+ "opacity": true,
6787
+ "orphans": true,
6788
+ "widows": true,
6789
+ "zIndex": true,
6790
+ "zoom": true
6791
+ },
6792
+
6793
+ // Add in properties whose names you wish to fix before
6794
+ // setting or getting the value
6795
+ cssProps: {
6796
+ // normalize float css property
6797
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
6798
+ },
6799
+
6800
+ // Get and set the style property on a DOM Node
6801
+ style: function( elem, name, value, extra ) {
6802
+ // Don't set styles on text and comment nodes
6803
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
6804
+ return;
6805
+ }
6806
+
6807
+ // Make sure that we're working with the right name
6808
+ var ret, type, hooks,
6809
+ origName = jQuery.camelCase( name ),
6810
+ style = elem.style;
6811
+
6812
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
6813
+
6814
+ // gets hook for the prefixed version
6815
+ // followed by the unprefixed version
6816
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
6817
+
6818
+ // Check if we're setting a value
6819
+ if ( value !== undefined ) {
6820
+ type = typeof value;
6821
+
6822
+ // convert relative number strings (+= or -=) to relative numbers. #7345
6823
+ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
6824
+ value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
6825
+ // Fixes bug #9237
6826
+ type = "number";
6827
+ }
6828
+
6829
+ // Make sure that NaN and null values aren't set. See: #7116
6830
+ if ( value == null || type === "number" && isNaN( value ) ) {
6831
+ return;
6832
+ }
6833
+
6834
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
6835
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
6836
+ value += "px";
6837
+ }
6838
+
6839
+ // Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
6840
+ // but it would mean to define eight (for every problematic property) identical functions
6841
+ if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
6842
+ style[ name ] = "inherit";
6843
+ }
6844
+
6845
+ // If a hook was provided, use that value, otherwise just set the specified value
6846
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
6847
+
6848
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
6849
+ // Fixes bug #5509
6850
+ try {
6851
+ style[ name ] = value;
6852
+ } catch(e) {}
6853
+ }
6854
+
6855
+ } else {
6856
+ // If a hook was provided get the non-computed value from there
6857
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
6858
+ return ret;
6859
+ }
6860
+
6861
+ // Otherwise just get the value from the style object
6862
+ return style[ name ];
6863
+ }
6864
+ },
6865
+
6866
+ css: function( elem, name, extra, styles ) {
6867
+ var num, val, hooks,
6868
+ origName = jQuery.camelCase( name );
6869
+
6870
+ // Make sure that we're working with the right name
6871
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
6872
+
6873
+ // gets hook for the prefixed version
6874
+ // followed by the unprefixed version
6875
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
6876
+
6877
+ // If a hook was provided get the computed value from there
6878
+ if ( hooks && "get" in hooks ) {
6879
+ val = hooks.get( elem, true, extra );
6880
+ }
6881
+
6882
+ // Otherwise, if a way to get the computed value exists, use that
6883
+ if ( val === undefined ) {
6884
+ val = curCSS( elem, name, styles );
6885
+ }
6886
+
6887
+ //convert "normal" to computed value
6888
+ if ( val === "normal" && name in cssNormalTransform ) {
6889
+ val = cssNormalTransform[ name ];
6890
+ }
6891
+
6892
+ // Return, converting to number if forced or a qualifier was provided and val looks numeric
6893
+ if ( extra === "" || extra ) {
6894
+ num = parseFloat( val );
6895
+ return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
6896
+ }
6897
+ return val;
6898
+ },
6899
+
6900
+ // A method for quickly swapping in/out CSS properties to get correct calculations
6901
+ swap: function( elem, options, callback, args ) {
6902
+ var ret, name,
6903
+ old = {};
6904
+
6905
+ // Remember the old values, and insert the new ones
6906
+ for ( name in options ) {
6907
+ old[ name ] = elem.style[ name ];
6908
+ elem.style[ name ] = options[ name ];
6909
+ }
6910
+
6911
+ ret = callback.apply( elem, args || [] );
6912
+
6913
+ // Revert the old values
6914
+ for ( name in options ) {
6915
+ elem.style[ name ] = old[ name ];
6916
+ }
6917
+
6918
+ return ret;
6919
+ }
6920
+ });
6921
+
6922
+ // NOTE: we've included the "window" in window.getComputedStyle
6923
+ // because jsdom on node.js will break without it.
6924
+ if ( window.getComputedStyle ) {
6925
+ getStyles = function( elem ) {
6926
+ return window.getComputedStyle( elem, null );
6927
+ };
6928
+
6929
+ curCSS = function( elem, name, _computed ) {
6930
+ var width, minWidth, maxWidth,
6931
+ computed = _computed || getStyles( elem ),
6932
+
6933
+ // getPropertyValue is only needed for .css('filter') in IE9, see #12537
6934
+ ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
6935
+ style = elem.style;
6936
+
6937
+ if ( computed ) {
6938
+
6939
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
6940
+ ret = jQuery.style( elem, name );
6941
+ }
6942
+
6943
+ // A tribute to the "awesome hack by Dean Edwards"
6944
+ // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
6945
+ // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
6946
+ // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
6947
+ if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
6948
+
6949
+ // Remember the original values
6950
+ width = style.width;
6951
+ minWidth = style.minWidth;
6952
+ maxWidth = style.maxWidth;
6953
+
6954
+ // Put in the new values to get a computed value out
6955
+ style.minWidth = style.maxWidth = style.width = ret;
6956
+ ret = computed.width;
6957
+
6958
+ // Revert the changed values
6959
+ style.width = width;
6960
+ style.minWidth = minWidth;
6961
+ style.maxWidth = maxWidth;
6962
+ }
6963
+ }
6964
+
6965
+ return ret;
6966
+ };
6967
+ } else if ( document.documentElement.currentStyle ) {
6968
+ getStyles = function( elem ) {
6969
+ return elem.currentStyle;
6970
+ };
6971
+
6972
+ curCSS = function( elem, name, _computed ) {
6973
+ var left, rs, rsLeft,
6974
+ computed = _computed || getStyles( elem ),
6975
+ ret = computed ? computed[ name ] : undefined,
6976
+ style = elem.style;
6977
+
6978
+ // Avoid setting ret to empty string here
6979
+ // so we don't default to auto
6980
+ if ( ret == null && style && style[ name ] ) {
6981
+ ret = style[ name ];
6982
+ }
6983
+
6984
+ // From the awesome hack by Dean Edwards
6985
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
6986
+
6987
+ // If we're not dealing with a regular pixel number
6988
+ // but a number that has a weird ending, we need to convert it to pixels
6989
+ // but not position css attributes, as those are proportional to the parent element instead
6990
+ // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
6991
+ if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
6992
+
6993
+ // Remember the original values
6994
+ left = style.left;
6995
+ rs = elem.runtimeStyle;
6996
+ rsLeft = rs && rs.left;
6997
+
6998
+ // Put in the new values to get a computed value out
6999
+ if ( rsLeft ) {
7000
+ rs.left = elem.currentStyle.left;
7001
+ }
7002
+ style.left = name === "fontSize" ? "1em" : ret;
7003
+ ret = style.pixelLeft + "px";
7004
+
7005
+ // Revert the changed values
7006
+ style.left = left;
7007
+ if ( rsLeft ) {
7008
+ rs.left = rsLeft;
7009
+ }
7010
+ }
7011
+
7012
+ return ret === "" ? "auto" : ret;
7013
+ };
7014
+ }
7015
+
7016
+ function setPositiveNumber( elem, value, subtract ) {
7017
+ var matches = rnumsplit.exec( value );
7018
+ return matches ?
7019
+ // Guard against undefined "subtract", e.g., when used as in cssHooks
7020
+ Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
7021
+ value;
7022
+ }
7023
+
7024
+ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
7025
+ var i = extra === ( isBorderBox ? "border" : "content" ) ?
7026
+ // If we already have the right measurement, avoid augmentation
7027
+ 4 :
7028
+ // Otherwise initialize for horizontal or vertical properties
7029
+ name === "width" ? 1 : 0,
7030
+
7031
+ val = 0;
7032
+
7033
+ for ( ; i < 4; i += 2 ) {
7034
+ // both box models exclude margin, so add it if we want it
7035
+ if ( extra === "margin" ) {
7036
+ val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
7037
+ }
7038
+
7039
+ if ( isBorderBox ) {
7040
+ // border-box includes padding, so remove it if we want content
7041
+ if ( extra === "content" ) {
7042
+ val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7043
+ }
7044
+
7045
+ // at this point, extra isn't border nor margin, so remove border
7046
+ if ( extra !== "margin" ) {
7047
+ val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7048
+ }
7049
+ } else {
7050
+ // at this point, extra isn't content, so add padding
7051
+ val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7052
+
7053
+ // at this point, extra isn't content nor padding, so add border
7054
+ if ( extra !== "padding" ) {
7055
+ val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7056
+ }
7057
+ }
7058
+ }
7059
+
7060
+ return val;
7061
+ }
7062
+
7063
+ function getWidthOrHeight( elem, name, extra ) {
7064
+
7065
+ // Start with offset property, which is equivalent to the border-box value
7066
+ var valueIsBorderBox = true,
7067
+ val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
7068
+ styles = getStyles( elem ),
7069
+ isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
7070
+
7071
+ // some non-html elements return undefined for offsetWidth, so check for null/undefined
7072
+ // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
7073
+ // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
7074
+ if ( val <= 0 || val == null ) {
7075
+ // Fall back to computed then uncomputed css if necessary
7076
+ val = curCSS( elem, name, styles );
7077
+ if ( val < 0 || val == null ) {
7078
+ val = elem.style[ name ];
7079
+ }
7080
+
7081
+ // Computed unit is not pixels. Stop here and return.
7082
+ if ( rnumnonpx.test(val) ) {
7083
+ return val;
7084
+ }
7085
+
7086
+ // we need the check for style in case a browser which returns unreliable values
7087
+ // for getComputedStyle silently falls back to the reliable elem.style
7088
+ valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
7089
+
7090
+ // Normalize "", auto, and prepare for extra
7091
+ val = parseFloat( val ) || 0;
7092
+ }
7093
+
7094
+ // use the active box-sizing model to add/subtract irrelevant styles
7095
+ return ( val +
7096
+ augmentWidthOrHeight(
7097
+ elem,
7098
+ name,
7099
+ extra || ( isBorderBox ? "border" : "content" ),
7100
+ valueIsBorderBox,
7101
+ styles
7102
+ )
7103
+ ) + "px";
7104
+ }
7105
+
7106
+ // Try to determine the default display value of an element
7107
+ function css_defaultDisplay( nodeName ) {
7108
+ var doc = document,
7109
+ display = elemdisplay[ nodeName ];
7110
+
7111
+ if ( !display ) {
7112
+ display = actualDisplay( nodeName, doc );
7113
+
7114
+ // If the simple way fails, read from inside an iframe
7115
+ if ( display === "none" || !display ) {
7116
+ // Use the already-created iframe if possible
7117
+ iframe = ( iframe ||
7118
+ jQuery("<iframe frameborder='0' width='0' height='0'/>")
7119
+ .css( "cssText", "display:block !important" )
7120
+ ).appendTo( doc.documentElement );
7121
+
7122
+ // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
7123
+ doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
7124
+ doc.write("<!doctype html><html><body>");
7125
+ doc.close();
7126
+
7127
+ display = actualDisplay( nodeName, doc );
7128
+ iframe.detach();
7129
+ }
7130
+
7131
+ // Store the correct default display
7132
+ elemdisplay[ nodeName ] = display;
7133
+ }
7134
+
7135
+ return display;
7136
+ }
7137
+
7138
+ // Called ONLY from within css_defaultDisplay
7139
+ function actualDisplay( name, doc ) {
7140
+ var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
7141
+ display = jQuery.css( elem[0], "display" );
7142
+ elem.remove();
7143
+ return display;
7144
+ }
7145
+
7146
+ jQuery.each([ "height", "width" ], function( i, name ) {
7147
+ jQuery.cssHooks[ name ] = {
7148
+ get: function( elem, computed, extra ) {
7149
+ if ( computed ) {
7150
+ // certain elements can have dimension info if we invisibly show them
7151
+ // however, it must have a current display style that would benefit from this
7152
+ return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
7153
+ jQuery.swap( elem, cssShow, function() {
7154
+ return getWidthOrHeight( elem, name, extra );
7155
+ }) :
7156
+ getWidthOrHeight( elem, name, extra );
7157
+ }
7158
+ },
7159
+
7160
+ set: function( elem, value, extra ) {
7161
+ var styles = extra && getStyles( elem );
7162
+ return setPositiveNumber( elem, value, extra ?
7163
+ augmentWidthOrHeight(
7164
+ elem,
7165
+ name,
7166
+ extra,
7167
+ jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
7168
+ styles
7169
+ ) : 0
7170
+ );
7171
+ }
7172
+ };
7173
+ });
7174
+
7175
+ if ( !jQuery.support.opacity ) {
7176
+ jQuery.cssHooks.opacity = {
7177
+ get: function( elem, computed ) {
7178
+ // IE uses filters for opacity
7179
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
7180
+ ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
7181
+ computed ? "1" : "";
7182
+ },
7183
+
7184
+ set: function( elem, value ) {
7185
+ var style = elem.style,
7186
+ currentStyle = elem.currentStyle,
7187
+ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
7188
+ filter = currentStyle && currentStyle.filter || style.filter || "";
7189
+
7190
+ // IE has trouble with opacity if it does not have layout
7191
+ // Force it by setting the zoom level
7192
+ style.zoom = 1;
7193
+
7194
+ // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
7195
+ // if value === "", then remove inline opacity #12685
7196
+ if ( ( value >= 1 || value === "" ) &&
7197
+ jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
7198
+ style.removeAttribute ) {
7199
+
7200
+ // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
7201
+ // if "filter:" is present at all, clearType is disabled, we want to avoid this
7202
+ // style.removeAttribute is IE Only, but so apparently is this code path...
7203
+ style.removeAttribute( "filter" );
7204
+
7205
+ // if there is no filter style applied in a css rule or unset inline opacity, we are done
7206
+ if ( value === "" || currentStyle && !currentStyle.filter ) {
7207
+ return;
7208
+ }
7209
+ }
7210
+
7211
+ // otherwise, set new filter values
7212
+ style.filter = ralpha.test( filter ) ?
7213
+ filter.replace( ralpha, opacity ) :
7214
+ filter + " " + opacity;
7215
+ }
7216
+ };
7217
+ }
7218
+
7219
+ // These hooks cannot be added until DOM ready because the support test
7220
+ // for it is not run until after DOM ready
7221
+ jQuery(function() {
7222
+ if ( !jQuery.support.reliableMarginRight ) {
7223
+ jQuery.cssHooks.marginRight = {
7224
+ get: function( elem, computed ) {
7225
+ if ( computed ) {
7226
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
7227
+ // Work around by temporarily setting element display to inline-block
7228
+ return jQuery.swap( elem, { "display": "inline-block" },
7229
+ curCSS, [ elem, "marginRight" ] );
7230
+ }
7231
+ }
7232
+ };
7233
+ }
7234
+
7235
+ // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
7236
+ // getComputedStyle returns percent when specified for top/left/bottom/right
7237
+ // rather than make the css module depend on the offset module, we just check for it here
7238
+ if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
7239
+ jQuery.each( [ "top", "left" ], function( i, prop ) {
7240
+ jQuery.cssHooks[ prop ] = {
7241
+ get: function( elem, computed ) {
7242
+ if ( computed ) {
7243
+ computed = curCSS( elem, prop );
7244
+ // if curCSS returns percentage, fallback to offset
7245
+ return rnumnonpx.test( computed ) ?
7246
+ jQuery( elem ).position()[ prop ] + "px" :
7247
+ computed;
7248
+ }
7249
+ }
7250
+ };
7251
+ });
7252
+ }
7253
+
7254
+ });
7255
+
7256
+ if ( jQuery.expr && jQuery.expr.filters ) {
7257
+ jQuery.expr.filters.hidden = function( elem ) {
7258
+ // Support: Opera <= 12.12
7259
+ // Opera reports offsetWidths and offsetHeights less than zero on some elements
7260
+ return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
7261
+ (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
7262
+ };
7263
+
7264
+ jQuery.expr.filters.visible = function( elem ) {
7265
+ return !jQuery.expr.filters.hidden( elem );
7266
+ };
7267
+ }
7268
+
7269
+ // These hooks are used by animate to expand properties
7270
+ jQuery.each({
7271
+ margin: "",
7272
+ padding: "",
7273
+ border: "Width"
7274
+ }, function( prefix, suffix ) {
7275
+ jQuery.cssHooks[ prefix + suffix ] = {
7276
+ expand: function( value ) {
7277
+ var i = 0,
7278
+ expanded = {},
7279
+
7280
+ // assumes a single number if not a string
7281
+ parts = typeof value === "string" ? value.split(" ") : [ value ];
7282
+
7283
+ for ( ; i < 4; i++ ) {
7284
+ expanded[ prefix + cssExpand[ i ] + suffix ] =
7285
+ parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
7286
+ }
7287
+
7288
+ return expanded;
7289
+ }
7290
+ };
7291
+
7292
+ if ( !rmargin.test( prefix ) ) {
7293
+ jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
7294
+ }
7295
+ });
7296
+ var r20 = /%20/g,
7297
+ rbracket = /\[\]$/,
7298
+ rCRLF = /\r?\n/g,
7299
+ rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
7300
+ rsubmittable = /^(?:input|select|textarea|keygen)/i;
7301
+
7302
+ jQuery.fn.extend({
7303
+ serialize: function() {
7304
+ return jQuery.param( this.serializeArray() );
7305
+ },
7306
+ serializeArray: function() {
7307
+ return this.map(function(){
7308
+ // Can add propHook for "elements" to filter or add form elements
7309
+ var elements = jQuery.prop( this, "elements" );
7310
+ return elements ? jQuery.makeArray( elements ) : this;
7311
+ })
7312
+ .filter(function(){
7313
+ var type = this.type;
7314
+ // Use .is(":disabled") so that fieldset[disabled] works
7315
+ return this.name && !jQuery( this ).is( ":disabled" ) &&
7316
+ rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
7317
+ ( this.checked || !manipulation_rcheckableType.test( type ) );
7318
+ })
7319
+ .map(function( i, elem ){
7320
+ var val = jQuery( this ).val();
7321
+
7322
+ return val == null ?
7323
+ null :
7324
+ jQuery.isArray( val ) ?
7325
+ jQuery.map( val, function( val ){
7326
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7327
+ }) :
7328
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7329
+ }).get();
7330
+ }
7331
+ });
7332
+
7333
+ //Serialize an array of form elements or a set of
7334
+ //key/values into a query string
7335
+ jQuery.param = function( a, traditional ) {
7336
+ var prefix,
7337
+ s = [],
7338
+ add = function( key, value ) {
7339
+ // If value is a function, invoke it and return its value
7340
+ value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
7341
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
7342
+ };
7343
+
7344
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
7345
+ if ( traditional === undefined ) {
7346
+ traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
7347
+ }
7348
+
7349
+ // If an array was passed in, assume that it is an array of form elements.
7350
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
7351
+ // Serialize the form elements
7352
+ jQuery.each( a, function() {
7353
+ add( this.name, this.value );
7354
+ });
7355
+
7356
+ } else {
7357
+ // If traditional, encode the "old" way (the way 1.3.2 or older
7358
+ // did it), otherwise encode params recursively.
7359
+ for ( prefix in a ) {
7360
+ buildParams( prefix, a[ prefix ], traditional, add );
7361
+ }
7362
+ }
7363
+
7364
+ // Return the resulting serialization
7365
+ return s.join( "&" ).replace( r20, "+" );
7366
+ };
7367
+
7368
+ function buildParams( prefix, obj, traditional, add ) {
7369
+ var name;
7370
+
7371
+ if ( jQuery.isArray( obj ) ) {
7372
+ // Serialize array item.
7373
+ jQuery.each( obj, function( i, v ) {
7374
+ if ( traditional || rbracket.test( prefix ) ) {
7375
+ // Treat each array item as a scalar.
7376
+ add( prefix, v );
7377
+
7378
+ } else {
7379
+ // Item is non-scalar (array or object), encode its numeric index.
7380
+ buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
7381
+ }
7382
+ });
7383
+
7384
+ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
7385
+ // Serialize object item.
7386
+ for ( name in obj ) {
7387
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
7388
+ }
7389
+
7390
+ } else {
7391
+ // Serialize scalar item.
7392
+ add( prefix, obj );
7393
+ }
7394
+ }
7395
+ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
7396
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
7397
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
7398
+
7399
+ // Handle event binding
7400
+ jQuery.fn[ name ] = function( data, fn ) {
7401
+ return arguments.length > 0 ?
7402
+ this.on( name, null, data, fn ) :
7403
+ this.trigger( name );
7404
+ };
7405
+ });
7406
+
7407
+ jQuery.fn.hover = function( fnOver, fnOut ) {
7408
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
7409
+ };
7410
+ var
7411
+ // Document location
7412
+ ajaxLocParts,
7413
+ ajaxLocation,
7414
+ ajax_nonce = jQuery.now(),
7415
+
7416
+ ajax_rquery = /\?/,
7417
+ rhash = /#.*$/,
7418
+ rts = /([?&])_=[^&]*/,
7419
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
7420
+ // #7653, #8125, #8152: local protocol detection
7421
+ rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
7422
+ rnoContent = /^(?:GET|HEAD)$/,
7423
+ rprotocol = /^\/\//,
7424
+ rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
7425
+
7426
+ // Keep a copy of the old load method
7427
+ _load = jQuery.fn.load,
7428
+
7429
+ /* Prefilters
7430
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
7431
+ * 2) These are called:
7432
+ * - BEFORE asking for a transport
7433
+ * - AFTER param serialization (s.data is a string if s.processData is true)
7434
+ * 3) key is the dataType
7435
+ * 4) the catchall symbol "*" can be used
7436
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
7437
+ */
7438
+ prefilters = {},
7439
+
7440
+ /* Transports bindings
7441
+ * 1) key is the dataType
7442
+ * 2) the catchall symbol "*" can be used
7443
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
7444
+ */
7445
+ transports = {},
7446
+
7447
+ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
7448
+ allTypes = "*/".concat("*");
7449
+
7450
+ // #8138, IE may throw an exception when accessing
7451
+ // a field from window.location if document.domain has been set
7452
+ try {
7453
+ ajaxLocation = location.href;
7454
+ } catch( e ) {
7455
+ // Use the href attribute of an A element
7456
+ // since IE will modify it given document.location
7457
+ ajaxLocation = document.createElement( "a" );
7458
+ ajaxLocation.href = "";
7459
+ ajaxLocation = ajaxLocation.href;
7460
+ }
7461
+
7462
+ // Segment location into parts
7463
+ ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
7464
+
7465
+ // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
7466
+ function addToPrefiltersOrTransports( structure ) {
7467
+
7468
+ // dataTypeExpression is optional and defaults to "*"
7469
+ return function( dataTypeExpression, func ) {
7470
+
7471
+ if ( typeof dataTypeExpression !== "string" ) {
7472
+ func = dataTypeExpression;
7473
+ dataTypeExpression = "*";
7474
+ }
7475
+
7476
+ var dataType,
7477
+ i = 0,
7478
+ dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
7479
+
7480
+ if ( jQuery.isFunction( func ) ) {
7481
+ // For each dataType in the dataTypeExpression
7482
+ while ( (dataType = dataTypes[i++]) ) {
7483
+ // Prepend if requested
7484
+ if ( dataType[0] === "+" ) {
7485
+ dataType = dataType.slice( 1 ) || "*";
7486
+ (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
7487
+
7488
+ // Otherwise append
7489
+ } else {
7490
+ (structure[ dataType ] = structure[ dataType ] || []).push( func );
7491
+ }
7492
+ }
7493
+ }
7494
+ };
7495
+ }
7496
+
7497
+ // Base inspection function for prefilters and transports
7498
+ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
7499
+
7500
+ var inspected = {},
7501
+ seekingTransport = ( structure === transports );
7502
+
7503
+ function inspect( dataType ) {
7504
+ var selected;
7505
+ inspected[ dataType ] = true;
7506
+ jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
7507
+ var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
7508
+ if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
7509
+ options.dataTypes.unshift( dataTypeOrTransport );
7510
+ inspect( dataTypeOrTransport );
7511
+ return false;
7512
+ } else if ( seekingTransport ) {
7513
+ return !( selected = dataTypeOrTransport );
7514
+ }
7515
+ });
7516
+ return selected;
7517
+ }
7518
+
7519
+ return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
7520
+ }
7521
+
7522
+ // A special extend for ajax options
7523
+ // that takes "flat" options (not to be deep extended)
7524
+ // Fixes #9887
7525
+ function ajaxExtend( target, src ) {
7526
+ var deep, key,
7527
+ flatOptions = jQuery.ajaxSettings.flatOptions || {};
7528
+
7529
+ for ( key in src ) {
7530
+ if ( src[ key ] !== undefined ) {
7531
+ ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
7532
+ }
7533
+ }
7534
+ if ( deep ) {
7535
+ jQuery.extend( true, target, deep );
7536
+ }
7537
+
7538
+ return target;
7539
+ }
7540
+
7541
+ jQuery.fn.load = function( url, params, callback ) {
7542
+ if ( typeof url !== "string" && _load ) {
7543
+ return _load.apply( this, arguments );
7544
+ }
7545
+
7546
+ var selector, response, type,
7547
+ self = this,
7548
+ off = url.indexOf(" ");
7549
+
7550
+ if ( off >= 0 ) {
7551
+ selector = url.slice( off, url.length );
7552
+ url = url.slice( 0, off );
7553
+ }
7554
+
7555
+ // If it's a function
7556
+ if ( jQuery.isFunction( params ) ) {
7557
+
7558
+ // We assume that it's the callback
7559
+ callback = params;
7560
+ params = undefined;
7561
+
7562
+ // Otherwise, build a param string
7563
+ } else if ( params && typeof params === "object" ) {
7564
+ type = "POST";
7565
+ }
7566
+
7567
+ // If we have elements to modify, make the request
7568
+ if ( self.length > 0 ) {
7569
+ jQuery.ajax({
7570
+ url: url,
7571
+
7572
+ // if "type" variable is undefined, then "GET" method will be used
7573
+ type: type,
7574
+ dataType: "html",
7575
+ data: params
7576
+ }).done(function( responseText ) {
7577
+
7578
+ // Save response for use in complete callback
7579
+ response = arguments;
7580
+
7581
+ self.html( selector ?
7582
+
7583
+ // If a selector was specified, locate the right elements in a dummy div
7584
+ // Exclude scripts to avoid IE 'Permission Denied' errors
7585
+ jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
7586
+
7587
+ // Otherwise use the full result
7588
+ responseText );
7589
+
7590
+ }).complete( callback && function( jqXHR, status ) {
7591
+ self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
7592
+ });
7593
+ }
7594
+
7595
+ return this;
7596
+ };
7597
+
7598
+ // Attach a bunch of functions for handling common AJAX events
7599
+ jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
7600
+ jQuery.fn[ type ] = function( fn ){
7601
+ return this.on( type, fn );
7602
+ };
7603
+ });
7604
+
7605
+ jQuery.each( [ "get", "post" ], function( i, method ) {
7606
+ jQuery[ method ] = function( url, data, callback, type ) {
7607
+ // shift arguments if data argument was omitted
7608
+ if ( jQuery.isFunction( data ) ) {
7609
+ type = type || callback;
7610
+ callback = data;
7611
+ data = undefined;
7612
+ }
7613
+
7614
+ return jQuery.ajax({
7615
+ url: url,
7616
+ type: method,
7617
+ dataType: type,
7618
+ data: data,
7619
+ success: callback
7620
+ });
7621
+ };
7622
+ });
7623
+
7624
+ jQuery.extend({
7625
+
7626
+ // Counter for holding the number of active queries
7627
+ active: 0,
7628
+
7629
+ // Last-Modified header cache for next request
7630
+ lastModified: {},
7631
+ etag: {},
7632
+
7633
+ ajaxSettings: {
7634
+ url: ajaxLocation,
7635
+ type: "GET",
7636
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
7637
+ global: true,
7638
+ processData: true,
7639
+ async: true,
7640
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
7641
+ /*
7642
+ timeout: 0,
7643
+ data: null,
7644
+ dataType: null,
7645
+ username: null,
7646
+ password: null,
7647
+ cache: null,
7648
+ throws: false,
7649
+ traditional: false,
7650
+ headers: {},
7651
+ */
7652
+
7653
+ accepts: {
7654
+ "*": allTypes,
7655
+ text: "text/plain",
7656
+ html: "text/html",
7657
+ xml: "application/xml, text/xml",
7658
+ json: "application/json, text/javascript"
7659
+ },
7660
+
7661
+ contents: {
7662
+ xml: /xml/,
7663
+ html: /html/,
7664
+ json: /json/
7665
+ },
7666
+
7667
+ responseFields: {
7668
+ xml: "responseXML",
7669
+ text: "responseText"
7670
+ },
7671
+
7672
+ // Data converters
7673
+ // Keys separate source (or catchall "*") and destination types with a single space
7674
+ converters: {
7675
+
7676
+ // Convert anything to text
7677
+ "* text": window.String,
7678
+
7679
+ // Text to html (true = no transformation)
7680
+ "text html": true,
7681
+
7682
+ // Evaluate text as a json expression
7683
+ "text json": jQuery.parseJSON,
7684
+
7685
+ // Parse text as xml
7686
+ "text xml": jQuery.parseXML
7687
+ },
7688
+
7689
+ // For options that shouldn't be deep extended:
7690
+ // you can add your own custom options here if
7691
+ // and when you create one that shouldn't be
7692
+ // deep extended (see ajaxExtend)
7693
+ flatOptions: {
7694
+ url: true,
7695
+ context: true
7696
+ }
7697
+ },
7698
+
7699
+ // Creates a full fledged settings object into target
7700
+ // with both ajaxSettings and settings fields.
7701
+ // If target is omitted, writes into ajaxSettings.
7702
+ ajaxSetup: function( target, settings ) {
7703
+ return settings ?
7704
+
7705
+ // Building a settings object
7706
+ ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
7707
+
7708
+ // Extending ajaxSettings
7709
+ ajaxExtend( jQuery.ajaxSettings, target );
7710
+ },
7711
+
7712
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
7713
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
7714
+
7715
+ // Main method
7716
+ ajax: function( url, options ) {
7717
+
7718
+ // If url is an object, simulate pre-1.5 signature
7719
+ if ( typeof url === "object" ) {
7720
+ options = url;
7721
+ url = undefined;
7722
+ }
7723
+
7724
+ // Force options to be an object
7725
+ options = options || {};
7726
+
7727
+ var // Cross-domain detection vars
7728
+ parts,
7729
+ // Loop variable
7730
+ i,
7731
+ // URL without anti-cache param
7732
+ cacheURL,
7733
+ // Response headers as string
7734
+ responseHeadersString,
7735
+ // timeout handle
7736
+ timeoutTimer,
7737
+
7738
+ // To know if global events are to be dispatched
7739
+ fireGlobals,
7740
+
7741
+ transport,
7742
+ // Response headers
7743
+ responseHeaders,
7744
+ // Create the final options object
7745
+ s = jQuery.ajaxSetup( {}, options ),
7746
+ // Callbacks context
7747
+ callbackContext = s.context || s,
7748
+ // Context for global events is callbackContext if it is a DOM node or jQuery collection
7749
+ globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
7750
+ jQuery( callbackContext ) :
7751
+ jQuery.event,
7752
+ // Deferreds
7753
+ deferred = jQuery.Deferred(),
7754
+ completeDeferred = jQuery.Callbacks("once memory"),
7755
+ // Status-dependent callbacks
7756
+ statusCode = s.statusCode || {},
7757
+ // Headers (they are sent all at once)
7758
+ requestHeaders = {},
7759
+ requestHeadersNames = {},
7760
+ // The jqXHR state
7761
+ state = 0,
7762
+ // Default abort message
7763
+ strAbort = "canceled",
7764
+ // Fake xhr
7765
+ jqXHR = {
7766
+ readyState: 0,
7767
+
7768
+ // Builds headers hashtable if needed
7769
+ getResponseHeader: function( key ) {
7770
+ var match;
7771
+ if ( state === 2 ) {
7772
+ if ( !responseHeaders ) {
7773
+ responseHeaders = {};
7774
+ while ( (match = rheaders.exec( responseHeadersString )) ) {
7775
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
7776
+ }
7777
+ }
7778
+ match = responseHeaders[ key.toLowerCase() ];
7779
+ }
7780
+ return match == null ? null : match;
7781
+ },
7782
+
7783
+ // Raw string
7784
+ getAllResponseHeaders: function() {
7785
+ return state === 2 ? responseHeadersString : null;
7786
+ },
7787
+
7788
+ // Caches the header
7789
+ setRequestHeader: function( name, value ) {
7790
+ var lname = name.toLowerCase();
7791
+ if ( !state ) {
7792
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
7793
+ requestHeaders[ name ] = value;
7794
+ }
7795
+ return this;
7796
+ },
7797
+
7798
+ // Overrides response content-type header
7799
+ overrideMimeType: function( type ) {
7800
+ if ( !state ) {
7801
+ s.mimeType = type;
7802
+ }
7803
+ return this;
7804
+ },
7805
+
7806
+ // Status-dependent callbacks
7807
+ statusCode: function( map ) {
7808
+ var code;
7809
+ if ( map ) {
7810
+ if ( state < 2 ) {
7811
+ for ( code in map ) {
7812
+ // Lazy-add the new callback in a way that preserves old ones
7813
+ statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
7814
+ }
7815
+ } else {
7816
+ // Execute the appropriate callbacks
7817
+ jqXHR.always( map[ jqXHR.status ] );
7818
+ }
7819
+ }
7820
+ return this;
7821
+ },
7822
+
7823
+ // Cancel the request
7824
+ abort: function( statusText ) {
7825
+ var finalText = statusText || strAbort;
7826
+ if ( transport ) {
7827
+ transport.abort( finalText );
7828
+ }
7829
+ done( 0, finalText );
7830
+ return this;
7831
+ }
7832
+ };
7833
+
7834
+ // Attach deferreds
7835
+ deferred.promise( jqXHR ).complete = completeDeferred.add;
7836
+ jqXHR.success = jqXHR.done;
7837
+ jqXHR.error = jqXHR.fail;
7838
+
7839
+ // Remove hash character (#7531: and string promotion)
7840
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
7841
+ // Handle falsy url in the settings object (#10093: consistency with old signature)
7842
+ // We also use the url parameter if available
7843
+ s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
7844
+
7845
+ // Alias method option to type as per ticket #12004
7846
+ s.type = options.method || options.type || s.method || s.type;
7847
+
7848
+ // Extract dataTypes list
7849
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
7850
+
7851
+ // A cross-domain request is in order when we have a protocol:host:port mismatch
7852
+ if ( s.crossDomain == null ) {
7853
+ parts = rurl.exec( s.url.toLowerCase() );
7854
+ s.crossDomain = !!( parts &&
7855
+ ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
7856
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
7857
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
7858
+ );
7859
+ }
7860
+
7861
+ // Convert data if not already a string
7862
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
7863
+ s.data = jQuery.param( s.data, s.traditional );
7864
+ }
7865
+
7866
+ // Apply prefilters
7867
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
7868
+
7869
+ // If request was aborted inside a prefilter, stop there
7870
+ if ( state === 2 ) {
7871
+ return jqXHR;
7872
+ }
7873
+
7874
+ // We can fire global events as of now if asked to
7875
+ fireGlobals = s.global;
7876
+
7877
+ // Watch for a new set of requests
7878
+ if ( fireGlobals && jQuery.active++ === 0 ) {
7879
+ jQuery.event.trigger("ajaxStart");
7880
+ }
7881
+
7882
+ // Uppercase the type
7883
+ s.type = s.type.toUpperCase();
7884
+
7885
+ // Determine if request has content
7886
+ s.hasContent = !rnoContent.test( s.type );
7887
+
7888
+ // Save the URL in case we're toying with the If-Modified-Since
7889
+ // and/or If-None-Match header later on
7890
+ cacheURL = s.url;
7891
+
7892
+ // More options handling for requests with no content
7893
+ if ( !s.hasContent ) {
7894
+
7895
+ // If data is available, append data to url
7896
+ if ( s.data ) {
7897
+ cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
7898
+ // #9682: remove data so that it's not used in an eventual retry
7899
+ delete s.data;
7900
+ }
7901
+
7902
+ // Add anti-cache in url if needed
7903
+ if ( s.cache === false ) {
7904
+ s.url = rts.test( cacheURL ) ?
7905
+
7906
+ // If there is already a '_' parameter, set its value
7907
+ cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
7908
+
7909
+ // Otherwise add one to the end
7910
+ cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
7911
+ }
7912
+ }
7913
+
7914
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
7915
+ if ( s.ifModified ) {
7916
+ if ( jQuery.lastModified[ cacheURL ] ) {
7917
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
7918
+ }
7919
+ if ( jQuery.etag[ cacheURL ] ) {
7920
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
7921
+ }
7922
+ }
7923
+
7924
+ // Set the correct header, if data is being sent
7925
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
7926
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
7927
+ }
7928
+
7929
+ // Set the Accepts header for the server, depending on the dataType
7930
+ jqXHR.setRequestHeader(
7931
+ "Accept",
7932
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
7933
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
7934
+ s.accepts[ "*" ]
7935
+ );
7936
+
7937
+ // Check for headers option
7938
+ for ( i in s.headers ) {
7939
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
7940
+ }
7941
+
7942
+ // Allow custom headers/mimetypes and early abort
7943
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
7944
+ // Abort if not done already and return
7945
+ return jqXHR.abort();
7946
+ }
7947
+
7948
+ // aborting is no longer a cancellation
7949
+ strAbort = "abort";
7950
+
7951
+ // Install callbacks on deferreds
7952
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
7953
+ jqXHR[ i ]( s[ i ] );
7954
+ }
7955
+
7956
+ // Get transport
7957
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
7958
+
7959
+ // If no transport, we auto-abort
7960
+ if ( !transport ) {
7961
+ done( -1, "No Transport" );
7962
+ } else {
7963
+ jqXHR.readyState = 1;
7964
+
7965
+ // Send global event
7966
+ if ( fireGlobals ) {
7967
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
7968
+ }
7969
+ // Timeout
7970
+ if ( s.async && s.timeout > 0 ) {
7971
+ timeoutTimer = setTimeout(function() {
7972
+ jqXHR.abort("timeout");
7973
+ }, s.timeout );
7974
+ }
7975
+
7976
+ try {
7977
+ state = 1;
7978
+ transport.send( requestHeaders, done );
7979
+ } catch ( e ) {
7980
+ // Propagate exception as error if not done
7981
+ if ( state < 2 ) {
7982
+ done( -1, e );
7983
+ // Simply rethrow otherwise
7984
+ } else {
7985
+ throw e;
7986
+ }
7987
+ }
7988
+ }
7989
+
7990
+ // Callback for when everything is done
7991
+ function done( status, nativeStatusText, responses, headers ) {
7992
+ var isSuccess, success, error, response, modified,
7993
+ statusText = nativeStatusText;
7994
+
7995
+ // Called once
7996
+ if ( state === 2 ) {
7997
+ return;
7998
+ }
7999
+
8000
+ // State is "done" now
8001
+ state = 2;
8002
+
8003
+ // Clear timeout if it exists
8004
+ if ( timeoutTimer ) {
8005
+ clearTimeout( timeoutTimer );
8006
+ }
8007
+
8008
+ // Dereference transport for early garbage collection
8009
+ // (no matter how long the jqXHR object will be used)
8010
+ transport = undefined;
8011
+
8012
+ // Cache response headers
8013
+ responseHeadersString = headers || "";
8014
+
8015
+ // Set readyState
8016
+ jqXHR.readyState = status > 0 ? 4 : 0;
8017
+
8018
+ // Get response data
8019
+ if ( responses ) {
8020
+ response = ajaxHandleResponses( s, jqXHR, responses );
8021
+ }
8022
+
8023
+ // If successful, handle type chaining
8024
+ if ( status >= 200 && status < 300 || status === 304 ) {
8025
+
8026
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8027
+ if ( s.ifModified ) {
8028
+ modified = jqXHR.getResponseHeader("Last-Modified");
8029
+ if ( modified ) {
8030
+ jQuery.lastModified[ cacheURL ] = modified;
8031
+ }
8032
+ modified = jqXHR.getResponseHeader("etag");
8033
+ if ( modified ) {
8034
+ jQuery.etag[ cacheURL ] = modified;
8035
+ }
8036
+ }
8037
+
8038
+ // if no content
8039
+ if ( status === 204 ) {
8040
+ isSuccess = true;
8041
+ statusText = "nocontent";
8042
+
8043
+ // if not modified
8044
+ } else if ( status === 304 ) {
8045
+ isSuccess = true;
8046
+ statusText = "notmodified";
8047
+
8048
+ // If we have data, let's convert it
8049
+ } else {
8050
+ isSuccess = ajaxConvert( s, response );
8051
+ statusText = isSuccess.state;
8052
+ success = isSuccess.data;
8053
+ error = isSuccess.error;
8054
+ isSuccess = !error;
8055
+ }
8056
+ } else {
8057
+ // We extract error from statusText
8058
+ // then normalize statusText and status for non-aborts
8059
+ error = statusText;
8060
+ if ( status || !statusText ) {
8061
+ statusText = "error";
8062
+ if ( status < 0 ) {
8063
+ status = 0;
8064
+ }
8065
+ }
8066
+ }
8067
+
8068
+ // Set data for the fake xhr object
8069
+ jqXHR.status = status;
8070
+ jqXHR.statusText = ( nativeStatusText || statusText ) + "";
8071
+
8072
+ // Success/Error
8073
+ if ( isSuccess ) {
8074
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
8075
+ } else {
8076
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
8077
+ }
8078
+
8079
+ // Status-dependent callbacks
8080
+ jqXHR.statusCode( statusCode );
8081
+ statusCode = undefined;
8082
+
8083
+ if ( fireGlobals ) {
8084
+ globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
8085
+ [ jqXHR, s, isSuccess ? success : error ] );
8086
+ }
8087
+
8088
+ // Complete
8089
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
8090
+
8091
+ if ( fireGlobals ) {
8092
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
8093
+ // Handle the global AJAX counter
8094
+ if ( !( --jQuery.active ) ) {
8095
+ jQuery.event.trigger("ajaxStop");
8096
+ }
8097
+ }
8098
+ }
8099
+
8100
+ return jqXHR;
8101
+ },
8102
+
8103
+ getScript: function( url, callback ) {
8104
+ return jQuery.get( url, undefined, callback, "script" );
8105
+ },
8106
+
8107
+ getJSON: function( url, data, callback ) {
8108
+ return jQuery.get( url, data, callback, "json" );
8109
+ }
8110
+ });
8111
+
8112
+ /* Handles responses to an ajax request:
8113
+ * - sets all responseXXX fields accordingly
8114
+ * - finds the right dataType (mediates between content-type and expected dataType)
8115
+ * - returns the corresponding response
8116
+ */
8117
+ function ajaxHandleResponses( s, jqXHR, responses ) {
8118
+ var firstDataType, ct, finalDataType, type,
8119
+ contents = s.contents,
8120
+ dataTypes = s.dataTypes,
8121
+ responseFields = s.responseFields;
8122
+
8123
+ // Fill responseXXX fields
8124
+ for ( type in responseFields ) {
8125
+ if ( type in responses ) {
8126
+ jqXHR[ responseFields[type] ] = responses[ type ];
8127
+ }
8128
+ }
8129
+
8130
+ // Remove auto dataType and get content-type in the process
8131
+ while( dataTypes[ 0 ] === "*" ) {
8132
+ dataTypes.shift();
8133
+ if ( ct === undefined ) {
8134
+ ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
8135
+ }
8136
+ }
8137
+
8138
+ // Check if we're dealing with a known content-type
8139
+ if ( ct ) {
8140
+ for ( type in contents ) {
8141
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
8142
+ dataTypes.unshift( type );
8143
+ break;
8144
+ }
8145
+ }
8146
+ }
8147
+
8148
+ // Check to see if we have a response for the expected dataType
8149
+ if ( dataTypes[ 0 ] in responses ) {
8150
+ finalDataType = dataTypes[ 0 ];
8151
+ } else {
8152
+ // Try convertible dataTypes
8153
+ for ( type in responses ) {
8154
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
8155
+ finalDataType = type;
8156
+ break;
8157
+ }
8158
+ if ( !firstDataType ) {
8159
+ firstDataType = type;
8160
+ }
8161
+ }
8162
+ // Or just use first one
8163
+ finalDataType = finalDataType || firstDataType;
8164
+ }
8165
+
8166
+ // If we found a dataType
8167
+ // We add the dataType to the list if needed
8168
+ // and return the corresponding response
8169
+ if ( finalDataType ) {
8170
+ if ( finalDataType !== dataTypes[ 0 ] ) {
8171
+ dataTypes.unshift( finalDataType );
8172
+ }
8173
+ return responses[ finalDataType ];
8174
+ }
8175
+ }
8176
+
8177
+ // Chain conversions given the request and the original response
8178
+ function ajaxConvert( s, response ) {
8179
+ var conv2, current, conv, tmp,
8180
+ converters = {},
8181
+ i = 0,
8182
+ // Work with a copy of dataTypes in case we need to modify it for conversion
8183
+ dataTypes = s.dataTypes.slice(),
8184
+ prev = dataTypes[ 0 ];
8185
+
8186
+ // Apply the dataFilter if provided
8187
+ if ( s.dataFilter ) {
8188
+ response = s.dataFilter( response, s.dataType );
8189
+ }
8190
+
8191
+ // Create converters map with lowercased keys
8192
+ if ( dataTypes[ 1 ] ) {
8193
+ for ( conv in s.converters ) {
8194
+ converters[ conv.toLowerCase() ] = s.converters[ conv ];
8195
+ }
8196
+ }
8197
+
8198
+ // Convert to each sequential dataType, tolerating list modification
8199
+ for ( ; (current = dataTypes[++i]); ) {
8200
+
8201
+ // There's only work to do if current dataType is non-auto
8202
+ if ( current !== "*" ) {
8203
+
8204
+ // Convert response if prev dataType is non-auto and differs from current
8205
+ if ( prev !== "*" && prev !== current ) {
8206
+
8207
+ // Seek a direct converter
8208
+ conv = converters[ prev + " " + current ] || converters[ "* " + current ];
8209
+
8210
+ // If none found, seek a pair
8211
+ if ( !conv ) {
8212
+ for ( conv2 in converters ) {
8213
+
8214
+ // If conv2 outputs current
8215
+ tmp = conv2.split(" ");
8216
+ if ( tmp[ 1 ] === current ) {
8217
+
8218
+ // If prev can be converted to accepted input
8219
+ conv = converters[ prev + " " + tmp[ 0 ] ] ||
8220
+ converters[ "* " + tmp[ 0 ] ];
8221
+ if ( conv ) {
8222
+ // Condense equivalence converters
8223
+ if ( conv === true ) {
8224
+ conv = converters[ conv2 ];
8225
+
8226
+ // Otherwise, insert the intermediate dataType
8227
+ } else if ( converters[ conv2 ] !== true ) {
8228
+ current = tmp[ 0 ];
8229
+ dataTypes.splice( i--, 0, current );
8230
+ }
8231
+
8232
+ break;
8233
+ }
8234
+ }
8235
+ }
8236
+ }
8237
+
8238
+ // Apply converter (if not an equivalence)
8239
+ if ( conv !== true ) {
8240
+
8241
+ // Unless errors are allowed to bubble, catch and return them
8242
+ if ( conv && s["throws"] ) {
8243
+ response = conv( response );
8244
+ } else {
8245
+ try {
8246
+ response = conv( response );
8247
+ } catch ( e ) {
8248
+ return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
8249
+ }
8250
+ }
8251
+ }
8252
+ }
8253
+
8254
+ // Update prev for next iteration
8255
+ prev = current;
8256
+ }
8257
+ }
8258
+
8259
+ return { state: "success", data: response };
8260
+ }
8261
+ // Install script dataType
8262
+ jQuery.ajaxSetup({
8263
+ accepts: {
8264
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
8265
+ },
8266
+ contents: {
8267
+ script: /(?:java|ecma)script/
8268
+ },
8269
+ converters: {
8270
+ "text script": function( text ) {
8271
+ jQuery.globalEval( text );
8272
+ return text;
8273
+ }
8274
+ }
8275
+ });
8276
+
8277
+ // Handle cache's special case and global
8278
+ jQuery.ajaxPrefilter( "script", function( s ) {
8279
+ if ( s.cache === undefined ) {
8280
+ s.cache = false;
8281
+ }
8282
+ if ( s.crossDomain ) {
8283
+ s.type = "GET";
8284
+ s.global = false;
8285
+ }
8286
+ });
8287
+
8288
+ // Bind script tag hack transport
8289
+ jQuery.ajaxTransport( "script", function(s) {
8290
+
8291
+ // This transport only deals with cross domain requests
8292
+ if ( s.crossDomain ) {
8293
+
8294
+ var script,
8295
+ head = document.head || jQuery("head")[0] || document.documentElement;
8296
+
8297
+ return {
8298
+
8299
+ send: function( _, callback ) {
8300
+
8301
+ script = document.createElement("script");
8302
+
8303
+ script.async = true;
8304
+
8305
+ if ( s.scriptCharset ) {
8306
+ script.charset = s.scriptCharset;
8307
+ }
8308
+
8309
+ script.src = s.url;
8310
+
8311
+ // Attach handlers for all browsers
8312
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
8313
+
8314
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
8315
+
8316
+ // Handle memory leak in IE
8317
+ script.onload = script.onreadystatechange = null;
8318
+
8319
+ // Remove the script
8320
+ if ( script.parentNode ) {
8321
+ script.parentNode.removeChild( script );
8322
+ }
8323
+
8324
+ // Dereference the script
8325
+ script = null;
8326
+
8327
+ // Callback if not abort
8328
+ if ( !isAbort ) {
8329
+ callback( 200, "success" );
8330
+ }
8331
+ }
8332
+ };
8333
+
8334
+ // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
8335
+ // Use native DOM manipulation to avoid our domManip AJAX trickery
8336
+ head.insertBefore( script, head.firstChild );
8337
+ },
8338
+
8339
+ abort: function() {
8340
+ if ( script ) {
8341
+ script.onload( undefined, true );
8342
+ }
8343
+ }
8344
+ };
8345
+ }
8346
+ });
8347
+ var oldCallbacks = [],
8348
+ rjsonp = /(=)\?(?=&|$)|\?\?/;
8349
+
8350
+ // Default jsonp settings
8351
+ jQuery.ajaxSetup({
8352
+ jsonp: "callback",
8353
+ jsonpCallback: function() {
8354
+ var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
8355
+ this[ callback ] = true;
8356
+ return callback;
8357
+ }
8358
+ });
8359
+
8360
+ // Detect, normalize options and install callbacks for jsonp requests
8361
+ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
8362
+
8363
+ var callbackName, overwritten, responseContainer,
8364
+ jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
8365
+ "url" :
8366
+ typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
8367
+ );
8368
+
8369
+ // Handle iff the expected data type is "jsonp" or we have a parameter to set
8370
+ if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
8371
+
8372
+ // Get callback name, remembering preexisting value associated with it
8373
+ callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
8374
+ s.jsonpCallback() :
8375
+ s.jsonpCallback;
8376
+
8377
+ // Insert callback into url or form data
8378
+ if ( jsonProp ) {
8379
+ s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
8380
+ } else if ( s.jsonp !== false ) {
8381
+ s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
8382
+ }
8383
+
8384
+ // Use data converter to retrieve json after script execution
8385
+ s.converters["script json"] = function() {
8386
+ if ( !responseContainer ) {
8387
+ jQuery.error( callbackName + " was not called" );
8388
+ }
8389
+ return responseContainer[ 0 ];
8390
+ };
8391
+
8392
+ // force json dataType
8393
+ s.dataTypes[ 0 ] = "json";
8394
+
8395
+ // Install callback
8396
+ overwritten = window[ callbackName ];
8397
+ window[ callbackName ] = function() {
8398
+ responseContainer = arguments;
8399
+ };
8400
+
8401
+ // Clean-up function (fires after converters)
8402
+ jqXHR.always(function() {
8403
+ // Restore preexisting value
8404
+ window[ callbackName ] = overwritten;
8405
+
8406
+ // Save back as free
8407
+ if ( s[ callbackName ] ) {
8408
+ // make sure that re-using the options doesn't screw things around
8409
+ s.jsonpCallback = originalSettings.jsonpCallback;
8410
+
8411
+ // save the callback name for future use
8412
+ oldCallbacks.push( callbackName );
8413
+ }
8414
+
8415
+ // Call if it was a function and we have a response
8416
+ if ( responseContainer && jQuery.isFunction( overwritten ) ) {
8417
+ overwritten( responseContainer[ 0 ] );
8418
+ }
8419
+
8420
+ responseContainer = overwritten = undefined;
8421
+ });
8422
+
8423
+ // Delegate to script
8424
+ return "script";
8425
+ }
8426
+ });
8427
+ var xhrCallbacks, xhrSupported,
8428
+ xhrId = 0,
8429
+ // #5280: Internet Explorer will keep connections alive if we don't abort on unload
8430
+ xhrOnUnloadAbort = window.ActiveXObject && function() {
8431
+ // Abort all pending requests
8432
+ var key;
8433
+ for ( key in xhrCallbacks ) {
8434
+ xhrCallbacks[ key ]( undefined, true );
8435
+ }
8436
+ };
8437
+
8438
+ // Functions to create xhrs
8439
+ function createStandardXHR() {
8440
+ try {
8441
+ return new window.XMLHttpRequest();
8442
+ } catch( e ) {}
8443
+ }
8444
+
8445
+ function createActiveXHR() {
8446
+ try {
8447
+ return new window.ActiveXObject("Microsoft.XMLHTTP");
8448
+ } catch( e ) {}
8449
+ }
8450
+
8451
+ // Create the request object
8452
+ // (This is still attached to ajaxSettings for backward compatibility)
8453
+ jQuery.ajaxSettings.xhr = window.ActiveXObject ?
8454
+ /* Microsoft failed to properly
8455
+ * implement the XMLHttpRequest in IE7 (can't request local files),
8456
+ * so we use the ActiveXObject when it is available
8457
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
8458
+ * we need a fallback.
8459
+ */
8460
+ function() {
8461
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
8462
+ } :
8463
+ // For all other browsers, use the standard XMLHttpRequest object
8464
+ createStandardXHR;
8465
+
8466
+ // Determine support properties
8467
+ xhrSupported = jQuery.ajaxSettings.xhr();
8468
+ jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
8469
+ xhrSupported = jQuery.support.ajax = !!xhrSupported;
8470
+
8471
+ // Create transport if the browser can provide an xhr
8472
+ if ( xhrSupported ) {
8473
+
8474
+ jQuery.ajaxTransport(function( s ) {
8475
+ // Cross domain only allowed if supported through XMLHttpRequest
8476
+ if ( !s.crossDomain || jQuery.support.cors ) {
8477
+
8478
+ var callback;
8479
+
8480
+ return {
8481
+ send: function( headers, complete ) {
8482
+
8483
+ // Get a new xhr
8484
+ var handle, i,
8485
+ xhr = s.xhr();
8486
+
8487
+ // Open the socket
8488
+ // Passing null username, generates a login popup on Opera (#2865)
8489
+ if ( s.username ) {
8490
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
8491
+ } else {
8492
+ xhr.open( s.type, s.url, s.async );
8493
+ }
8494
+
8495
+ // Apply custom fields if provided
8496
+ if ( s.xhrFields ) {
8497
+ for ( i in s.xhrFields ) {
8498
+ xhr[ i ] = s.xhrFields[ i ];
8499
+ }
8500
+ }
8501
+
8502
+ // Override mime type if needed
8503
+ if ( s.mimeType && xhr.overrideMimeType ) {
8504
+ xhr.overrideMimeType( s.mimeType );
8505
+ }
8506
+
8507
+ // X-Requested-With header
8508
+ // For cross-domain requests, seeing as conditions for a preflight are
8509
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
8510
+ // (it can always be set on a per-request basis or even using ajaxSetup)
8511
+ // For same-domain requests, won't change header if already provided.
8512
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
8513
+ headers["X-Requested-With"] = "XMLHttpRequest";
8514
+ }
8515
+
8516
+ // Need an extra try/catch for cross domain requests in Firefox 3
8517
+ try {
8518
+ for ( i in headers ) {
8519
+ xhr.setRequestHeader( i, headers[ i ] );
8520
+ }
8521
+ } catch( err ) {}
8522
+
8523
+ // Do send the request
8524
+ // This may raise an exception which is actually
8525
+ // handled in jQuery.ajax (so no try/catch here)
8526
+ xhr.send( ( s.hasContent && s.data ) || null );
8527
+
8528
+ // Listener
8529
+ callback = function( _, isAbort ) {
8530
+ var status, responseHeaders, statusText, responses;
8531
+
8532
+ // Firefox throws exceptions when accessing properties
8533
+ // of an xhr when a network error occurred
8534
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
8535
+ try {
8536
+
8537
+ // Was never called and is aborted or complete
8538
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
8539
+
8540
+ // Only called once
8541
+ callback = undefined;
8542
+
8543
+ // Do not keep as active anymore
8544
+ if ( handle ) {
8545
+ xhr.onreadystatechange = jQuery.noop;
8546
+ if ( xhrOnUnloadAbort ) {
8547
+ delete xhrCallbacks[ handle ];
8548
+ }
8549
+ }
8550
+
8551
+ // If it's an abort
8552
+ if ( isAbort ) {
8553
+ // Abort it manually if needed
8554
+ if ( xhr.readyState !== 4 ) {
8555
+ xhr.abort();
8556
+ }
8557
+ } else {
8558
+ responses = {};
8559
+ status = xhr.status;
8560
+ responseHeaders = xhr.getAllResponseHeaders();
8561
+
8562
+ // When requesting binary data, IE6-9 will throw an exception
8563
+ // on any attempt to access responseText (#11426)
8564
+ if ( typeof xhr.responseText === "string" ) {
8565
+ responses.text = xhr.responseText;
8566
+ }
8567
+
8568
+ // Firefox throws an exception when accessing
8569
+ // statusText for faulty cross-domain requests
8570
+ try {
8571
+ statusText = xhr.statusText;
8572
+ } catch( e ) {
8573
+ // We normalize with Webkit giving an empty statusText
8574
+ statusText = "";
8575
+ }
8576
+
8577
+ // Filter status for non standard behaviors
8578
+
8579
+ // If the request is local and we have data: assume a success
8580
+ // (success with no data won't get notified, that's the best we
8581
+ // can do given current implementations)
8582
+ if ( !status && s.isLocal && !s.crossDomain ) {
8583
+ status = responses.text ? 200 : 404;
8584
+ // IE - #1450: sometimes returns 1223 when it should be 204
8585
+ } else if ( status === 1223 ) {
8586
+ status = 204;
8587
+ }
8588
+ }
8589
+ }
8590
+ } catch( firefoxAccessException ) {
8591
+ if ( !isAbort ) {
8592
+ complete( -1, firefoxAccessException );
8593
+ }
8594
+ }
8595
+
8596
+ // Call complete if needed
8597
+ if ( responses ) {
8598
+ complete( status, statusText, responses, responseHeaders );
8599
+ }
8600
+ };
8601
+
8602
+ if ( !s.async ) {
8603
+ // if we're in sync mode we fire the callback
8604
+ callback();
8605
+ } else if ( xhr.readyState === 4 ) {
8606
+ // (IE6 & IE7) if it's in cache and has been
8607
+ // retrieved directly we need to fire the callback
8608
+ setTimeout( callback );
8609
+ } else {
8610
+ handle = ++xhrId;
8611
+ if ( xhrOnUnloadAbort ) {
8612
+ // Create the active xhrs callbacks list if needed
8613
+ // and attach the unload handler
8614
+ if ( !xhrCallbacks ) {
8615
+ xhrCallbacks = {};
8616
+ jQuery( window ).unload( xhrOnUnloadAbort );
8617
+ }
8618
+ // Add to list of active xhrs callbacks
8619
+ xhrCallbacks[ handle ] = callback;
8620
+ }
8621
+ xhr.onreadystatechange = callback;
8622
+ }
8623
+ },
8624
+
8625
+ abort: function() {
8626
+ if ( callback ) {
8627
+ callback( undefined, true );
8628
+ }
8629
+ }
8630
+ };
8631
+ }
8632
+ });
8633
+ }
8634
+ var fxNow, timerId,
8635
+ rfxtypes = /^(?:toggle|show|hide)$/,
8636
+ rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
8637
+ rrun = /queueHooks$/,
8638
+ animationPrefilters = [ defaultPrefilter ],
8639
+ tweeners = {
8640
+ "*": [function( prop, value ) {
8641
+ var end, unit,
8642
+ tween = this.createTween( prop, value ),
8643
+ parts = rfxnum.exec( value ),
8644
+ target = tween.cur(),
8645
+ start = +target || 0,
8646
+ scale = 1,
8647
+ maxIterations = 20;
8648
+
8649
+ if ( parts ) {
8650
+ end = +parts[2];
8651
+ unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
8652
+
8653
+ // We need to compute starting value
8654
+ if ( unit !== "px" && start ) {
8655
+ // Iteratively approximate from a nonzero starting point
8656
+ // Prefer the current property, because this process will be trivial if it uses the same units
8657
+ // Fallback to end or a simple constant
8658
+ start = jQuery.css( tween.elem, prop, true ) || end || 1;
8659
+
8660
+ do {
8661
+ // If previous iteration zeroed out, double until we get *something*
8662
+ // Use a string for doubling factor so we don't accidentally see scale as unchanged below
8663
+ scale = scale || ".5";
8664
+
8665
+ // Adjust and apply
8666
+ start = start / scale;
8667
+ jQuery.style( tween.elem, prop, start + unit );
8668
+
8669
+ // Update scale, tolerating zero or NaN from tween.cur()
8670
+ // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
8671
+ } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
8672
+ }
8673
+
8674
+ tween.unit = unit;
8675
+ tween.start = start;
8676
+ // If a +=/-= token was provided, we're doing a relative animation
8677
+ tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
8678
+ }
8679
+ return tween;
8680
+ }]
8681
+ };
8682
+
8683
+ // Animations created synchronously will run synchronously
8684
+ function createFxNow() {
8685
+ setTimeout(function() {
8686
+ fxNow = undefined;
8687
+ });
8688
+ return ( fxNow = jQuery.now() );
8689
+ }
8690
+
8691
+ function createTweens( animation, props ) {
8692
+ jQuery.each( props, function( prop, value ) {
8693
+ var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
8694
+ index = 0,
8695
+ length = collection.length;
8696
+ for ( ; index < length; index++ ) {
8697
+ if ( collection[ index ].call( animation, prop, value ) ) {
8698
+
8699
+ // we're done with this property
8700
+ return;
8701
+ }
8702
+ }
8703
+ });
8704
+ }
8705
+
8706
+ function Animation( elem, properties, options ) {
8707
+ var result,
8708
+ stopped,
8709
+ index = 0,
8710
+ length = animationPrefilters.length,
8711
+ deferred = jQuery.Deferred().always( function() {
8712
+ // don't match elem in the :animated selector
8713
+ delete tick.elem;
8714
+ }),
8715
+ tick = function() {
8716
+ if ( stopped ) {
8717
+ return false;
8718
+ }
8719
+ var currentTime = fxNow || createFxNow(),
8720
+ remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
8721
+ // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
8722
+ temp = remaining / animation.duration || 0,
8723
+ percent = 1 - temp,
8724
+ index = 0,
8725
+ length = animation.tweens.length;
8726
+
8727
+ for ( ; index < length ; index++ ) {
8728
+ animation.tweens[ index ].run( percent );
8729
+ }
8730
+
8731
+ deferred.notifyWith( elem, [ animation, percent, remaining ]);
8732
+
8733
+ if ( percent < 1 && length ) {
8734
+ return remaining;
8735
+ } else {
8736
+ deferred.resolveWith( elem, [ animation ] );
8737
+ return false;
8738
+ }
8739
+ },
8740
+ animation = deferred.promise({
8741
+ elem: elem,
8742
+ props: jQuery.extend( {}, properties ),
8743
+ opts: jQuery.extend( true, { specialEasing: {} }, options ),
8744
+ originalProperties: properties,
8745
+ originalOptions: options,
8746
+ startTime: fxNow || createFxNow(),
8747
+ duration: options.duration,
8748
+ tweens: [],
8749
+ createTween: function( prop, end ) {
8750
+ var tween = jQuery.Tween( elem, animation.opts, prop, end,
8751
+ animation.opts.specialEasing[ prop ] || animation.opts.easing );
8752
+ animation.tweens.push( tween );
8753
+ return tween;
8754
+ },
8755
+ stop: function( gotoEnd ) {
8756
+ var index = 0,
8757
+ // if we are going to the end, we want to run all the tweens
8758
+ // otherwise we skip this part
8759
+ length = gotoEnd ? animation.tweens.length : 0;
8760
+ if ( stopped ) {
8761
+ return this;
8762
+ }
8763
+ stopped = true;
8764
+ for ( ; index < length ; index++ ) {
8765
+ animation.tweens[ index ].run( 1 );
8766
+ }
8767
+
8768
+ // resolve when we played the last frame
8769
+ // otherwise, reject
8770
+ if ( gotoEnd ) {
8771
+ deferred.resolveWith( elem, [ animation, gotoEnd ] );
8772
+ } else {
8773
+ deferred.rejectWith( elem, [ animation, gotoEnd ] );
8774
+ }
8775
+ return this;
8776
+ }
8777
+ }),
8778
+ props = animation.props;
8779
+
8780
+ propFilter( props, animation.opts.specialEasing );
8781
+
8782
+ for ( ; index < length ; index++ ) {
8783
+ result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
8784
+ if ( result ) {
8785
+ return result;
8786
+ }
8787
+ }
8788
+
8789
+ createTweens( animation, props );
8790
+
8791
+ if ( jQuery.isFunction( animation.opts.start ) ) {
8792
+ animation.opts.start.call( elem, animation );
8793
+ }
8794
+
8795
+ jQuery.fx.timer(
8796
+ jQuery.extend( tick, {
8797
+ elem: elem,
8798
+ anim: animation,
8799
+ queue: animation.opts.queue
8800
+ })
8801
+ );
8802
+
8803
+ // attach callbacks from options
8804
+ return animation.progress( animation.opts.progress )
8805
+ .done( animation.opts.done, animation.opts.complete )
8806
+ .fail( animation.opts.fail )
8807
+ .always( animation.opts.always );
8808
+ }
8809
+
8810
+ function propFilter( props, specialEasing ) {
8811
+ var value, name, index, easing, hooks;
8812
+
8813
+ // camelCase, specialEasing and expand cssHook pass
8814
+ for ( index in props ) {
8815
+ name = jQuery.camelCase( index );
8816
+ easing = specialEasing[ name ];
8817
+ value = props[ index ];
8818
+ if ( jQuery.isArray( value ) ) {
8819
+ easing = value[ 1 ];
8820
+ value = props[ index ] = value[ 0 ];
8821
+ }
8822
+
8823
+ if ( index !== name ) {
8824
+ props[ name ] = value;
8825
+ delete props[ index ];
8826
+ }
8827
+
8828
+ hooks = jQuery.cssHooks[ name ];
8829
+ if ( hooks && "expand" in hooks ) {
8830
+ value = hooks.expand( value );
8831
+ delete props[ name ];
8832
+
8833
+ // not quite $.extend, this wont overwrite keys already present.
8834
+ // also - reusing 'index' from above because we have the correct "name"
8835
+ for ( index in value ) {
8836
+ if ( !( index in props ) ) {
8837
+ props[ index ] = value[ index ];
8838
+ specialEasing[ index ] = easing;
8839
+ }
8840
+ }
8841
+ } else {
8842
+ specialEasing[ name ] = easing;
8843
+ }
8844
+ }
8845
+ }
8846
+
8847
+ jQuery.Animation = jQuery.extend( Animation, {
8848
+
8849
+ tweener: function( props, callback ) {
8850
+ if ( jQuery.isFunction( props ) ) {
8851
+ callback = props;
8852
+ props = [ "*" ];
8853
+ } else {
8854
+ props = props.split(" ");
8855
+ }
8856
+
8857
+ var prop,
8858
+ index = 0,
8859
+ length = props.length;
8860
+
8861
+ for ( ; index < length ; index++ ) {
8862
+ prop = props[ index ];
8863
+ tweeners[ prop ] = tweeners[ prop ] || [];
8864
+ tweeners[ prop ].unshift( callback );
8865
+ }
8866
+ },
8867
+
8868
+ prefilter: function( callback, prepend ) {
8869
+ if ( prepend ) {
8870
+ animationPrefilters.unshift( callback );
8871
+ } else {
8872
+ animationPrefilters.push( callback );
8873
+ }
8874
+ }
8875
+ });
8876
+
8877
+ function defaultPrefilter( elem, props, opts ) {
8878
+ /*jshint validthis:true */
8879
+ var prop, index, length,
8880
+ value, dataShow, toggle,
8881
+ tween, hooks, oldfire,
8882
+ anim = this,
8883
+ style = elem.style,
8884
+ orig = {},
8885
+ handled = [],
8886
+ hidden = elem.nodeType && isHidden( elem );
8887
+
8888
+ // handle queue: false promises
8889
+ if ( !opts.queue ) {
8890
+ hooks = jQuery._queueHooks( elem, "fx" );
8891
+ if ( hooks.unqueued == null ) {
8892
+ hooks.unqueued = 0;
8893
+ oldfire = hooks.empty.fire;
8894
+ hooks.empty.fire = function() {
8895
+ if ( !hooks.unqueued ) {
8896
+ oldfire();
8897
+ }
8898
+ };
8899
+ }
8900
+ hooks.unqueued++;
8901
+
8902
+ anim.always(function() {
8903
+ // doing this makes sure that the complete handler will be called
8904
+ // before this completes
8905
+ anim.always(function() {
8906
+ hooks.unqueued--;
8907
+ if ( !jQuery.queue( elem, "fx" ).length ) {
8908
+ hooks.empty.fire();
8909
+ }
8910
+ });
8911
+ });
8912
+ }
8913
+
8914
+ // height/width overflow pass
8915
+ if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
8916
+ // Make sure that nothing sneaks out
8917
+ // Record all 3 overflow attributes because IE does not
8918
+ // change the overflow attribute when overflowX and
8919
+ // overflowY are set to the same value
8920
+ opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
8921
+
8922
+ // Set display property to inline-block for height/width
8923
+ // animations on inline elements that are having width/height animated
8924
+ if ( jQuery.css( elem, "display" ) === "inline" &&
8925
+ jQuery.css( elem, "float" ) === "none" ) {
8926
+
8927
+ // inline-level elements accept inline-block;
8928
+ // block-level elements need to be inline with layout
8929
+ if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
8930
+ style.display = "inline-block";
8931
+
8932
+ } else {
8933
+ style.zoom = 1;
8934
+ }
8935
+ }
8936
+ }
8937
+
8938
+ if ( opts.overflow ) {
8939
+ style.overflow = "hidden";
8940
+ if ( !jQuery.support.shrinkWrapBlocks ) {
8941
+ anim.always(function() {
8942
+ style.overflow = opts.overflow[ 0 ];
8943
+ style.overflowX = opts.overflow[ 1 ];
8944
+ style.overflowY = opts.overflow[ 2 ];
8945
+ });
8946
+ }
8947
+ }
8948
+
8949
+
8950
+ // show/hide pass
8951
+ for ( index in props ) {
8952
+ value = props[ index ];
8953
+ if ( rfxtypes.exec( value ) ) {
8954
+ delete props[ index ];
8955
+ toggle = toggle || value === "toggle";
8956
+ if ( value === ( hidden ? "hide" : "show" ) ) {
8957
+ continue;
8958
+ }
8959
+ handled.push( index );
8960
+ }
8961
+ }
8962
+
8963
+ length = handled.length;
8964
+ if ( length ) {
8965
+ dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
8966
+ if ( "hidden" in dataShow ) {
8967
+ hidden = dataShow.hidden;
8968
+ }
8969
+
8970
+ // store state if its toggle - enables .stop().toggle() to "reverse"
8971
+ if ( toggle ) {
8972
+ dataShow.hidden = !hidden;
8973
+ }
8974
+ if ( hidden ) {
8975
+ jQuery( elem ).show();
8976
+ } else {
8977
+ anim.done(function() {
8978
+ jQuery( elem ).hide();
8979
+ });
8980
+ }
8981
+ anim.done(function() {
8982
+ var prop;
8983
+ jQuery._removeData( elem, "fxshow" );
8984
+ for ( prop in orig ) {
8985
+ jQuery.style( elem, prop, orig[ prop ] );
8986
+ }
8987
+ });
8988
+ for ( index = 0 ; index < length ; index++ ) {
8989
+ prop = handled[ index ];
8990
+ tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
8991
+ orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
8992
+
8993
+ if ( !( prop in dataShow ) ) {
8994
+ dataShow[ prop ] = tween.start;
8995
+ if ( hidden ) {
8996
+ tween.end = tween.start;
8997
+ tween.start = prop === "width" || prop === "height" ? 1 : 0;
8998
+ }
8999
+ }
9000
+ }
9001
+ }
9002
+ }
9003
+
9004
+ function Tween( elem, options, prop, end, easing ) {
9005
+ return new Tween.prototype.init( elem, options, prop, end, easing );
9006
+ }
9007
+ jQuery.Tween = Tween;
9008
+
9009
+ Tween.prototype = {
9010
+ constructor: Tween,
9011
+ init: function( elem, options, prop, end, easing, unit ) {
9012
+ this.elem = elem;
9013
+ this.prop = prop;
9014
+ this.easing = easing || "swing";
9015
+ this.options = options;
9016
+ this.start = this.now = this.cur();
9017
+ this.end = end;
9018
+ this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
9019
+ },
9020
+ cur: function() {
9021
+ var hooks = Tween.propHooks[ this.prop ];
9022
+
9023
+ return hooks && hooks.get ?
9024
+ hooks.get( this ) :
9025
+ Tween.propHooks._default.get( this );
9026
+ },
9027
+ run: function( percent ) {
9028
+ var eased,
9029
+ hooks = Tween.propHooks[ this.prop ];
9030
+
9031
+ if ( this.options.duration ) {
9032
+ this.pos = eased = jQuery.easing[ this.easing ](
9033
+ percent, this.options.duration * percent, 0, 1, this.options.duration
9034
+ );
9035
+ } else {
9036
+ this.pos = eased = percent;
9037
+ }
9038
+ this.now = ( this.end - this.start ) * eased + this.start;
9039
+
9040
+ if ( this.options.step ) {
9041
+ this.options.step.call( this.elem, this.now, this );
9042
+ }
9043
+
9044
+ if ( hooks && hooks.set ) {
9045
+ hooks.set( this );
9046
+ } else {
9047
+ Tween.propHooks._default.set( this );
9048
+ }
9049
+ return this;
9050
+ }
9051
+ };
9052
+
9053
+ Tween.prototype.init.prototype = Tween.prototype;
9054
+
9055
+ Tween.propHooks = {
9056
+ _default: {
9057
+ get: function( tween ) {
9058
+ var result;
9059
+
9060
+ if ( tween.elem[ tween.prop ] != null &&
9061
+ (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
9062
+ return tween.elem[ tween.prop ];
9063
+ }
9064
+
9065
+ // passing an empty string as a 3rd parameter to .css will automatically
9066
+ // attempt a parseFloat and fallback to a string if the parse fails
9067
+ // so, simple values such as "10px" are parsed to Float.
9068
+ // complex values such as "rotate(1rad)" are returned as is.
9069
+ result = jQuery.css( tween.elem, tween.prop, "" );
9070
+ // Empty strings, null, undefined and "auto" are converted to 0.
9071
+ return !result || result === "auto" ? 0 : result;
9072
+ },
9073
+ set: function( tween ) {
9074
+ // use step hook for back compat - use cssHook if its there - use .style if its
9075
+ // available and use plain properties where available
9076
+ if ( jQuery.fx.step[ tween.prop ] ) {
9077
+ jQuery.fx.step[ tween.prop ]( tween );
9078
+ } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
9079
+ jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
9080
+ } else {
9081
+ tween.elem[ tween.prop ] = tween.now;
9082
+ }
9083
+ }
9084
+ }
9085
+ };
9086
+
9087
+ // Remove in 2.0 - this supports IE8's panic based approach
9088
+ // to setting things on disconnected nodes
9089
+
9090
+ Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
9091
+ set: function( tween ) {
9092
+ if ( tween.elem.nodeType && tween.elem.parentNode ) {
9093
+ tween.elem[ tween.prop ] = tween.now;
9094
+ }
9095
+ }
9096
+ };
9097
+
9098
+ jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
9099
+ var cssFn = jQuery.fn[ name ];
9100
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
9101
+ return speed == null || typeof speed === "boolean" ?
9102
+ cssFn.apply( this, arguments ) :
9103
+ this.animate( genFx( name, true ), speed, easing, callback );
9104
+ };
9105
+ });
9106
+
9107
+ jQuery.fn.extend({
9108
+ fadeTo: function( speed, to, easing, callback ) {
9109
+
9110
+ // show any hidden elements after setting opacity to 0
9111
+ return this.filter( isHidden ).css( "opacity", 0 ).show()
9112
+
9113
+ // animate to the value specified
9114
+ .end().animate({ opacity: to }, speed, easing, callback );
9115
+ },
9116
+ animate: function( prop, speed, easing, callback ) {
9117
+ var empty = jQuery.isEmptyObject( prop ),
9118
+ optall = jQuery.speed( speed, easing, callback ),
9119
+ doAnimation = function() {
9120
+ // Operate on a copy of prop so per-property easing won't be lost
9121
+ var anim = Animation( this, jQuery.extend( {}, prop ), optall );
9122
+ doAnimation.finish = function() {
9123
+ anim.stop( true );
9124
+ };
9125
+ // Empty animations, or finishing resolves immediately
9126
+ if ( empty || jQuery._data( this, "finish" ) ) {
9127
+ anim.stop( true );
9128
+ }
9129
+ };
9130
+ doAnimation.finish = doAnimation;
9131
+
9132
+ return empty || optall.queue === false ?
9133
+ this.each( doAnimation ) :
9134
+ this.queue( optall.queue, doAnimation );
9135
+ },
9136
+ stop: function( type, clearQueue, gotoEnd ) {
9137
+ var stopQueue = function( hooks ) {
9138
+ var stop = hooks.stop;
9139
+ delete hooks.stop;
9140
+ stop( gotoEnd );
9141
+ };
9142
+
9143
+ if ( typeof type !== "string" ) {
9144
+ gotoEnd = clearQueue;
9145
+ clearQueue = type;
9146
+ type = undefined;
9147
+ }
9148
+ if ( clearQueue && type !== false ) {
9149
+ this.queue( type || "fx", [] );
9150
+ }
9151
+
9152
+ return this.each(function() {
9153
+ var dequeue = true,
9154
+ index = type != null && type + "queueHooks",
9155
+ timers = jQuery.timers,
9156
+ data = jQuery._data( this );
9157
+
9158
+ if ( index ) {
9159
+ if ( data[ index ] && data[ index ].stop ) {
9160
+ stopQueue( data[ index ] );
9161
+ }
9162
+ } else {
9163
+ for ( index in data ) {
9164
+ if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
9165
+ stopQueue( data[ index ] );
9166
+ }
9167
+ }
9168
+ }
9169
+
9170
+ for ( index = timers.length; index--; ) {
9171
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
9172
+ timers[ index ].anim.stop( gotoEnd );
9173
+ dequeue = false;
9174
+ timers.splice( index, 1 );
9175
+ }
9176
+ }
9177
+
9178
+ // start the next in the queue if the last step wasn't forced
9179
+ // timers currently will call their complete callbacks, which will dequeue
9180
+ // but only if they were gotoEnd
9181
+ if ( dequeue || !gotoEnd ) {
9182
+ jQuery.dequeue( this, type );
9183
+ }
9184
+ });
9185
+ },
9186
+ finish: function( type ) {
9187
+ if ( type !== false ) {
9188
+ type = type || "fx";
9189
+ }
9190
+ return this.each(function() {
9191
+ var index,
9192
+ data = jQuery._data( this ),
9193
+ queue = data[ type + "queue" ],
9194
+ hooks = data[ type + "queueHooks" ],
9195
+ timers = jQuery.timers,
9196
+ length = queue ? queue.length : 0;
9197
+
9198
+ // enable finishing flag on private data
9199
+ data.finish = true;
9200
+
9201
+ // empty the queue first
9202
+ jQuery.queue( this, type, [] );
9203
+
9204
+ if ( hooks && hooks.cur && hooks.cur.finish ) {
9205
+ hooks.cur.finish.call( this );
9206
+ }
9207
+
9208
+ // look for any active animations, and finish them
9209
+ for ( index = timers.length; index--; ) {
9210
+ if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
9211
+ timers[ index ].anim.stop( true );
9212
+ timers.splice( index, 1 );
9213
+ }
9214
+ }
9215
+
9216
+ // look for any animations in the old queue and finish them
9217
+ for ( index = 0; index < length; index++ ) {
9218
+ if ( queue[ index ] && queue[ index ].finish ) {
9219
+ queue[ index ].finish.call( this );
9220
+ }
9221
+ }
9222
+
9223
+ // turn off finishing flag
9224
+ delete data.finish;
9225
+ });
9226
+ }
9227
+ });
9228
+
9229
+ // Generate parameters to create a standard animation
9230
+ function genFx( type, includeWidth ) {
9231
+ var which,
9232
+ attrs = { height: type },
9233
+ i = 0;
9234
+
9235
+ // if we include width, step value is 1 to do all cssExpand values,
9236
+ // if we don't include width, step value is 2 to skip over Left and Right
9237
+ includeWidth = includeWidth? 1 : 0;
9238
+ for( ; i < 4 ; i += 2 - includeWidth ) {
9239
+ which = cssExpand[ i ];
9240
+ attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
9241
+ }
9242
+
9243
+ if ( includeWidth ) {
9244
+ attrs.opacity = attrs.width = type;
9245
+ }
9246
+
9247
+ return attrs;
9248
+ }
9249
+
9250
+ // Generate shortcuts for custom animations
9251
+ jQuery.each({
9252
+ slideDown: genFx("show"),
9253
+ slideUp: genFx("hide"),
9254
+ slideToggle: genFx("toggle"),
9255
+ fadeIn: { opacity: "show" },
9256
+ fadeOut: { opacity: "hide" },
9257
+ fadeToggle: { opacity: "toggle" }
9258
+ }, function( name, props ) {
9259
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
9260
+ return this.animate( props, speed, easing, callback );
9261
+ };
9262
+ });
9263
+
9264
+ jQuery.speed = function( speed, easing, fn ) {
9265
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
9266
+ complete: fn || !fn && easing ||
9267
+ jQuery.isFunction( speed ) && speed,
9268
+ duration: speed,
9269
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
9270
+ };
9271
+
9272
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
9273
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
9274
+
9275
+ // normalize opt.queue - true/undefined/null -> "fx"
9276
+ if ( opt.queue == null || opt.queue === true ) {
9277
+ opt.queue = "fx";
9278
+ }
9279
+
9280
+ // Queueing
9281
+ opt.old = opt.complete;
9282
+
9283
+ opt.complete = function() {
9284
+ if ( jQuery.isFunction( opt.old ) ) {
9285
+ opt.old.call( this );
9286
+ }
9287
+
9288
+ if ( opt.queue ) {
9289
+ jQuery.dequeue( this, opt.queue );
9290
+ }
9291
+ };
9292
+
9293
+ return opt;
9294
+ };
9295
+
9296
+ jQuery.easing = {
9297
+ linear: function( p ) {
9298
+ return p;
9299
+ },
9300
+ swing: function( p ) {
9301
+ return 0.5 - Math.cos( p*Math.PI ) / 2;
9302
+ }
9303
+ };
9304
+
9305
+ jQuery.timers = [];
9306
+ jQuery.fx = Tween.prototype.init;
9307
+ jQuery.fx.tick = function() {
9308
+ var timer,
9309
+ timers = jQuery.timers,
9310
+ i = 0;
9311
+
9312
+ fxNow = jQuery.now();
9313
+
9314
+ for ( ; i < timers.length; i++ ) {
9315
+ timer = timers[ i ];
9316
+ // Checks the timer has not already been removed
9317
+ if ( !timer() && timers[ i ] === timer ) {
9318
+ timers.splice( i--, 1 );
9319
+ }
9320
+ }
9321
+
9322
+ if ( !timers.length ) {
9323
+ jQuery.fx.stop();
9324
+ }
9325
+ fxNow = undefined;
9326
+ };
9327
+
9328
+ jQuery.fx.timer = function( timer ) {
9329
+ if ( timer() && jQuery.timers.push( timer ) ) {
9330
+ jQuery.fx.start();
9331
+ }
9332
+ };
9333
+
9334
+ jQuery.fx.interval = 13;
9335
+
9336
+ jQuery.fx.start = function() {
9337
+ if ( !timerId ) {
9338
+ timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
9339
+ }
9340
+ };
9341
+
9342
+ jQuery.fx.stop = function() {
9343
+ clearInterval( timerId );
9344
+ timerId = null;
9345
+ };
9346
+
9347
+ jQuery.fx.speeds = {
9348
+ slow: 600,
9349
+ fast: 200,
9350
+ // Default speed
9351
+ _default: 400
9352
+ };
9353
+
9354
+ // Back Compat <1.8 extension point
9355
+ jQuery.fx.step = {};
9356
+
9357
+ if ( jQuery.expr && jQuery.expr.filters ) {
9358
+ jQuery.expr.filters.animated = function( elem ) {
9359
+ return jQuery.grep(jQuery.timers, function( fn ) {
9360
+ return elem === fn.elem;
9361
+ }).length;
9362
+ };
9363
+ }
9364
+ jQuery.fn.offset = function( options ) {
9365
+ if ( arguments.length ) {
9366
+ return options === undefined ?
9367
+ this :
9368
+ this.each(function( i ) {
9369
+ jQuery.offset.setOffset( this, options, i );
9370
+ });
9371
+ }
9372
+
9373
+ var docElem, win,
9374
+ box = { top: 0, left: 0 },
9375
+ elem = this[ 0 ],
9376
+ doc = elem && elem.ownerDocument;
9377
+
9378
+ if ( !doc ) {
9379
+ return;
9380
+ }
9381
+
9382
+ docElem = doc.documentElement;
9383
+
9384
+ // Make sure it's not a disconnected DOM node
9385
+ if ( !jQuery.contains( docElem, elem ) ) {
9386
+ return box;
9387
+ }
9388
+
9389
+ // If we don't have gBCR, just use 0,0 rather than error
9390
+ // BlackBerry 5, iOS 3 (original iPhone)
9391
+ if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
9392
+ box = elem.getBoundingClientRect();
9393
+ }
9394
+ win = getWindow( doc );
9395
+ return {
9396
+ top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
9397
+ left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
9398
+ };
9399
+ };
9400
+
9401
+ jQuery.offset = {
9402
+
9403
+ setOffset: function( elem, options, i ) {
9404
+ var position = jQuery.css( elem, "position" );
9405
+
9406
+ // set position first, in-case top/left are set even on static elem
9407
+ if ( position === "static" ) {
9408
+ elem.style.position = "relative";
9409
+ }
9410
+
9411
+ var curElem = jQuery( elem ),
9412
+ curOffset = curElem.offset(),
9413
+ curCSSTop = jQuery.css( elem, "top" ),
9414
+ curCSSLeft = jQuery.css( elem, "left" ),
9415
+ calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
9416
+ props = {}, curPosition = {}, curTop, curLeft;
9417
+
9418
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
9419
+ if ( calculatePosition ) {
9420
+ curPosition = curElem.position();
9421
+ curTop = curPosition.top;
9422
+ curLeft = curPosition.left;
9423
+ } else {
9424
+ curTop = parseFloat( curCSSTop ) || 0;
9425
+ curLeft = parseFloat( curCSSLeft ) || 0;
9426
+ }
9427
+
9428
+ if ( jQuery.isFunction( options ) ) {
9429
+ options = options.call( elem, i, curOffset );
9430
+ }
9431
+
9432
+ if ( options.top != null ) {
9433
+ props.top = ( options.top - curOffset.top ) + curTop;
9434
+ }
9435
+ if ( options.left != null ) {
9436
+ props.left = ( options.left - curOffset.left ) + curLeft;
9437
+ }
9438
+
9439
+ if ( "using" in options ) {
9440
+ options.using.call( elem, props );
9441
+ } else {
9442
+ curElem.css( props );
9443
+ }
9444
+ }
9445
+ };
9446
+
9447
+
9448
+ jQuery.fn.extend({
9449
+
9450
+ position: function() {
9451
+ if ( !this[ 0 ] ) {
9452
+ return;
9453
+ }
9454
+
9455
+ var offsetParent, offset,
9456
+ parentOffset = { top: 0, left: 0 },
9457
+ elem = this[ 0 ];
9458
+
9459
+ // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
9460
+ if ( jQuery.css( elem, "position" ) === "fixed" ) {
9461
+ // we assume that getBoundingClientRect is available when computed position is fixed
9462
+ offset = elem.getBoundingClientRect();
9463
+ } else {
9464
+ // Get *real* offsetParent
9465
+ offsetParent = this.offsetParent();
9466
+
9467
+ // Get correct offsets
9468
+ offset = this.offset();
9469
+ if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
9470
+ parentOffset = offsetParent.offset();
9471
+ }
9472
+
9473
+ // Add offsetParent borders
9474
+ parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
9475
+ parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
9476
+ }
9477
+
9478
+ // Subtract parent offsets and element margins
9479
+ // note: when an element has margin: auto the offsetLeft and marginLeft
9480
+ // are the same in Safari causing offset.left to incorrectly be 0
9481
+ return {
9482
+ top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
9483
+ left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
9484
+ };
9485
+ },
9486
+
9487
+ offsetParent: function() {
9488
+ return this.map(function() {
9489
+ var offsetParent = this.offsetParent || document.documentElement;
9490
+ while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
9491
+ offsetParent = offsetParent.offsetParent;
9492
+ }
9493
+ return offsetParent || document.documentElement;
9494
+ });
9495
+ }
9496
+ });
9497
+
9498
+
9499
+ // Create scrollLeft and scrollTop methods
9500
+ jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
9501
+ var top = /Y/.test( prop );
9502
+
9503
+ jQuery.fn[ method ] = function( val ) {
9504
+ return jQuery.access( this, function( elem, method, val ) {
9505
+ var win = getWindow( elem );
9506
+
9507
+ if ( val === undefined ) {
9508
+ return win ? (prop in win) ? win[ prop ] :
9509
+ win.document.documentElement[ method ] :
9510
+ elem[ method ];
9511
+ }
9512
+
9513
+ if ( win ) {
9514
+ win.scrollTo(
9515
+ !top ? val : jQuery( win ).scrollLeft(),
9516
+ top ? val : jQuery( win ).scrollTop()
9517
+ );
9518
+
9519
+ } else {
9520
+ elem[ method ] = val;
9521
+ }
9522
+ }, method, val, arguments.length, null );
9523
+ };
9524
+ });
9525
+
9526
+ function getWindow( elem ) {
9527
+ return jQuery.isWindow( elem ) ?
9528
+ elem :
9529
+ elem.nodeType === 9 ?
9530
+ elem.defaultView || elem.parentWindow :
9531
+ false;
9532
+ }
9533
+ // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
9534
+ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
9535
+ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
9536
+ // margin is only for outerHeight, outerWidth
9537
+ jQuery.fn[ funcName ] = function( margin, value ) {
9538
+ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
9539
+ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
9540
+
9541
+ return jQuery.access( this, function( elem, type, value ) {
9542
+ var doc;
9543
+
9544
+ if ( jQuery.isWindow( elem ) ) {
9545
+ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
9546
+ // isn't a whole lot we can do. See pull request at this URL for discussion:
9547
+ // https://github.com/jquery/jquery/pull/764
9548
+ return elem.document.documentElement[ "client" + name ];
9549
+ }
9550
+
9551
+ // Get document width or height
9552
+ if ( elem.nodeType === 9 ) {
9553
+ doc = elem.documentElement;
9554
+
9555
+ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
9556
+ // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
9557
+ return Math.max(
9558
+ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
9559
+ elem.body[ "offset" + name ], doc[ "offset" + name ],
9560
+ doc[ "client" + name ]
9561
+ );
9562
+ }
9563
+
9564
+ return value === undefined ?
9565
+ // Get width or height on the element, requesting but not forcing parseFloat
9566
+ jQuery.css( elem, type, extra ) :
9567
+
9568
+ // Set width or height on the element
9569
+ jQuery.style( elem, type, value, extra );
9570
+ }, type, chainable ? margin : undefined, chainable, null );
9571
+ };
9572
+ });
9573
+ });
9574
+ // Limit scope pollution from any deprecated API
9575
+ // (function() {
9576
+
9577
+ // })();
9578
+ // Expose jQuery to the global object
9579
+ window.jQuery = jQuery;
9580
+
9581
+ // Expose jQuery as an AMD module, but only for AMD loaders that
9582
+ // understand the issues with loading multiple versions of jQuery
9583
+ // in a page that all might call define(). The loader will indicate
9584
+ // they have special allowances for multiple jQuery versions by
9585
+ // specifying define.amd.jQuery = true. Register as a named module,
9586
+ // since jQuery can be concatenated with other files that may use define,
9587
+ // but not use a proper concatenation script that understands anonymous
9588
+ // AMD modules. A named AMD is safest and most robust way to register.
9589
+ // Lowercase jquery is used because AMD module names are derived from
9590
+ // file names, and jQuery is normally delivered in a lowercase file name.
9591
+ // Do this after creating the global so that if an AMD module wants to call
9592
+ // noConflict to hide this version of jQuery, it will work.
9593
+ if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
9594
+ define( "jquery", [], function () { return jQuery; } );
9595
+ }
9596
+
9597
+ })( window );
skin/adminhtml/base/default/eurotext/translationmanager/js/eurotext.js ADDED
@@ -0,0 +1,898 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // allow utf8-detection: öäü€
2
+
3
+ jQuery(document).ready(function()
4
+ {
5
+ if (Translator==null)
6
+ {
7
+ return;
8
+ }
9
+
10
+ Translator.add('Do you really want to delete this project?','Möchten Sie dieses Projekt wirklich löschen?');
11
+ });
12
+
13
+ jQuery.fn.setPropAndAttr = function (attrName, attrValue) {
14
+ return this.each(function () {
15
+ jQuery(this).attr(attrName, attrValue);
16
+
17
+ if (jQuery(this).prop) {
18
+ jQuery(this).prop(attrName, attrValue);
19
+ }
20
+ });
21
+ };
22
+
23
+ function eurotext_closeme()
24
+ {
25
+ window.close();
26
+ return false;
27
+ }
28
+
29
+ function eurotext_confirmdelete()
30
+ {
31
+ if (confirm(Translator.translate("Do you really want to delete this project?"))==true)
32
+ {
33
+ return true;
34
+ }
35
+
36
+ return false;
37
+ }
38
+
39
+ function eurotext_confirmreset()
40
+ {
41
+ if (confirm(Translator.translate("Do you really want to reset this project?"))==true)
42
+ {
43
+ return true;
44
+ }
45
+
46
+ return false;
47
+ }
48
+
49
+ function eurotext_endsWith(str,suffix)
50
+ {
51
+ return str.indexOf(suffix, str.length - suffix.length) !== -1;
52
+ };
53
+
54
+ function eurotext_uploadzip()
55
+ {
56
+ var fname=jQuery("#zipfile").val();
57
+ fname=fname.toUpperCase();
58
+
59
+ if (eurotext_endsWith(fname,".ZIP"))
60
+ {
61
+ document.getElementById("zipfile_form").submit();
62
+ }
63
+ else
64
+ {
65
+ alert(Translator.translate("No file was selected, or the file selected is not a ZIP file"));
66
+ }
67
+ return false;
68
+ }
69
+
70
+ function eurotext_startimport()
71
+ {
72
+ jQuery("#eurotext_importbtt").attr("disabled",true);
73
+ jQuery("#eurotext_importprogress").html(Translator.translate("Processing - please wait …"));
74
+ eurotext_importstep_offset=0;
75
+
76
+ eurotext_importstep();
77
+
78
+ return false;
79
+ }
80
+
81
+ function eurotext_importstep_retry()
82
+ {
83
+ jQuery("#eurotext_importprogress").html(Translator.translate("Processing - please wait …"));
84
+
85
+ eurotext_importstep();
86
+
87
+ return false;
88
+ }
89
+
90
+ var eurotext_importstep_offset=0;
91
+ function eurotext_importstep()
92
+ {
93
+ var postdata=new Array();
94
+ postdata["form_key"]=eurotext_formkey;
95
+ postdata["project_id"]=eurotext_project_id;
96
+ postdata["offset"]=eurotext_importstep_offset;
97
+
98
+ var postparam=eurotext_eurotext_buildpostparam(postdata);
99
+
100
+ jQuery.ajax({
101
+ type: "POST",
102
+ url: eurotext_project_importstepurl,
103
+ data: postparam,
104
+ success: function(data)
105
+ {
106
+ var jsonData=null;
107
+
108
+ try
109
+ {
110
+ jsonData=jQuery.parseJSON(data);
111
+ }
112
+ catch(e)
113
+ {
114
+ jQuery("#eurotext_importprogress").html("<span style='color:red;font-weight:bold;'>"+e+"<br>"+data+"</span> [ <a href='#' onclick='return eurotext_importstep_retry()'>"+Translator.translate("Try again")+"</a> ]");
115
+ }
116
+
117
+ if (typeof jsonData !== "undefined")
118
+ {
119
+ if (jsonData.status_code=="ok")
120
+ {
121
+ jQuery("#eurotext_importprogress").html(jsonData.status_msg);
122
+
123
+ // Update positions:
124
+ eurotext_importstep_offset=jsonData.offset;
125
+
126
+ if (jsonData.finished=="0")
127
+ {
128
+ eurotext_importstep();
129
+ }
130
+ else
131
+ {
132
+ location.reload();
133
+ }
134
+ }
135
+ else
136
+ {
137
+ jQuery("#eurotext_importprogress").html("<span style='color:red;font-weight:bold;'>"+jsonData.status_msg+"</span> [ <a href='#' onclick='return eurotext_importstep_retry()'>"+Translator.translate("Try again")+"</a> ]");
138
+ }
139
+ }
140
+ },
141
+ error: function(data,textStatus,errorThrown)
142
+ {
143
+ jQuery("#eurotext_importprogress").html("<span style='color:red;font-weight:bold;'>"+errorThrown+"</span> [ <a href='#' onclick='return eurotext_importstep_retry()'>"+Translator.translate("Try again")+"</a> ]");
144
+ }
145
+ });
146
+
147
+ return false;
148
+ }
149
+
150
+
151
+ /************** eMail Templates ***********************************/
152
+ var eurotext_selectemails_ignore=false;
153
+ function eurotext_selectemail(field,file_hash)
154
+ {
155
+ if (eurotext_selectemails_ignore)
156
+ {
157
+ return false;
158
+ }
159
+
160
+ eurotext_selectemails_ignore=true;
161
+
162
+ var fieldName="#et_selemail"+field+"_"+file_hash;
163
+
164
+ var checkboxActive=jQuery(fieldName).is(":checked");
165
+ var checkboxActiveVal=checkboxActive ? "enabled" : "disabled";
166
+
167
+ jQuery(".et_selemail_"+file_hash).attr("checked",checkboxActive);
168
+
169
+ var postdata=new Array();
170
+ postdata['cnt']=1;
171
+ postdata['file_hash_0']=file_hash;
172
+ postdata['set_0']=checkboxActiveVal;
173
+
174
+ eurotext_selectedemails_send(postdata,function()
175
+ {
176
+ eurotext_selectemails_ignore=false;
177
+ });
178
+ return false;
179
+ }
180
+
181
+ function eurotext_selectedemails_send(postdata,doneFunc)
182
+ {
183
+ jQuery(".et_selemail").attr("disabled",true);
184
+ jQuery("#et_saveinfo").html(Translator.translate("Loading - please wait..."));
185
+
186
+ postdata["form_key"]=eurotext_formkey;
187
+ postdata['project_id']=eurotext_projectid;
188
+
189
+ var postparam=eurotext_eurotext_buildpostparam(postdata);
190
+
191
+ jQuery.ajax({
192
+ type: "POST",
193
+ url: eurotext_selectemails_saveurl,
194
+ data: postparam,
195
+ success: function(data)
196
+ {
197
+ jQuery("#selectedemails_result").html(data);
198
+ },
199
+ error: function(data,textStatus,errorThrown)
200
+ {
201
+ jQuery("#selectedemails_result").html(errorThrown);
202
+ },
203
+ complete: function()
204
+ {
205
+ jQuery(".et_selemail").attr("disabled",false);
206
+ jQuery("#et_saveinfo").html("");
207
+ if (doneFunc)
208
+ {
209
+ doneFunc();
210
+ }
211
+ }
212
+ });
213
+ return false;
214
+ }
215
+
216
+ /************** Language Files ***********************************/
217
+ var eurotext_selectlangfile_ignore=false;
218
+ function eurotext_selectlangfile(field,line_hash)
219
+ {
220
+ if (eurotext_selectlangfile_ignore)
221
+ {
222
+ return false;
223
+ }
224
+
225
+ eurotext_selectlangfile_ignore=true;
226
+
227
+ var fieldName="#et_sellangfile"+field+"_"+line_hash;
228
+
229
+ var checkboxActive=jQuery(fieldName).is(":checked");
230
+ var checkboxActiveVal=checkboxActive ? "enabled" : "disabled";
231
+
232
+ jQuery(".et_sellangfile_"+line_hash).attr("checked",checkboxActive);
233
+
234
+ var postdata=new Array();
235
+ postdata['cnt']=1;
236
+ postdata['langfile_linehash_0']=line_hash;
237
+ postdata['set_0']=checkboxActiveVal;
238
+
239
+ eurotext_selectedlangfiles_send(postdata,function()
240
+ {
241
+ eurotext_selectlangfile_ignore=false;
242
+ });
243
+ return false;
244
+ }
245
+
246
+ function eurotext_selectedlangfiles_send(postdata,doneFunc)
247
+ {
248
+ jQuery(".et_sellangfile").attr("disabled",true);
249
+ jQuery("#et_saveinfo").html(Translator.translate("Loading - please wait..."));
250
+
251
+ postdata["form_key"]=eurotext_formkey;
252
+ postdata['project_id']=eurotext_projectid;
253
+
254
+ var postparam=eurotext_eurotext_buildpostparam(postdata);
255
+
256
+ jQuery.ajax({
257
+ type: "POST",
258
+ url: eurotext_selectlangfiles_saveurl,
259
+ data: postparam,
260
+ success: function(data)
261
+ {
262
+ jQuery("#selectedlangfiles_result").html(data);
263
+ },
264
+ error: function(data,textStatus,errorThrown)
265
+ {
266
+ jQuery("#selectedlangfiles_result").html(errorThrown);
267
+ },
268
+ complete: function()
269
+ {
270
+ jQuery(".et_sellangfile").attr("disabled",false);
271
+ jQuery("#et_saveinfo").html("");
272
+ if (doneFunc)
273
+ {
274
+ doneFunc();
275
+ }
276
+ }
277
+ });
278
+ return false;
279
+ }
280
+
281
+ /************** CMS-Page ***********************************/
282
+ var eurotext_selectcmspage_ignore=false;
283
+ function eurotext_selectcmspage(field,cmspage_id)
284
+ {
285
+ if (eurotext_selectcmspage_ignore)
286
+ {
287
+ return false;
288
+ }
289
+
290
+ eurotext_selectcmspage_ignore=true;
291
+
292
+ var fieldName="#et_selcmspage"+field+"_"+cmspage_id;
293
+
294
+ var checkboxActive=jQuery(fieldName).is(":checked");
295
+ var checkboxActiveVal=checkboxActive ? "enabled" : "disabled";
296
+
297
+ jQuery(".et_selcmspage_"+cmspage_id).attr("checked",checkboxActive);
298
+
299
+ var postdata=new Array();
300
+ postdata['cnt_pages']=1;
301
+ postdata['page_id_0']=cmspage_id;
302
+ postdata['setpage_0']=checkboxActiveVal;
303
+
304
+ eurotext_selectedcmspages_send(postdata,function()
305
+ {
306
+ eurotext_selectcmspage_ignore=false;
307
+ });
308
+ return false;
309
+ }
310
+
311
+ function eurotext_selectcmsblock(field,cmsblock_id)
312
+ {
313
+ if (eurotext_selectcmspage_ignore)
314
+ {
315
+ return false;
316
+ }
317
+
318
+ eurotext_selectcmspage_ignore=true;
319
+
320
+ var fieldName="#et_selcmsblock"+field+"_"+cmsblock_id;
321
+
322
+ var checkboxActive=jQuery(fieldName).is(":checked");
323
+ var checkboxActiveVal=checkboxActive ? "enabled" : "disabled";
324
+
325
+ jQuery(".et_selcmsblock_"+cmsblock_id).attr("checked",checkboxActive);
326
+
327
+ var postdata=new Array();
328
+ postdata['cnt_blocks']=1;
329
+ postdata['block_id_0']=cmsblock_id;
330
+ postdata['setblock_0']=checkboxActiveVal;
331
+
332
+ eurotext_selectedcmspages_send(postdata,function()
333
+ {
334
+ eurotext_selectcmspage_ignore=false;
335
+ });
336
+ return false;
337
+ }
338
+
339
+ function eurotext_selectedcmspages_send(postdata,doneFunc)
340
+ {
341
+ jQuery(".et_selcmspage").attr("disabled",true);
342
+ jQuery(".et_selcmsblock").attr("disabled",true);
343
+ jQuery("#et_saveinfo").html(Translator.translate("Loading - please wait..."));
344
+
345
+ postdata["form_key"]=eurotext_formkey;
346
+ postdata['project_id']=eurotext_projectid;
347
+
348
+ var postparam=eurotext_eurotext_buildpostparam(postdata);
349
+
350
+ jQuery.ajax({
351
+ type: "POST",
352
+ url: eurotext_selectcmspages_saveurl,
353
+ data: postparam,
354
+ success: function(data)
355
+ {
356
+ jQuery("#selectedcmspages_result").html(data);
357
+ },
358
+ error: function(data,textStatus,errorThrown)
359
+ {
360
+ jQuery("#selectedcmspages_result").html(errorThrown);
361
+ },
362
+ complete: function()
363
+ {
364
+ jQuery(".et_selcmspage").attr("disabled",false);
365
+ jQuery(".et_selcmsblock").attr("disabled",false);
366
+ jQuery("#et_saveinfo").html("");
367
+ if (doneFunc)
368
+ {
369
+ doneFunc();
370
+ }
371
+ }
372
+ });
373
+ return false;
374
+ }
375
+
376
+ /************** Category ***********************************/
377
+ var eurotext_selectcategory_ignore=false;
378
+ function eurotext_selectcategory(field,category_id)
379
+ {
380
+ if (eurotext_selectcategory_ignore)
381
+ {
382
+ return;
383
+ }
384
+
385
+ eurotext_selectcategory_ignore=true;
386
+
387
+ var fieldName="#et_selcategory"+field+"_"+category_id;
388
+
389
+ jQuery(".et_selcategoryitem").attr("disabled",true);
390
+
391
+ var checkboxActive=jQuery(fieldName).is(":checked");
392
+ var checkboxActiveVal=checkboxActive ? "enabled" : "disabled";
393
+
394
+ jQuery(".et_selcategory_"+category_id).attr("checked",checkboxActive);
395
+
396
+ var postdata=new Array();
397
+ postdata['cnt']=1;
398
+ postdata['category_id_0']=category_id;
399
+ postdata['set_0']=checkboxActiveVal;
400
+
401
+ eurotext_selectedcategories_send(postdata,false,function()
402
+ {
403
+ eurotext_selectcategory_ignore=false;
404
+ this.location.reload();
405
+ });
406
+ return false;
407
+ }
408
+
409
+ function eurotext_selectedcategories_send(postdata,showResult,doneFunc)
410
+ {
411
+ jQuery(".et_selcategory").attr("disabled",true);
412
+ jQuery("#et_saveinfo").html(Translator.translate("Loading - please wait..."));
413
+
414
+ postdata["form_key"]=eurotext_formkey;
415
+ postdata['project_id']=eurotext_projectid;
416
+
417
+ var postparam=eurotext_eurotext_buildpostparam(postdata);
418
+
419
+ jQuery.ajax({
420
+ type: "POST",
421
+ url: eurotext_selectcategories_saveurl,
422
+ data: postparam,
423
+ success: function(data)
424
+ {
425
+ if (showResult)
426
+ {
427
+ jQuery("#selectedcategories_result").html(data);
428
+ }
429
+ },
430
+ error: function(data,textStatus,errorThrown)
431
+ {
432
+ if (showResult)
433
+ {
434
+ jQuery("#selectedcategories_result").html(errorThrown);
435
+ }
436
+ },
437
+ complete: function()
438
+ {
439
+ if (showResult)
440
+ {
441
+ jQuery("#et_saveinfo").html("");
442
+ }
443
+ if (doneFunc)
444
+ {
445
+ doneFunc();
446
+ }
447
+ }
448
+ });
449
+ return false;
450
+ }
451
+
452
+ /************** Product ***********************************/
453
+ var eurotext_selectproduct_ignore=false;
454
+ function eurotext_selectproduct(field,product_id)
455
+ {
456
+ if (eurotext_selectproduct_ignore)
457
+ {
458
+ return;
459
+ }
460
+
461
+ eurotext_selectproduct_ignore=true;
462
+
463
+ var fieldName="#et_selproduct"+field+"_"+product_id;
464
+
465
+ jQuery(".et_selproduct_"+product_id).attr("disabled",true);
466
+ jQuery(".et_selproductitem").attr("disabled",true);
467
+
468
+ var checkboxActive=jQuery(fieldName).is(":checked");
469
+ var checkboxActiveVal=checkboxActive ? "enabled" : "disabled";
470
+
471
+ jQuery(".et_selproduct_"+product_id).attr("checked",checkboxActive);
472
+
473
+ var postdata=new Array();
474
+ postdata['cnt']=1;
475
+ postdata['product_id_0']=product_id;
476
+ postdata['set_0']=checkboxActiveVal;
477
+
478
+ eurotext_selectedproducts_send(postdata,function()
479
+ {
480
+ jQuery(".et_selproduct_"+product_id).attr("disabled",false);
481
+ jQuery(".et_selproductitem").attr("disabled",false);
482
+
483
+ eurotext_selectproduct_ignore=false;
484
+ });
485
+ return false;
486
+ }
487
+
488
+ function eurotext_selectedproducts_send(postdata,doneFunc)
489
+ {
490
+ jQuery(".et_selproduct").attr("disabled",true);
491
+ jQuery("#et_saveinfo").html(Translator.translate("Loading - please wait..."));
492
+
493
+ postdata["form_key"]=eurotext_formkey;
494
+ postdata['project_id']=eurotext_projectid;
495
+ postdata['catids']=eurotext_catids;
496
+ postdata['filter_status']=jQuery("#filter_status").val();
497
+ postdata['filter_stock']=jQuery("#filter_stock").val();
498
+
499
+ var postparam=eurotext_eurotext_buildpostparam(postdata);
500
+
501
+ jQuery.ajax({
502
+ type: "POST",
503
+ url: eurotext_selectproducts_saveurl,
504
+ data: postparam,
505
+ success: function(data)
506
+ {
507
+ var htmldata=data['htmldata'];
508
+ var proddata=data['products'];
509
+ var catdata=data['categories'];
510
+ jQuery("#selectedproducts_result").html(htmldata);
511
+
512
+ jQuery(".et_selproductitem").setPropAndAttr("checked",false);
513
+ for(idx=0; idx<proddata.length; idx++)
514
+ {
515
+ jQuery(".et_selproduct_"+proddata[idx]).setPropAndAttr("checked",true);
516
+ }
517
+
518
+ jQuery(".eurotext_catsel").setPropAndAttr("checked",false);
519
+ jQuery(".eurotext_catsel").setPropAndAttr("inderminate",false);
520
+ for(idx=0; idx<catdata.length; idx++)
521
+ {
522
+ var catitem=catdata[idx];
523
+ console.log(catitem);
524
+ jQuery("#eurotext_catsel_"+catitem['id']).setPropAndAttr("checked",catitem['checked']);
525
+ jQuery("#eurotext_catsel_"+catitem['id']).setPropAndAttr("indeterminate",catitem['indeterminate']);
526
+
527
+ var catstate="unchecked";
528
+ if (catitem['checked']) { catstate="checked"};
529
+ if (catitem['indeterminate']) { catstate="indeterminate"};
530
+
531
+ jQuery("#eurotext_catsel_"+catitem['id']).setPropAndAttr("x-state",catstate);
532
+ }
533
+ },
534
+ error: function(data,textStatus,errorThrown)
535
+ {
536
+ jQuery("#selectedproducts_result").html("error: "+errorThrown);
537
+ },
538
+ complete: function()
539
+ {
540
+ jQuery("#et_saveinfo").html("");
541
+ if (doneFunc)
542
+ {
543
+ doneFunc();
544
+ }
545
+ }
546
+ });
547
+ return false;
548
+ }
549
+
550
+ function eurotext_findproducts()
551
+ {
552
+ var _pagesize=eurotext_pagesize;
553
+ var _page=eurotext_page;
554
+ var _catids=eurotext_catids;
555
+ var find=jQuery("#find_products").val();
556
+
557
+ var _filterstatus=jQuery("#filter_status").val();
558
+ var _filterstock=jQuery("#filter_stock").val();
559
+ var _filterproducttype=jQuery("#filter_product_type").val();
560
+
561
+ var url=eurotext_selectproducts_url+"find/"+encodeURIComponent(find)+"/pagesize/"+_pagesize+"/page/"+_page+"/catids/"+_catids+"/status/"+_filterstatus+"/stock/"+_filterstock+"/producttype/"+encodeURIComponent(_filterproducttype);
562
+ location.href=url;
563
+ return false;
564
+ }
565
+
566
+ function eurotext_savesettings()
567
+ {
568
+ jQuery("#eurotext_savebtt").html(Translator.translate("Please wait")+" …");
569
+
570
+ var postdata=new Array();
571
+ postdata["form_key"]=eurotext_formkey;
572
+ postdata["username"]=jQuery("#et_username").val();
573
+ postdata["password"]=jQuery("#et_password").val();
574
+ postdata["customerid"]=jQuery("#et_customerid").val();
575
+
576
+ postdata["et_products_per_file"]=jQuery("#et_products_per_file").val();
577
+ postdata["et_categories_per_file"]=jQuery("#et_categories_per_file").val();
578
+ postdata["et_cmspages_per_file"]=jQuery("#et_cmspages_per_file").val();
579
+
580
+ var postparam=eurotext_eurotext_buildpostparam(postdata);
581
+
582
+ jQuery.ajax({
583
+ type: "POST",
584
+ url: eurotext_saveurl,
585
+ data: postparam,
586
+ success: function(data)
587
+ {
588
+ location.reload();
589
+ },
590
+ error: function(data,textStatus,errorThrown)
591
+ {
592
+ //location.reload();
593
+ }
594
+ });
595
+
596
+ return false;
597
+ }
598
+
599
+ var eurotext_select2_url="";
600
+ var eurotext_select2_project_id=-1;
601
+
602
+ function eurotext_select(url,project_id, unsetCheckbox)
603
+ {
604
+ eurotext_select2_url=url;
605
+ eurotext_select2_project_id=project_id;
606
+
607
+ if (unsetCheckbox!=null)
608
+ {
609
+ // Checkbox mit ID unsetCheckbox deaktivieren
610
+ jQuery("#"+unsetCheckbox).get(0).checked=false;
611
+ }
612
+
613
+ eurotext_saveproject(eurotext_select2);
614
+
615
+ return false;
616
+ }
617
+
618
+ function eurotext_select2()
619
+ {
620
+ var w=window.open(eurotext_select2_url,"","width=900, height=600, status=yes, scrollbars=yes");
621
+ if (w==null)
622
+ {
623
+ alert(Translator.translate("Please deactivate your popup blocker for the Magento backend."));
624
+ }
625
+
626
+ return false;
627
+ }
628
+
629
+ function eurotext_sendproject()
630
+ {
631
+ jQuery("#eurotext_angebot_btt").attr("disabled","disabled");
632
+ jQuery("#eurotext_angebot_btt").val(Translator.translate("Please wait - this may take a while")+" …");
633
+ jQuery("#eurotext_sendprogress").html(Translator.translate("Please wait - saving project settings")+" …");
634
+ eurotext_saveproject(eurotext_sendproject_aftersave);
635
+ }
636
+
637
+ var eurotext_sendproject_step=0;
638
+ var eurotext_sendproject_offset=0;
639
+
640
+ function eurotext_sendproject_aftersave(id)
641
+ {
642
+ eurotext_sendproject_step=0;
643
+ eurotext_sendproject_offset=0;
644
+ jQuery("#eurotext_sendprogress").html(Translator.translate("Please wait - this may take a while")+" …");
645
+ eurotext_sendproject_singlestep();
646
+ }
647
+
648
+ function eurotext_sendproject_singlestep_retry()
649
+ {
650
+ jQuery("#eurotext_sendprogress").html(Translator.translate("Please wait - this may take a while")+" …");
651
+ eurotext_sendproject_singlestep();
652
+ return false;
653
+ }
654
+
655
+ function eurotext_sendproject_singlestep()
656
+ {
657
+ var postdata=new Array();
658
+ postdata["form_key"]=eurotext_formkey;
659
+ postdata["project_id"]=eurotext_project_id;
660
+ postdata["offset"]=eurotext_sendproject_offset;
661
+ postdata["step"]=eurotext_sendproject_step;
662
+
663
+ var postparam=eurotext_eurotext_buildpostparam(postdata);
664
+
665
+ jQuery.ajax({
666
+ type: "POST",
667
+ url: eurotext_project_sendurl,
668
+ data: postparam,
669
+ success: function(data)
670
+ {
671
+ var jsonData=null;
672
+
673
+ try
674
+ {
675
+ jsonData=jQuery.parseJSON(data);
676
+ }
677
+ catch(e)
678
+ {
679
+ jQuery("#eurotext_sendprogress").html("<span style='color:red;font-weight:bold;'>"+e+"<br>"+data+"</span> &nbsp; [ <a href='#' onclick='return eurotext_sendproject_singlestep_retry()'>"+Translator.translate("Try again")+"</a> ]");
680
+ jsonData=null;
681
+ }
682
+
683
+ if (jsonData!=null)
684
+ {
685
+ if (jsonData.status_code=="ok")
686
+ {
687
+ jQuery("#eurotext_sendprogress").html(jsonData.status_msg);
688
+
689
+ // Update positions:
690
+ eurotext_sendproject_step=jsonData.step;
691
+ eurotext_sendproject_offset=jsonData.offset;
692
+
693
+ if (jsonData.finished=="0")
694
+ {
695
+ eurotext_sendproject_singlestep();
696
+ }
697
+ else
698
+ {
699
+ location.reload();
700
+ }
701
+ }
702
+ else
703
+ {
704
+ jQuery("#eurotext_sendprogress").html("<span style='color:red;font-weight:bold;'>"+jsonData.status_msg+"</span> &nbsp; [ <a href='#' onclick='return eurotext_sendproject_singlestep_retry()'>"+Translator.translate("Try again")+"</a> ]");
705
+ }
706
+ }
707
+ },
708
+ error: function(data,textStatus,errorThrown)
709
+ {
710
+ jQuery("#eurotext_sendprogress").html("<span style='color:red;font-weight:bold;'>"+errorThrown+"</span> &nbsp; [ <a href='#' onclick='return eurotext_sendproject_singlestep_retry()'>"+Translator.translate("Try again")+"</a> ]");
711
+ }
712
+ });
713
+
714
+ return false;
715
+ }
716
+
717
+ function eurotext_reloadpage()
718
+ {
719
+ location.reload();
720
+ return false;
721
+ }
722
+
723
+ function eurotext_eurotext_buildpostparam(postdata)
724
+ {
725
+ var postparam="";
726
+
727
+ var i=0;
728
+ for(var pKey in postdata)
729
+ {
730
+ if (postdata.hasOwnProperty(pKey))
731
+ {
732
+ if (i>0)
733
+ {
734
+ postparam+="&";
735
+ }
736
+
737
+ postparam+=encodeURIComponent(pKey);
738
+ postparam+="=";
739
+ postparam+=encodeURIComponent(postdata[pKey]);
740
+ i++;
741
+ }
742
+ }
743
+
744
+ return postparam;
745
+ }
746
+
747
+ var layer_div=null;
748
+ var layer_div2=null;
749
+
750
+ function eurotext_closelayer()
751
+ {
752
+ if (layer_div!=null)
753
+ {
754
+ jQuery(layer_div).remove();
755
+ jQuery(layer_div2).remove();
756
+
757
+ layer_div=null;
758
+ layer_div2=null;
759
+ }
760
+ }
761
+
762
+ function eurotext_openlayer(content)
763
+ {
764
+ if (layer_div!=null)
765
+ {
766
+ eurotext_closelayer();
767
+ }
768
+
769
+ layer_div=document.createElement("div");
770
+ document.body.appendChild(layer_div);
771
+
772
+ layer_div.style.width="100%";
773
+ layer_div.style.height="100%";
774
+ layer_div.style.top="0px";
775
+ layer_div.style.left="0px";
776
+ layer_div.style.backgroundColor="white";
777
+ layer_div.style.opacity="0.8";
778
+ layer_div.style.position="fixed";
779
+ layer_div.style.display="block";
780
+ layer_div.style.zIndex=9998;
781
+
782
+ layer_div2=document.createElement("div");
783
+ document.body.appendChild(layer_div2);
784
+
785
+ layer_div2.style.top="0px";
786
+ layer_div2.style.left="0px";
787
+ layer_div2.style.position="fixed";
788
+ layer_div2.style.color="black";
789
+ layer_div2.style.fontWeight="bold";
790
+ layer_div2.style.display="block";
791
+ layer_div2.style.zIndex=9999;
792
+ layer_div2.innerHTML=content;
793
+
794
+ var x=jQuery(layer_div).width()-jQuery(layer_div2).width();
795
+ x=x/2;
796
+
797
+ var y=jQuery(layer_div).height()-jQuery(layer_div2).height();
798
+ y=y/2;
799
+
800
+ layer_div2.style.left=x+"px";
801
+ layer_div2.style.top=y+"px";
802
+ }
803
+
804
+ function eurotext_saveproject(callback_func)
805
+ {
806
+ eurotext_openlayer(Translator.translate("Please wait - saving project settings")+" …");
807
+
808
+ jQuery("#eurotext_saving").html(Translator.translate("Please wait - saving project settings")+" …");
809
+ jQuery("#btt_saveproject").attr('disabled', 'disabled');
810
+ jQuery("#btt_saveproject").val(Translator.translate("Please wait")+" …");
811
+
812
+ var postdata=new Array();
813
+ postdata["form_key"]=eurotext_formkey;
814
+ postdata["id"]=eurotext_project_id;
815
+ postdata["project_name"]=jQuery("#form_project_name").val();
816
+ postdata["storeview_src"]=jQuery("#form_storeview_src").val();
817
+ postdata["storeview_dst"]=jQuery("#form_storeview_dst").val();
818
+ postdata["langfilesmode"]=jQuery("#form_langfilesmode").is(":checked") ? "1":"0";
819
+ postdata["export_seo"]=jQuery("#form_export_seo").is(":checked") ? "1":"0";
820
+ postdata["export_attributes"]=jQuery("#form_export_attributes").is(":checked") ? "1":"0";
821
+ postdata["export_urlkeys"]=jQuery("#form_export_urlkeys").is(":checked") ? "1":"0";
822
+ postdata["productmode"]=jQuery("#form_productmode").is(":checked") ? "1":"0";
823
+ postdata["categorymode"]=jQuery("#form_categorymode").is(":checked") ? "1":"0";
824
+ postdata["cmsmode"]=jQuery("#form_cmsmode").is(":checked") ? "1":"0";
825
+ postdata["templatemode"]=jQuery("#form_templatemode").is(":checked") ? "1":"0";
826
+
827
+ var postparam=eurotext_eurotext_buildpostparam(postdata);
828
+
829
+ jQuery.ajax({
830
+ type: "POST",
831
+ url: eurotext_project_saveurl,
832
+ data: postparam,
833
+ success: function(data)
834
+ {
835
+ var jsonData=null;
836
+ try
837
+ {
838
+ jsonData=jQuery.parseJSON(data);
839
+ }
840
+ catch(e)
841
+ {
842
+ jQuery("#eurotext_saving").html("<span style='color:red;font-weight:bold;'>"+e+"</span>");
843
+ jsonData=null;
844
+ }
845
+
846
+ if (jsonData !== null)
847
+ {
848
+ if (jsonData.status_code=="ok")
849
+ {
850
+ jQuery("#eurotext_saving").html(jsonData.status_msg);
851
+
852
+ if (callback_func)
853
+ {
854
+ callback_func();
855
+ }
856
+ }
857
+ else
858
+ {
859
+ jQuery("#eurotext_saving").html("<span style='color:red;font-weight:bold;'>"+jsonData.status_msg+"</span>");
860
+ }
861
+ }
862
+ else
863
+ {
864
+ jQuery("#eurotext_saving").html("<span style='color:red;font-weight:bold;'>"+data+"</span>");
865
+ }
866
+ },
867
+ error: function(data,textStatus,errorThrown)
868
+ {
869
+ jQuery("#eurotext_saving").html("<span style='color:red;font-weight:bold;'>"+Translator.translate("Unable to save project settings")+": "+errorThrown+"</span>");
870
+ },
871
+ complete: function()
872
+ {
873
+ jQuery("#btt_saveproject").removeAttr('disabled');
874
+ jQuery("#btt_saveproject").val(Translator.translate("Save project"));
875
+ eurotext_closelayer();
876
+ }
877
+ });
878
+
879
+ return false;
880
+ }
881
+
882
+ function eurotext_unlock2()
883
+ {
884
+ return eurotext_unlock();
885
+ }
886
+
887
+ function eurotext_unlock()
888
+ {
889
+ jQuery("#et_form").find("input, select").each(function()
890
+ {
891
+ jQuery(this).attr("disabled",false);
892
+ });
893
+
894
+ jQuery("#register_change").hide();
895
+ jQuery("#register_submit").attr("disabled",false);
896
+
897
+ return false;
898
+ }