WP External Links (nofollow new tab seo) - Version 2.0.0

Version Description

  • REQUIREMENTS: PHP 5.3+
  • Complete rebuilt
  • Added noopener and noreferrer
  • Added font icons (font awesome and dashicons)
  • Added options for internal links
  • Added Network settings (WPMU support)
  • Contribution: David Page solving bug home_url()
Download this release

Release Info

Developer freelancephp
Plugin Icon 128x128 WP External Links (nofollow new tab seo)
Version 2.0.0
Comparing to
See all releases

Code changes from version 1.81 to 2.0.0

Files changed (83) hide show
  1. LICENSE +0 -20
  2. README.md +0 -6
  3. data/json/dashicons.json +1 -0
  4. data/json/fontawesome.json +1 -0
  5. images/icon-email-encoder-bundle-16.png +0 -0
  6. images/icon-wp-external-links-16.png +0 -0
  7. images/icon-wp-external-links-32.png +0 -0
  8. images/icon-wp-mailto-links-16.png +0 -0
  9. images/tipsy.gif +0 -0
  10. {js/src → includes}/.htaccess +0 -0
  11. includes/admin/class-wpel-network-page.php +202 -0
  12. includes/admin/class-wpel-settings-page.php +245 -0
  13. includes/admin/network-fields/class-wpel-network-admin-fields.php +114 -0
  14. includes/admin/network-fields/class-wpel-network-fields.php +130 -0
  15. includes/admin/settings-fields/class-wpel-admin-fields.php +78 -0
  16. includes/admin/settings-fields/class-wpel-exceptions-fields.php +204 -0
  17. includes/admin/settings-fields/class-wpel-external-link-fields.php +41 -0
  18. includes/admin/settings-fields/class-wpel-internal-link-fields.php +40 -0
  19. includes/admin/settings-fields/class-wpel-link-fields-base.php +337 -0
  20. includes/class-admin-external-links.php +0 -608
  21. includes/class-wp-external-links.php +0 -548
  22. includes/class-wpel-front-ignore.php +155 -0
  23. includes/class-wpel-front.php +443 -0
  24. includes/class-wpel-link.php +81 -0
  25. includes/class-wpel-plugin.php +86 -0
  26. includes/class-wpel-registerhooks.php +219 -0
  27. includes/class-wpel-template-tags.php +46 -0
  28. includes/class-wpel-textdomain.php +26 -0
  29. includes/phpQuery.php +0 -5702
  30. includes/wp-plugin-dev-classes/class-wp-meta-box-page.php +0 -664
  31. includes/wp-plugin-dev-classes/class-wp-option-forms.php +0 -374
  32. js/admin-wp-external-links.js +0 -232
  33. js/src/admin-wp-external-links.js +0 -232
  34. js/src/wp-external-links.js +0 -69
  35. js/src/wp-option-forms.js +0 -55
  36. js/wp-external-links.js +0 -2
  37. libs/fwp/class-fwp-debug.php +112 -0
  38. libs/fwp/class-fwp-dom-element.php +249 -0
  39. libs/fwp/class-fwp-html-fields.php +264 -0
  40. libs/fwp/class-fwp-settings-section-fields.php +249 -0
  41. libs/fwp/filter-hooks/class-fwp-final-output.php +45 -0
  42. libs/fwp/filter-hooks/class-fwp-widget-output.php +82 -0
  43. libs/wprun/class-wprun-autoloader.php +115 -0
  44. libs/wprun/class-wprun-base.php +402 -0
  45. public/css/wpel-admin.css +79 -0
  46. public/css/wpel.css +58 -0
  47. {images → public/images}/link-icon-example.png +0 -0
  48. images/ext-icons/ext-icon-1.png → public/images/wpel-icons/icon-1.png +0 -0
  49. images/ext-icons/ext-icon-10.png → public/images/wpel-icons/icon-10.png +0 -0
  50. images/ext-icons/ext-icon-11.png → public/images/wpel-icons/icon-11.png +0 -0
  51. images/ext-icons/ext-icon-12.png → public/images/wpel-icons/icon-12.png +0 -0
  52. images/ext-icons/ext-icon-13.png → public/images/wpel-icons/icon-13.png +0 -0
  53. images/ext-icons/ext-icon-14.png → public/images/wpel-icons/icon-14.png +0 -0
  54. images/ext-icons/ext-icon-15.png → public/images/wpel-icons/icon-15.png +0 -0
  55. images/ext-icons/ext-icon-16.png → public/images/wpel-icons/icon-16.png +0 -0
  56. images/ext-icons/ext-icon-17.png → public/images/wpel-icons/icon-17.png +0 -0
  57. images/ext-icons/ext-icon-18.png → public/images/wpel-icons/icon-18.png +0 -0
  58. images/ext-icons/ext-icon-19.png → public/images/wpel-icons/icon-19.png +0 -0
  59. images/ext-icons/ext-icon-2.png → public/images/wpel-icons/icon-2.png +0 -0
  60. images/ext-icons/ext-icon-20.png → public/images/wpel-icons/icon-20.png +0 -0
  61. images/ext-icons/ext-icon-3.png → public/images/wpel-icons/icon-3.png +0 -0
  62. images/ext-icons/ext-icon-4.png → public/images/wpel-icons/icon-4.png +0 -0
  63. images/ext-icons/ext-icon-5.png → public/images/wpel-icons/icon-5.png +0 -0
  64. images/ext-icons/ext-icon-6.png → public/images/wpel-icons/icon-6.png +0 -0
  65. images/ext-icons/ext-icon-7.png → public/images/wpel-icons/icon-7.png +0 -0
  66. images/ext-icons/ext-icon-8.png → public/images/wpel-icons/icon-8.png +0 -0
  67. images/ext-icons/ext-icon-9.png → public/images/wpel-icons/icon-9.png +0 -0
  68. public/js/wpel-admin.js +108 -0
  69. readme.txt +119 -117
  70. screenshot-1.png +0 -0
  71. screenshot-2.png +0 -0
  72. screenshot-3.png +0 -0
  73. templates/network-page/help-tabs/under-construction.php +17 -0
  74. templates/network-page/main.php +63 -0
  75. templates/partials/nav-tabs.php +30 -0
  76. templates/partials/tab-contents/fields-default.php +26 -0
  77. templates/partials/tab-contents/support.php +72 -0
  78. templates/requirements-notice.php +22 -0
  79. templates/settings-page/help-tabs/data-attributes.php +23 -0
  80. templates/settings-page/help-tabs/under-construction.php +17 -0
  81. templates/settings-page/main.php +51 -0
  82. templates/settings-page/tab-contents/exceptions.php +26 -0
  83. wp-external-links.php +68 -56
LICENSE DELETED
@@ -1,20 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2014 Victor Villaverde Laan
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of
6
- this software and associated documentation files (the "Software"), to deal in
7
- the Software without restriction, including without limitation the rights to
8
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
- the Software, and to permit persons to whom the Software is furnished to do so,
10
- subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md DELETED
@@ -1,6 +0,0 @@
1
- WP-External-Links
2
- =================
3
-
4
- Put it on Github so everybody can contribute code changes.
5
-
6
- See http://wordpress.org/plugins/wp-external-links/
 
 
 
 
 
 
data/json/dashicons.json ADDED
@@ -0,0 +1 @@
 
1
+ {"icons":[{"unicode":"f333","className":"dashicons-menu"},{"unicode":"f319","className":"dashicons-admin-site"},{"unicode":"f226","className":"dashicons-dashboard"},{"unicode":"f109","className":"dashicons-admin-post"},{"unicode":"f104","className":"dashicons-admin-media"},{"unicode":"f103","className":"dashicons-admin-links"},{"unicode":"f105","className":"dashicons-admin-page"},{"unicode":"f101","className":"dashicons-admin-comments"},{"unicode":"f100","className":"dashicons-admin-appearance"},{"unicode":"f106","className":"dashicons-admin-plugins"},{"unicode":"f110","className":"dashicons-admin-users"},{"unicode":"f107","className":"dashicons-admin-tools"},{"unicode":"f108","className":"dashicons-admin-settings"},{"unicode":"f112","className":"dashicons-admin-network"},{"unicode":"f102","className":"dashicons-admin-home"},{"unicode":"f111","className":"dashicons-admin-generic"},{"unicode":"f148","className":"dashicons-admin-collapse"},{"unicode":"f536","className":"dashicons-filter"},{"unicode":"f540","className":"dashicons-admin-customizer"},{"unicode":"f541","className":"dashicons-admin-multisite"},{"unicode":"f119","className":"dashicons-welcome-write-blog"},{"unicode":"f133","className":"dashicons-welcome-add-page"},{"unicode":"f115","className":"dashicons-welcome-view-site"},{"unicode":"f116","className":"dashicons-welcome-widgets-menus"},{"unicode":"f117","className":"dashicons-welcome-comments"},{"unicode":"f118","className":"dashicons-welcome-learn-more"},{"unicode":"f123","className":"dashicons-format-aside"},{"unicode":"f128","className":"dashicons-format-image"},{"unicode":"f161","className":"dashicons-format-gallery"},{"unicode":"f126","className":"dashicons-format-video"},{"unicode":"f130","className":"dashicons-format-status"},{"unicode":"f122","className":"dashicons-format-quote"},{"unicode":"f125","className":"dashicons-format-chat"},{"unicode":"f127","className":"dashicons-format-audio"},{"unicode":"f306","className":"dashicons-camera"},{"unicode":"f232","className":"dashicons-images-alt"},{"unicode":"f233","className":"dashicons-images-alt2"},{"unicode":"f234","className":"dashicons-video-alt"},{"unicode":"f235","className":"dashicons-video-alt2"},{"unicode":"f236","className":"dashicons-video-alt3"},{"unicode":"f501","className":"dashicons-media-archive"},{"unicode":"f500","className":"dashicons-media-audio"},{"unicode":"f499","className":"dashicons-media-code"},{"unicode":"f498","className":"dashicons-media-default"},{"unicode":"f497","className":"dashicons-media-document"},{"unicode":"f496","className":"dashicons-media-interactive"},{"unicode":"f495","className":"dashicons-media-spreadsheet"},{"unicode":"f491","className":"dashicons-media-text"},{"unicode":"f490","className":"dashicons-media-video"},{"unicode":"f492","className":"dashicons-playlist-audio"},{"unicode":"f493","className":"dashicons-playlist-video"},{"unicode":"f522","className":"dashicons-controls-play"},{"unicode":"f523","className":"dashicons-controls-pause"},{"unicode":"f519","className":"dashicons-controls-forward"},{"unicode":"f517","className":"dashicons-controls-skipforward"},{"unicode":"f518","className":"dashicons-controls-back"},{"unicode":"f516","className":"dashicons-controls-skipback"},{"unicode":"f515","className":"dashicons-controls-repeat"},{"unicode":"f521","className":"dashicons-controls-volumeon"},{"unicode":"f520","className":"dashicons-controls-volumeoff"},{"unicode":"f165","className":"dashicons-image-crop"},{"unicode":"f531","className":"dashicons-image-rotate"},{"unicode":"f166","className":"dashicons-image-rotate-left"},{"unicode":"f167","className":"dashicons-image-rotate-right"},{"unicode":"f168","className":"dashicons-image-flip-vertical"},{"unicode":"f169","className":"dashicons-image-flip-horizontal"},{"unicode":"f533","className":"dashicons-image-filter"},{"unicode":"f171","className":"dashicons-undo"},{"unicode":"f172","className":"dashicons-redo"},{"unicode":"f200","className":"dashicons-editor-bold"},{"unicode":"f201","className":"dashicons-editor-italic"},{"unicode":"f203","className":"dashicons-editor-ul"},{"unicode":"f204","className":"dashicons-editor-ol"},{"unicode":"f205","className":"dashicons-editor-quote"},{"unicode":"f206","className":"dashicons-editor-alignleft"},{"unicode":"f207","className":"dashicons-editor-aligncenter"},{"unicode":"f208","className":"dashicons-editor-alignright"},{"unicode":"f209","className":"dashicons-editor-insertmore"},{"unicode":"f210","className":"dashicons-editor-spellcheck"},{"unicode":"f211","className":"dashicons-editor-expand"},{"unicode":"f506","className":"dashicons-editor-contract"},{"unicode":"f212","className":"dashicons-editor-kitchensink"},{"unicode":"f213","className":"dashicons-editor-underline"},{"unicode":"f214","className":"dashicons-editor-justify"},{"unicode":"f215","className":"dashicons-editor-textcolor"},{"unicode":"f216","className":"dashicons-editor-paste-word"},{"unicode":"f217","className":"dashicons-editor-paste-text"},{"unicode":"f218","className":"dashicons-editor-removeformatting"},{"unicode":"f219","className":"dashicons-editor-video"},{"unicode":"f220","className":"dashicons-editor-customchar"},{"unicode":"f221","className":"dashicons-editor-outdent"},{"unicode":"f222","className":"dashicons-editor-indent"},{"unicode":"f223","className":"dashicons-editor-help"},{"unicode":"f224","className":"dashicons-editor-strikethrough"},{"unicode":"f225","className":"dashicons-editor-unlink"},{"unicode":"f320","className":"dashicons-editor-rtl"},{"unicode":"f474","className":"dashicons-editor-break"},{"unicode":"f475","className":"dashicons-editor-code"},{"unicode":"f476","className":"dashicons-editor-paragraph"},{"unicode":"f535","className":"dashicons-editor-table"},{"unicode":"f135","className":"dashicons-align-left"},{"unicode":"f136","className":"dashicons-align-right"},{"unicode":"f134","className":"dashicons-align-center"},{"unicode":"f138","className":"dashicons-align-none"},{"unicode":"f160","className":"dashicons-lock"},{"unicode":"f528","className":"dashicons-unlock"},{"unicode":"f145","className":"dashicons-calendar"},{"unicode":"f508","className":"dashicons-calendar-alt"},{"unicode":"f177","className":"dashicons-visibility"},{"unicode":"f530","className":"dashicons-hidden"},{"unicode":"f173","className":"dashicons-post-status"},{"unicode":"f464","className":"dashicons-edit"},{"unicode":"f182","className":"dashicons-trash"},{"unicode":"f537","className":"dashicons-sticky"},{"unicode":"f504","className":"dashicons-external"},{"unicode":"f142","className":"dashicons-arrow-up"},{"unicode":"f140","className":"dashicons-arrow-down"},{"unicode":"f139","className":"dashicons-arrow-right"},{"unicode":"f141","className":"dashicons-arrow-left"},{"unicode":"f342","className":"dashicons-arrow-up-alt"},{"unicode":"f346","className":"dashicons-arrow-down-alt"},{"unicode":"f344","className":"dashicons-arrow-right-alt"},{"unicode":"f340","className":"dashicons-arrow-left-alt"},{"unicode":"f343","className":"dashicons-arrow-up-alt2"},{"unicode":"f347","className":"dashicons-arrow-down-alt2"},{"unicode":"f345","className":"dashicons-arrow-right-alt2"},{"unicode":"f341","className":"dashicons-arrow-left-alt2"},{"unicode":"f156","className":"dashicons-sort"},{"unicode":"f229","className":"dashicons-leftright"},{"unicode":"f503","className":"dashicons-randomize"},{"unicode":"f163","className":"dashicons-list-view"},{"unicode":"f164","className":"dashicons-exerpt-view"},{"unicode":"f509","className":"dashicons-grid-view"},{"unicode":"f237","className":"dashicons-share"},{"unicode":"f240","className":"dashicons-share-alt"},{"unicode":"f242","className":"dashicons-share-alt2"},{"unicode":"f301","className":"dashicons-twitter"},{"unicode":"f303","className":"dashicons-rss"},{"unicode":"f465","className":"dashicons-email"},{"unicode":"f466","className":"dashicons-email-alt"},{"unicode":"f304","className":"dashicons-facebook"},{"unicode":"f305","className":"dashicons-facebook-alt"},{"unicode":"f462","className":"dashicons-googleplus"},{"unicode":"f325","className":"dashicons-networking"},{"unicode":"f308","className":"dashicons-hammer"},{"unicode":"f309","className":"dashicons-art"},{"unicode":"f310","className":"dashicons-migrate"},{"unicode":"f311","className":"dashicons-performance"},{"unicode":"f483","className":"dashicons-universal-access"},{"unicode":"f507","className":"dashicons-universal-access-alt"},{"unicode":"f486","className":"dashicons-tickets"},{"unicode":"f484","className":"dashicons-nametag"},{"unicode":"f481","className":"dashicons-clipboard"},{"unicode":"f487","className":"dashicons-heart"},{"unicode":"f488","className":"dashicons-megaphone"},{"unicode":"f489","className":"dashicons-schedule"},{"unicode":"f120","className":"dashicons-wordpress"},{"unicode":"f324","className":"dashicons-wordpress-alt"},{"unicode":"f157","className":"dashicons-pressthis"},{"unicode":"f463","className":"dashicons-update"},{"unicode":"f180","className":"dashicons-screenoptions"},{"unicode":"f348","className":"dashicons-info"},{"unicode":"f174","className":"dashicons-cart"},{"unicode":"f175","className":"dashicons-feedback"},{"unicode":"f176","className":"dashicons-cloud"},{"unicode":"f326","className":"dashicons-translation"},{"unicode":"f323","className":"dashicons-tag"},{"unicode":"f318","className":"dashicons-category"},{"unicode":"f480","className":"dashicons-archive"},{"unicode":"f479","className":"dashicons-tagcloud"},{"unicode":"f478","className":"dashicons-text"},{"unicode":"f147","className":"dashicons-yes"},{"unicode":"f158","className":"dashicons-no"},{"unicode":"f335","className":"dashicons-no-alt"},{"unicode":"f132","className":"dashicons-plus"},{"unicode":"f502","className":"dashicons-plus-alt"},{"unicode":"f460","className":"dashicons-minus"},{"unicode":"f153","className":"dashicons-dismiss"},{"unicode":"f159","className":"dashicons-marker"},{"unicode":"f155","className":"dashicons-star-filled"},{"unicode":"f459","className":"dashicons-star-half"},{"unicode":"f154","className":"dashicons-star-empty"},{"unicode":"f227","className":"dashicons-flag"},{"unicode":"f534","className":"dashicons-warning"},{"unicode":"f230","className":"dashicons-location"},{"unicode":"f231","className":"dashicons-location-alt"},{"unicode":"f178","className":"dashicons-vault"},{"unicode":"f332","className":"dashicons-shield"},{"unicode":"f334","className":"dashicons-shield-alt"},{"unicode":"f468","className":"dashicons-sos"},{"unicode":"f179","className":"dashicons-search"},{"unicode":"f181","className":"dashicons-slides"},{"unicode":"f183","className":"dashicons-analytics"},{"unicode":"f184","className":"dashicons-chart-pie"},{"unicode":"f185","className":"dashicons-chart-bar"},{"unicode":"f238","className":"dashicons-chart-line"},{"unicode":"f239","className":"dashicons-chart-area"},{"unicode":"f307","className":"dashicons-groups"},{"unicode":"f338","className":"dashicons-businessman"},{"unicode":"f336","className":"dashicons-id"},{"unicode":"f337","className":"dashicons-id-alt"},{"unicode":"f312","className":"dashicons-products"},{"unicode":"f313","className":"dashicons-awards"},{"unicode":"f314","className":"dashicons-forms"},{"unicode":"f473","className":"dashicons-testimonial"},{"unicode":"f322","className":"dashicons-portfolio"},{"unicode":"f330","className":"dashicons-book"},{"unicode":"f331","className":"dashicons-book-alt"},{"unicode":"f316","className":"dashicons-download"},{"unicode":"f317","className":"dashicons-upload"},{"unicode":"f321","className":"dashicons-backup"},{"unicode":"f469","className":"dashicons-clock"},{"unicode":"f339","className":"dashicons-lightbulb"},{"unicode":"f482","className":"dashicons-microphone"},{"unicode":"f472","className":"dashicons-desktop"},{"unicode":"f471","className":"dashicons-tablet"},{"unicode":"f470","className":"dashicons-smartphone"},{"unicode":"f525","className":"dashicons-phone"},{"unicode":"f510","className":"dashicons-index-card"},{"unicode":"f511","className":"dashicons-carrot"},{"unicode":"f512","className":"dashicons-building"},{"unicode":"f513","className":"dashicons-store"},{"unicode":"f514","className":"dashicons-album"},{"unicode":"f527","className":"dashicons-palmtree"},{"unicode":"f524","className":"dashicons-tickets-alt"},{"unicode":"f526","className":"dashicons-money"},{"unicode":"f328","className":"dashicons-smiley"},{"unicode":"f529","className":"dashicons-thumbs-up"},{"unicode":"f542","className":"dashicons-thumbs-down"},{"unicode":"f538","className":"dashicons-layout"}]}
data/json/fontawesome.json ADDED
@@ -0,0 +1 @@
 
1
+ {"icons":[{"unicode":"f26e","className":"fa-500px"},{"unicode":"f042","className":"fa-adjust"},{"unicode":"f170","className":"fa-adn"},{"unicode":"f037","className":"fa-align-center"},{"unicode":"f039","className":"fa-align-justify"},{"unicode":"f036","className":"fa-align-left"},{"unicode":"f038","className":"fa-align-right"},{"unicode":"f270","className":"fa-amazon"},{"unicode":"f0f9","className":"fa-ambulance"},{"unicode":"f13d","className":"fa-anchor"},{"unicode":"f17b","className":"fa-android"},{"unicode":"f209","className":"fa-angellist"},{"unicode":"f103","className":"fa-angle-double-down"},{"unicode":"f100","className":"fa-angle-double-left"},{"unicode":"f101","className":"fa-angle-double-right"},{"unicode":"f102","className":"fa-angle-double-up"},{"unicode":"f107","className":"fa-angle-down"},{"unicode":"f104","className":"fa-angle-left"},{"unicode":"f105","className":"fa-angle-right"},{"unicode":"f106","className":"fa-angle-up"},{"unicode":"f179","className":"fa-apple"},{"unicode":"f187","className":"fa-archive"},{"unicode":"f1fe","className":"fa-area-chart"},{"unicode":"f0ab","className":"fa-arrow-circle-down"},{"unicode":"f0a8","className":"fa-arrow-circle-left"},{"unicode":"f01a","className":"fa-arrow-circle-o-down"},{"unicode":"f190","className":"fa-arrow-circle-o-left"},{"unicode":"f18e","className":"fa-arrow-circle-o-right"},{"unicode":"f01b","className":"fa-arrow-circle-o-up"},{"unicode":"f0a9","className":"fa-arrow-circle-right"},{"unicode":"f0aa","className":"fa-arrow-circle-up"},{"unicode":"f063","className":"fa-arrow-down"},{"unicode":"f060","className":"fa-arrow-left"},{"unicode":"f061","className":"fa-arrow-right"},{"unicode":"f062","className":"fa-arrow-up"},{"unicode":"f047","className":"fa-arrows"},{"unicode":"f0b2","className":"fa-arrows-alt"},{"unicode":"f07e","className":"fa-arrows-h"},{"unicode":"f07d","className":"fa-arrows-v"},{"unicode":"f069","className":"fa-asterisk"},{"unicode":"f1fa","className":"fa-at"},{"unicode":"f1b9","className":"fa-automobile"},{"unicode":"f04a","className":"fa-backward"},{"unicode":"f24e","className":"fa-balance-scale"},{"unicode":"f05e","className":"fa-ban"},{"unicode":"f19c","className":"fa-bank"},{"unicode":"f080","className":"fa-bar-chart"},{"unicode":"f080","className":"fa-bar-chart-o"},{"unicode":"f02a","className":"fa-barcode"},{"unicode":"f0c9","className":"fa-bars"},{"unicode":"f244","className":"fa-battery-0"},{"unicode":"f243","className":"fa-battery-1"},{"unicode":"f242","className":"fa-battery-2"},{"unicode":"f241","className":"fa-battery-3"},{"unicode":"f240","className":"fa-battery-4"},{"unicode":"f244","className":"fa-battery-empty"},{"unicode":"f240","className":"fa-battery-full"},{"unicode":"f242","className":"fa-battery-half"},{"unicode":"f243","className":"fa-battery-quarter"},{"unicode":"f241","className":"fa-battery-three-quarters"},{"unicode":"f236","className":"fa-bed"},{"unicode":"f0fc","className":"fa-beer"},{"unicode":"f1b4","className":"fa-behance"},{"unicode":"f1b5","className":"fa-behance-square"},{"unicode":"f0f3","className":"fa-bell"},{"unicode":"f0a2","className":"fa-bell-o"},{"unicode":"f1f6","className":"fa-bell-slash"},{"unicode":"f1f7","className":"fa-bell-slash-o"},{"unicode":"f206","className":"fa-bicycle"},{"unicode":"f1e5","className":"fa-binoculars"},{"unicode":"f1fd","className":"fa-birthday-cake"},{"unicode":"f171","className":"fa-bitbucket"},{"unicode":"f172","className":"fa-bitbucket-square"},{"unicode":"f15a","className":"fa-bitcoin"},{"unicode":"f27e","className":"fa-black-tie"},{"unicode":"f293","className":"fa-bluetooth"},{"unicode":"f294","className":"fa-bluetooth-b"},{"unicode":"f032","className":"fa-bold"},{"unicode":"f0e7","className":"fa-bolt"},{"unicode":"f1e2","className":"fa-bomb"},{"unicode":"f02d","className":"fa-book"},{"unicode":"f02e","className":"fa-bookmark"},{"unicode":"f097","className":"fa-bookmark-o"},{"unicode":"f0b1","className":"fa-briefcase"},{"unicode":"f15a","className":"fa-btc"},{"unicode":"f188","className":"fa-bug"},{"unicode":"f1ad","className":"fa-building"},{"unicode":"f0f7","className":"fa-building-o"},{"unicode":"f0a1","className":"fa-bullhorn"},{"unicode":"f140","className":"fa-bullseye"},{"unicode":"f207","className":"fa-bus"},{"unicode":"f20d","className":"fa-buysellads"},{"unicode":"f1ba","className":"fa-cab"},{"unicode":"f1ec","className":"fa-calculator"},{"unicode":"f073","className":"fa-calendar"},{"unicode":"f274","className":"fa-calendar-check-o"},{"unicode":"f272","className":"fa-calendar-minus-o"},{"unicode":"f133","className":"fa-calendar-o"},{"unicode":"f271","className":"fa-calendar-plus-o"},{"unicode":"f273","className":"fa-calendar-times-o"},{"unicode":"f030","className":"fa-camera"},{"unicode":"f083","className":"fa-camera-retro"},{"unicode":"f1b9","className":"fa-car"},{"unicode":"f0d7","className":"fa-caret-down"},{"unicode":"f0d9","className":"fa-caret-left"},{"unicode":"f0da","className":"fa-caret-right"},{"unicode":"f150","className":"fa-caret-square-o-down"},{"unicode":"f191","className":"fa-caret-square-o-left"},{"unicode":"f152","className":"fa-caret-square-o-right"},{"unicode":"f151","className":"fa-caret-square-o-up"},{"unicode":"f0d8","className":"fa-caret-up"},{"unicode":"f218","className":"fa-cart-arrow-down"},{"unicode":"f217","className":"fa-cart-plus"},{"unicode":"f20a","className":"fa-cc"},{"unicode":"f1f3","className":"fa-cc-amex"},{"unicode":"f24c","className":"fa-cc-diners-club"},{"unicode":"f1f2","className":"fa-cc-discover"},{"unicode":"f24b","className":"fa-cc-jcb"},{"unicode":"f1f1","className":"fa-cc-mastercard"},{"unicode":"f1f4","className":"fa-cc-paypal"},{"unicode":"f1f5","className":"fa-cc-stripe"},{"unicode":"f1f0","className":"fa-cc-visa"},{"unicode":"f0a3","className":"fa-certificate"},{"unicode":"f0c1","className":"fa-chain"},{"unicode":"f127","className":"fa-chain-broken"},{"unicode":"f00c","className":"fa-check"},{"unicode":"f058","className":"fa-check-circle"},{"unicode":"f05d","className":"fa-check-circle-o"},{"unicode":"f14a","className":"fa-check-square"},{"unicode":"f046","className":"fa-check-square-o"},{"unicode":"f13a","className":"fa-chevron-circle-down"},{"unicode":"f137","className":"fa-chevron-circle-left"},{"unicode":"f138","className":"fa-chevron-circle-right"},{"unicode":"f139","className":"fa-chevron-circle-up"},{"unicode":"f078","className":"fa-chevron-down"},{"unicode":"f053","className":"fa-chevron-left"},{"unicode":"f054","className":"fa-chevron-right"},{"unicode":"f077","className":"fa-chevron-up"},{"unicode":"f1ae","className":"fa-child"},{"unicode":"f268","className":"fa-chrome"},{"unicode":"f111","className":"fa-circle"},{"unicode":"f10c","className":"fa-circle-o"},{"unicode":"f1ce","className":"fa-circle-o-notch"},{"unicode":"f1db","className":"fa-circle-thin"},{"unicode":"f0ea","className":"fa-clipboard"},{"unicode":"f017","className":"fa-clock-o"},{"unicode":"f24d","className":"fa-clone"},{"unicode":"f00d","className":"fa-close"},{"unicode":"f0c2","className":"fa-cloud"},{"unicode":"f0ed","className":"fa-cloud-download"},{"unicode":"f0ee","className":"fa-cloud-upload"},{"unicode":"f157","className":"fa-cny"},{"unicode":"f121","className":"fa-code"},{"unicode":"f126","className":"fa-code-fork"},{"unicode":"f1cb","className":"fa-codepen"},{"unicode":"f284","className":"fa-codiepie"},{"unicode":"f0f4","className":"fa-coffee"},{"unicode":"f013","className":"fa-cog"},{"unicode":"f085","className":"fa-cogs"},{"unicode":"f0db","className":"fa-columns"},{"unicode":"f075","className":"fa-comment"},{"unicode":"f0e5","className":"fa-comment-o"},{"unicode":"f27a","className":"fa-commenting"},{"unicode":"f27b","className":"fa-commenting-o"},{"unicode":"f086","className":"fa-comments"},{"unicode":"f0e6","className":"fa-comments-o"},{"unicode":"f14e","className":"fa-compass"},{"unicode":"f066","className":"fa-compress"},{"unicode":"f20e","className":"fa-connectdevelop"},{"unicode":"f26d","className":"fa-contao"},{"unicode":"f0c5","className":"fa-copy"},{"unicode":"f1f9","className":"fa-copyright"},{"unicode":"f25e","className":"fa-creative-commons"},{"unicode":"f09d","className":"fa-credit-card"},{"unicode":"f283","className":"fa-credit-card-alt"},{"unicode":"f125","className":"fa-crop"},{"unicode":"f05b","className":"fa-crosshairs"},{"unicode":"f13c","className":"fa-css3"},{"unicode":"f1b2","className":"fa-cube"},{"unicode":"f1b3","className":"fa-cubes"},{"unicode":"f0c4","className":"fa-cut"},{"unicode":"f0f5","className":"fa-cutlery"},{"unicode":"f0e4","className":"fa-dashboard"},{"unicode":"f210","className":"fa-dashcube"},{"unicode":"f1c0","className":"fa-database"},{"unicode":"f03b","className":"fa-dedent"},{"unicode":"f1a5","className":"fa-delicious"},{"unicode":"f108","className":"fa-desktop"},{"unicode":"f1bd","className":"fa-deviantart"},{"unicode":"f219","className":"fa-diamond"},{"unicode":"f1a6","className":"fa-digg"},{"unicode":"f155","className":"fa-dollar"},{"unicode":"f192","className":"fa-dot-circle-o"},{"unicode":"f019","className":"fa-download"},{"unicode":"f17d","className":"fa-dribbble"},{"unicode":"f16b","className":"fa-dropbox"},{"unicode":"f1a9","className":"fa-drupal"},{"unicode":"f282","className":"fa-edge"},{"unicode":"f044","className":"fa-edit"},{"unicode":"f052","className":"fa-eject"},{"unicode":"f141","className":"fa-ellipsis-h"},{"unicode":"f142","className":"fa-ellipsis-v"},{"unicode":"f1d1","className":"fa-empire"},{"unicode":"f0e0","className":"fa-envelope"},{"unicode":"f003","className":"fa-envelope-o"},{"unicode":"f199","className":"fa-envelope-square"},{"unicode":"f12d","className":"fa-eraser"},{"unicode":"f153","className":"fa-eur"},{"unicode":"f153","className":"fa-euro"},{"unicode":"f0ec","className":"fa-exchange"},{"unicode":"f12a","className":"fa-exclamation"},{"unicode":"f06a","className":"fa-exclamation-circle"},{"unicode":"f071","className":"fa-exclamation-triangle"},{"unicode":"f065","className":"fa-expand"},{"unicode":"f23e","className":"fa-expeditedssl"},{"unicode":"f08e","className":"fa-external-link"},{"unicode":"f14c","className":"fa-external-link-square"},{"unicode":"f06e","className":"fa-eye"},{"unicode":"f070","className":"fa-eye-slash"},{"unicode":"f1fb","className":"fa-eyedropper"},{"unicode":"f09a","className":"fa-facebook"},{"unicode":"f09a","className":"fa-facebook-f"},{"unicode":"f230","className":"fa-facebook-official"},{"unicode":"f082","className":"fa-facebook-square"},{"unicode":"f049","className":"fa-fast-backward"},{"unicode":"f050","className":"fa-fast-forward"},{"unicode":"f1ac","className":"fa-fax"},{"unicode":"f09e","className":"fa-feed"},{"unicode":"f182","className":"fa-female"},{"unicode":"f0fb","className":"fa-fighter-jet"},{"unicode":"f15b","className":"fa-file"},{"unicode":"f1c6","className":"fa-file-archive-o"},{"unicode":"f1c7","className":"fa-file-audio-o"},{"unicode":"f1c9","className":"fa-file-code-o"},{"unicode":"f1c3","className":"fa-file-excel-o"},{"unicode":"f1c5","className":"fa-file-image-o"},{"unicode":"f1c8","className":"fa-file-movie-o"},{"unicode":"f016","className":"fa-file-o"},{"unicode":"f1c1","className":"fa-file-pdf-o"},{"unicode":"f1c5","className":"fa-file-photo-o"},{"unicode":"f1c5","className":"fa-file-picture-o"},{"unicode":"f1c4","className":"fa-file-powerpoint-o"},{"unicode":"f1c7","className":"fa-file-sound-o"},{"unicode":"f15c","className":"fa-file-text"},{"unicode":"f0f6","className":"fa-file-text-o"},{"unicode":"f1c8","className":"fa-file-video-o"},{"unicode":"f1c2","className":"fa-file-word-o"},{"unicode":"f1c6","className":"fa-file-zip-o"},{"unicode":"f0c5","className":"fa-files-o"},{"unicode":"f008","className":"fa-film"},{"unicode":"f0b0","className":"fa-filter"},{"unicode":"f06d","className":"fa-fire"},{"unicode":"f134","className":"fa-fire-extinguisher"},{"unicode":"f269","className":"fa-firefox"},{"unicode":"f024","className":"fa-flag"},{"unicode":"f11e","className":"fa-flag-checkered"},{"unicode":"f11d","className":"fa-flag-o"},{"unicode":"f0e7","className":"fa-flash"},{"unicode":"f0c3","className":"fa-flask"},{"unicode":"f16e","className":"fa-flickr"},{"unicode":"f0c7","className":"fa-floppy-o"},{"unicode":"f07b","className":"fa-folder"},{"unicode":"f114","className":"fa-folder-o"},{"unicode":"f07c","className":"fa-folder-open"},{"unicode":"f115","className":"fa-folder-open-o"},{"unicode":"f031","className":"fa-font"},{"unicode":"f280","className":"fa-fonticons"},{"unicode":"f286","className":"fa-fort-awesome"},{"unicode":"f211","className":"fa-forumbee"},{"unicode":"f04e","className":"fa-forward"},{"unicode":"f180","className":"fa-foursquare"},{"unicode":"f119","className":"fa-frown-o"},{"unicode":"f1e3","className":"fa-futbol-o"},{"unicode":"f11b","className":"fa-gamepad"},{"unicode":"f0e3","className":"fa-gavel"},{"unicode":"f154","className":"fa-gbp"},{"unicode":"f1d1","className":"fa-ge"},{"unicode":"f013","className":"fa-gear"},{"unicode":"f085","className":"fa-gears"},{"unicode":"f22d","className":"fa-genderless"},{"unicode":"f265","className":"fa-get-pocket"},{"unicode":"f260","className":"fa-gg"},{"unicode":"f261","className":"fa-gg-circle"},{"unicode":"f06b","className":"fa-gift"},{"unicode":"f1d3","className":"fa-git"},{"unicode":"f1d2","className":"fa-git-square"},{"unicode":"f09b","className":"fa-github"},{"unicode":"f113","className":"fa-github-alt"},{"unicode":"f092","className":"fa-github-square"},{"unicode":"f184","className":"fa-gittip"},{"unicode":"f000","className":"fa-glass"},{"unicode":"f0ac","className":"fa-globe"},{"unicode":"f1a0","className":"fa-google"},{"unicode":"f0d5","className":"fa-google-plus"},{"unicode":"f0d4","className":"fa-google-plus-square"},{"unicode":"f1ee","className":"fa-google-wallet"},{"unicode":"f19d","className":"fa-graduation-cap"},{"unicode":"f184","className":"fa-gratipay"},{"unicode":"f0c0","className":"fa-group"},{"unicode":"f0fd","className":"fa-h-square"},{"unicode":"f1d4","className":"fa-hacker-news"},{"unicode":"f255","className":"fa-hand-grab-o"},{"unicode":"f258","className":"fa-hand-lizard-o"},{"unicode":"f0a7","className":"fa-hand-o-down"},{"unicode":"f0a5","className":"fa-hand-o-left"},{"unicode":"f0a4","className":"fa-hand-o-right"},{"unicode":"f0a6","className":"fa-hand-o-up"},{"unicode":"f256","className":"fa-hand-paper-o"},{"unicode":"f25b","className":"fa-hand-peace-o"},{"unicode":"f25a","className":"fa-hand-pointer-o"},{"unicode":"f255","className":"fa-hand-rock-o"},{"unicode":"f257","className":"fa-hand-scissors-o"},{"unicode":"f259","className":"fa-hand-spock-o"},{"unicode":"f256","className":"fa-hand-stop-o"},{"unicode":"f292","className":"fa-hashtag"},{"unicode":"f0a0","className":"fa-hdd-o"},{"unicode":"f1dc","className":"fa-header"},{"unicode":"f025","className":"fa-headphones"},{"unicode":"f004","className":"fa-heart"},{"unicode":"f08a","className":"fa-heart-o"},{"unicode":"f21e","className":"fa-heartbeat"},{"unicode":"f1da","className":"fa-history"},{"unicode":"f015","className":"fa-home"},{"unicode":"f0f8","className":"fa-hospital-o"},{"unicode":"f236","className":"fa-hotel"},{"unicode":"f254","className":"fa-hourglass"},{"unicode":"f251","className":"fa-hourglass-1"},{"unicode":"f252","className":"fa-hourglass-2"},{"unicode":"f253","className":"fa-hourglass-3"},{"unicode":"f253","className":"fa-hourglass-end"},{"unicode":"f252","className":"fa-hourglass-half"},{"unicode":"f250","className":"fa-hourglass-o"},{"unicode":"f251","className":"fa-hourglass-start"},{"unicode":"f27c","className":"fa-houzz"},{"unicode":"f13b","className":"fa-html5"},{"unicode":"f246","className":"fa-i-cursor"},{"unicode":"f20b","className":"fa-ils"},{"unicode":"f03e","className":"fa-image"},{"unicode":"f01c","className":"fa-inbox"},{"unicode":"f03c","className":"fa-indent"},{"unicode":"f275","className":"fa-industry"},{"unicode":"f129","className":"fa-info"},{"unicode":"f05a","className":"fa-info-circle"},{"unicode":"f156","className":"fa-inr"},{"unicode":"f16d","className":"fa-instagram"},{"unicode":"f19c","className":"fa-institution"},{"unicode":"f26b","className":"fa-internet-explorer"},{"unicode":"f224","className":"fa-intersex"},{"unicode":"f208","className":"fa-ioxhost"},{"unicode":"f033","className":"fa-italic"},{"unicode":"f1aa","className":"fa-joomla"},{"unicode":"f157","className":"fa-jpy"},{"unicode":"f1cc","className":"fa-jsfiddle"},{"unicode":"f084","className":"fa-key"},{"unicode":"f11c","className":"fa-keyboard-o"},{"unicode":"f159","className":"fa-krw"},{"unicode":"f1ab","className":"fa-language"},{"unicode":"f109","className":"fa-laptop"},{"unicode":"f202","className":"fa-lastfm"},{"unicode":"f203","className":"fa-lastfm-square"},{"unicode":"f06c","className":"fa-leaf"},{"unicode":"f212","className":"fa-leanpub"},{"unicode":"f0e3","className":"fa-legal"},{"unicode":"f094","className":"fa-lemon-o"},{"unicode":"f149","className":"fa-level-down"},{"unicode":"f148","className":"fa-level-up"},{"unicode":"f1cd","className":"fa-life-bouy"},{"unicode":"f1cd","className":"fa-life-buoy"},{"unicode":"f1cd","className":"fa-life-ring"},{"unicode":"f1cd","className":"fa-life-saver"},{"unicode":"f0eb","className":"fa-lightbulb-o"},{"unicode":"f201","className":"fa-line-chart"},{"unicode":"f0c1","className":"fa-link"},{"unicode":"f0e1","className":"fa-linkedin"},{"unicode":"f08c","className":"fa-linkedin-square"},{"unicode":"f17c","className":"fa-linux"},{"unicode":"f03a","className":"fa-list"},{"unicode":"f022","className":"fa-list-alt"},{"unicode":"f0cb","className":"fa-list-ol"},{"unicode":"f0ca","className":"fa-list-ul"},{"unicode":"f124","className":"fa-location-arrow"},{"unicode":"f023","className":"fa-lock"},{"unicode":"f175","className":"fa-long-arrow-down"},{"unicode":"f177","className":"fa-long-arrow-left"},{"unicode":"f178","className":"fa-long-arrow-right"},{"unicode":"f176","className":"fa-long-arrow-up"},{"unicode":"f0d0","className":"fa-magic"},{"unicode":"f076","className":"fa-magnet"},{"unicode":"f064","className":"fa-mail-forward"},{"unicode":"f112","className":"fa-mail-reply"},{"unicode":"f122","className":"fa-mail-reply-all"},{"unicode":"f183","className":"fa-male"},{"unicode":"f279","className":"fa-map"},{"unicode":"f041","className":"fa-map-marker"},{"unicode":"f278","className":"fa-map-o"},{"unicode":"f276","className":"fa-map-pin"},{"unicode":"f277","className":"fa-map-signs"},{"unicode":"f222","className":"fa-mars"},{"unicode":"f227","className":"fa-mars-double"},{"unicode":"f229","className":"fa-mars-stroke"},{"unicode":"f22b","className":"fa-mars-stroke-h"},{"unicode":"f22a","className":"fa-mars-stroke-v"},{"unicode":"f136","className":"fa-maxcdn"},{"unicode":"f20c","className":"fa-meanpath"},{"unicode":"f23a","className":"fa-medium"},{"unicode":"f0fa","className":"fa-medkit"},{"unicode":"f11a","className":"fa-meh-o"},{"unicode":"f223","className":"fa-mercury"},{"unicode":"f130","className":"fa-microphone"},{"unicode":"f131","className":"fa-microphone-slash"},{"unicode":"f068","className":"fa-minus"},{"unicode":"f056","className":"fa-minus-circle"},{"unicode":"f146","className":"fa-minus-square"},{"unicode":"f147","className":"fa-minus-square-o"},{"unicode":"f289","className":"fa-mixcloud"},{"unicode":"f10b","className":"fa-mobile"},{"unicode":"f10b","className":"fa-mobile-phone"},{"unicode":"f285","className":"fa-modx"},{"unicode":"f0d6","className":"fa-money"},{"unicode":"f186","className":"fa-moon-o"},{"unicode":"f19d","className":"fa-mortar-board"},{"unicode":"f21c","className":"fa-motorcycle"},{"unicode":"f245","className":"fa-mouse-pointer"},{"unicode":"f001","className":"fa-music"},{"unicode":"f0c9","className":"fa-navicon"},{"unicode":"f22c","className":"fa-neuter"},{"unicode":"f1ea","className":"fa-newspaper-o"},{"unicode":"f247","className":"fa-object-group"},{"unicode":"f248","className":"fa-object-ungroup"},{"unicode":"f263","className":"fa-odnoklassniki"},{"unicode":"f264","className":"fa-odnoklassniki-square"},{"unicode":"f23d","className":"fa-opencart"},{"unicode":"f19b","className":"fa-openid"},{"unicode":"f26a","className":"fa-opera"},{"unicode":"f23c","className":"fa-optin-monster"},{"unicode":"f03b","className":"fa-outdent"},{"unicode":"f18c","className":"fa-pagelines"},{"unicode":"f1fc","className":"fa-paint-brush"},{"unicode":"f1d8","className":"fa-paper-plane"},{"unicode":"f1d9","className":"fa-paper-plane-o"},{"unicode":"f0c6","className":"fa-paperclip"},{"unicode":"f1dd","className":"fa-paragraph"},{"unicode":"f0ea","className":"fa-paste"},{"unicode":"f04c","className":"fa-pause"},{"unicode":"f28b","className":"fa-pause-circle"},{"unicode":"f28c","className":"fa-pause-circle-o"},{"unicode":"f1b0","className":"fa-paw"},{"unicode":"f1ed","className":"fa-paypal"},{"unicode":"f040","className":"fa-pencil"},{"unicode":"f14b","className":"fa-pencil-square"},{"unicode":"f044","className":"fa-pencil-square-o"},{"unicode":"f295","className":"fa-percent"},{"unicode":"f095","className":"fa-phone"},{"unicode":"f098","className":"fa-phone-square"},{"unicode":"f03e","className":"fa-photo"},{"unicode":"f03e","className":"fa-picture-o"},{"unicode":"f200","className":"fa-pie-chart"},{"unicode":"f1a7","className":"fa-pied-piper"},{"unicode":"f1a8","className":"fa-pied-piper-alt"},{"unicode":"f0d2","className":"fa-pinterest"},{"unicode":"f231","className":"fa-pinterest-p"},{"unicode":"f0d3","className":"fa-pinterest-square"},{"unicode":"f072","className":"fa-plane"},{"unicode":"f04b","className":"fa-play"},{"unicode":"f144","className":"fa-play-circle"},{"unicode":"f01d","className":"fa-play-circle-o"},{"unicode":"f1e6","className":"fa-plug"},{"unicode":"f067","className":"fa-plus"},{"unicode":"f055","className":"fa-plus-circle"},{"unicode":"f0fe","className":"fa-plus-square"},{"unicode":"f196","className":"fa-plus-square-o"},{"unicode":"f011","className":"fa-power-off"},{"unicode":"f02f","className":"fa-print"},{"unicode":"f288","className":"fa-product-hunt"},{"unicode":"f12e","className":"fa-puzzle-piece"},{"unicode":"f1d6","className":"fa-qq"},{"unicode":"f029","className":"fa-qrcode"},{"unicode":"f128","className":"fa-question"},{"unicode":"f059","className":"fa-question-circle"},{"unicode":"f10d","className":"fa-quote-left"},{"unicode":"f10e","className":"fa-quote-right"},{"unicode":"f1d0","className":"fa-ra"},{"unicode":"f074","className":"fa-random"},{"unicode":"f1d0","className":"fa-rebel"},{"unicode":"f1b8","className":"fa-recycle"},{"unicode":"f1a1","className":"fa-reddit"},{"unicode":"f281","className":"fa-reddit-alien"},{"unicode":"f1a2","className":"fa-reddit-square"},{"unicode":"f021","className":"fa-refresh"},{"unicode":"f25d","className":"fa-registered"},{"unicode":"f00d","className":"fa-remove"},{"unicode":"f18b","className":"fa-renren"},{"unicode":"f0c9","className":"fa-reorder"},{"unicode":"f01e","className":"fa-repeat"},{"unicode":"f112","className":"fa-reply"},{"unicode":"f122","className":"fa-reply-all"},{"unicode":"f079","className":"fa-retweet"},{"unicode":"f157","className":"fa-rmb"},{"unicode":"f018","className":"fa-road"},{"unicode":"f135","className":"fa-rocket"},{"unicode":"f0e2","className":"fa-rotate-left"},{"unicode":"f01e","className":"fa-rotate-right"},{"unicode":"f158","className":"fa-rouble"},{"unicode":"f09e","className":"fa-rss"},{"unicode":"f143","className":"fa-rss-square"},{"unicode":"f158","className":"fa-rub"},{"unicode":"f158","className":"fa-ruble"},{"unicode":"f156","className":"fa-rupee"},{"unicode":"f267","className":"fa-safari"},{"unicode":"f0c7","className":"fa-save"},{"unicode":"f0c4","className":"fa-scissors"},{"unicode":"f28a","className":"fa-scribd"},{"unicode":"f002","className":"fa-search"},{"unicode":"f010","className":"fa-search-minus"},{"unicode":"f00e","className":"fa-search-plus"},{"unicode":"f213","className":"fa-sellsy"},{"unicode":"f1d8","className":"fa-send"},{"unicode":"f1d9","className":"fa-send-o"},{"unicode":"f233","className":"fa-server"},{"unicode":"f064","className":"fa-share"},{"unicode":"f1e0","className":"fa-share-alt"},{"unicode":"f1e1","className":"fa-share-alt-square"},{"unicode":"f14d","className":"fa-share-square"},{"unicode":"f045","className":"fa-share-square-o"},{"unicode":"f20b","className":"fa-shekel"},{"unicode":"f20b","className":"fa-sheqel"},{"unicode":"f132","className":"fa-shield"},{"unicode":"f21a","className":"fa-ship"},{"unicode":"f214","className":"fa-shirtsinbulk"},{"unicode":"f290","className":"fa-shopping-bag"},{"unicode":"f291","className":"fa-shopping-basket"},{"unicode":"f07a","className":"fa-shopping-cart"},{"unicode":"f090","className":"fa-sign-in"},{"unicode":"f08b","className":"fa-sign-out"},{"unicode":"f012","className":"fa-signal"},{"unicode":"f215","className":"fa-simplybuilt"},{"unicode":"f0e8","className":"fa-sitemap"},{"unicode":"f216","className":"fa-skyatlas"},{"unicode":"f17e","className":"fa-skype"},{"unicode":"f198","className":"fa-slack"},{"unicode":"f1de","className":"fa-sliders"},{"unicode":"f1e7","className":"fa-slideshare"},{"unicode":"f118","className":"fa-smile-o"},{"unicode":"f1e3","className":"fa-soccer-ball-o"},{"unicode":"f0dc","className":"fa-sort"},{"unicode":"f15d","className":"fa-sort-alpha-asc"},{"unicode":"f15e","className":"fa-sort-alpha-desc"},{"unicode":"f160","className":"fa-sort-amount-asc"},{"unicode":"f161","className":"fa-sort-amount-desc"},{"unicode":"f0de","className":"fa-sort-asc"},{"unicode":"f0dd","className":"fa-sort-desc"},{"unicode":"f0dd","className":"fa-sort-down"},{"unicode":"f162","className":"fa-sort-numeric-asc"},{"unicode":"f163","className":"fa-sort-numeric-desc"},{"unicode":"f0de","className":"fa-sort-up"},{"unicode":"f1be","className":"fa-soundcloud"},{"unicode":"f197","className":"fa-space-shuttle"},{"unicode":"f110","className":"fa-spinner"},{"unicode":"f1b1","className":"fa-spoon"},{"unicode":"f1bc","className":"fa-spotify"},{"unicode":"f0c8","className":"fa-square"},{"unicode":"f096","className":"fa-square-o"},{"unicode":"f18d","className":"fa-stack-exchange"},{"unicode":"f16c","className":"fa-stack-overflow"},{"unicode":"f005","className":"fa-star"},{"unicode":"f089","className":"fa-star-half"},{"unicode":"f123","className":"fa-star-half-empty"},{"unicode":"f123","className":"fa-star-half-full"},{"unicode":"f123","className":"fa-star-half-o"},{"unicode":"f006","className":"fa-star-o"},{"unicode":"f1b6","className":"fa-steam"},{"unicode":"f1b7","className":"fa-steam-square"},{"unicode":"f048","className":"fa-step-backward"},{"unicode":"f051","className":"fa-step-forward"},{"unicode":"f0f1","className":"fa-stethoscope"},{"unicode":"f249","className":"fa-sticky-note"},{"unicode":"f24a","className":"fa-sticky-note-o"},{"unicode":"f04d","className":"fa-stop"},{"unicode":"f28d","className":"fa-stop-circle"},{"unicode":"f28e","className":"fa-stop-circle-o"},{"unicode":"f21d","className":"fa-street-view"},{"unicode":"f0cc","className":"fa-strikethrough"},{"unicode":"f1a4","className":"fa-stumbleupon"},{"unicode":"f1a3","className":"fa-stumbleupon-circle"},{"unicode":"f12c","className":"fa-subscript"},{"unicode":"f239","className":"fa-subway"},{"unicode":"f0f2","className":"fa-suitcase"},{"unicode":"f185","className":"fa-sun-o"},{"unicode":"f12b","className":"fa-superscript"},{"unicode":"f1cd","className":"fa-support"},{"unicode":"f0ce","className":"fa-table"},{"unicode":"f10a","className":"fa-tablet"},{"unicode":"f0e4","className":"fa-tachometer"},{"unicode":"f02b","className":"fa-tag"},{"unicode":"f02c","className":"fa-tags"},{"unicode":"f0ae","className":"fa-tasks"},{"unicode":"f1ba","className":"fa-taxi"},{"unicode":"f26c","className":"fa-television"},{"unicode":"f1d5","className":"fa-tencent-weibo"},{"unicode":"f120","className":"fa-terminal"},{"unicode":"f034","className":"fa-text-height"},{"unicode":"f035","className":"fa-text-width"},{"unicode":"f00a","className":"fa-th"},{"unicode":"f009","className":"fa-th-large"},{"unicode":"f00b","className":"fa-th-list"},{"unicode":"f08d","className":"fa-thumb-tack"},{"unicode":"f165","className":"fa-thumbs-down"},{"unicode":"f088","className":"fa-thumbs-o-down"},{"unicode":"f087","className":"fa-thumbs-o-up"},{"unicode":"f164","className":"fa-thumbs-up"},{"unicode":"f145","className":"fa-ticket"},{"unicode":"f00d","className":"fa-times"},{"unicode":"f057","className":"fa-times-circle"},{"unicode":"f05c","className":"fa-times-circle-o"},{"unicode":"f043","className":"fa-tint"},{"unicode":"f150","className":"fa-toggle-down"},{"unicode":"f191","className":"fa-toggle-left"},{"unicode":"f204","className":"fa-toggle-off"},{"unicode":"f205","className":"fa-toggle-on"},{"unicode":"f152","className":"fa-toggle-right"},{"unicode":"f151","className":"fa-toggle-up"},{"unicode":"f25c","className":"fa-trademark"},{"unicode":"f238","className":"fa-train"},{"unicode":"f224","className":"fa-transgender"},{"unicode":"f225","className":"fa-transgender-alt"},{"unicode":"f1f8","className":"fa-trash"},{"unicode":"f014","className":"fa-trash-o"},{"unicode":"f1bb","className":"fa-tree"},{"unicode":"f181","className":"fa-trello"},{"unicode":"f262","className":"fa-tripadvisor"},{"unicode":"f091","className":"fa-trophy"},{"unicode":"f0d1","className":"fa-truck"},{"unicode":"f195","className":"fa-try"},{"unicode":"f1e4","className":"fa-tty"},{"unicode":"f173","className":"fa-tumblr"},{"unicode":"f174","className":"fa-tumblr-square"},{"unicode":"f195","className":"fa-turkish-lira"},{"unicode":"f26c","className":"fa-tv"},{"unicode":"f1e8","className":"fa-twitch"},{"unicode":"f099","className":"fa-twitter"},{"unicode":"f081","className":"fa-twitter-square"},{"unicode":"f0e9","className":"fa-umbrella"},{"unicode":"f0cd","className":"fa-underline"},{"unicode":"f0e2","className":"fa-undo"},{"unicode":"f19c","className":"fa-university"},{"unicode":"f127","className":"fa-unlink"},{"unicode":"f09c","className":"fa-unlock"},{"unicode":"f13e","className":"fa-unlock-alt"},{"unicode":"f0dc","className":"fa-unsorted"},{"unicode":"f093","className":"fa-upload"},{"unicode":"f287","className":"fa-usb"},{"unicode":"f155","className":"fa-usd"},{"unicode":"f007","className":"fa-user"},{"unicode":"f0f0","className":"fa-user-md"},{"unicode":"f234","className":"fa-user-plus"},{"unicode":"f21b","className":"fa-user-secret"},{"unicode":"f235","className":"fa-user-times"},{"unicode":"f0c0","className":"fa-users"},{"unicode":"f221","className":"fa-venus"},{"unicode":"f226","className":"fa-venus-double"},{"unicode":"f228","className":"fa-venus-mars"},{"unicode":"f237","className":"fa-viacoin"},{"unicode":"f03d","className":"fa-video-camera"},{"unicode":"f27d","className":"fa-vimeo"},{"unicode":"f194","className":"fa-vimeo-square"},{"unicode":"f1ca","className":"fa-vine"},{"unicode":"f189","className":"fa-vk"},{"unicode":"f027","className":"fa-volume-down"},{"unicode":"f026","className":"fa-volume-off"},{"unicode":"f028","className":"fa-volume-up"},{"unicode":"f071","className":"fa-warning"},{"unicode":"f1d7","className":"fa-wechat"},{"unicode":"f18a","className":"fa-weibo"},{"unicode":"f1d7","className":"fa-weixin"},{"unicode":"f232","className":"fa-whatsapp"},{"unicode":"f193","className":"fa-wheelchair"},{"unicode":"f1eb","className":"fa-wifi"},{"unicode":"f266","className":"fa-wikipedia-w"},{"unicode":"f17a","className":"fa-windows"},{"unicode":"f159","className":"fa-won"},{"unicode":"f19a","className":"fa-wordpress"},{"unicode":"f0ad","className":"fa-wrench"},{"unicode":"f168","className":"fa-xing"},{"unicode":"f169","className":"fa-xing-square"},{"unicode":"f23b","className":"fa-y-combinator"},{"unicode":"f1d4","className":"fa-y-combinator-square"},{"unicode":"f19e","className":"fa-yahoo"},{"unicode":"f23b","className":"fa-yc"},{"unicode":"f1d4","className":"fa-yc-square"},{"unicode":"f1e9","className":"fa-yelp"},{"unicode":"f157","className":"fa-yen"},{"unicode":"f167","className":"fa-youtube"},{"unicode":"f16a","className":"fa-youtube-play"},{"unicode":"f166","className":"fa-youtube-square"}]}
images/icon-email-encoder-bundle-16.png DELETED
Binary file
images/icon-wp-external-links-16.png DELETED
Binary file
images/icon-wp-external-links-32.png DELETED
Binary file
images/icon-wp-mailto-links-16.png DELETED
Binary file
images/tipsy.gif DELETED
Binary file
{js/src → includes}/.htaccess RENAMED
File without changes
includes/admin/class-wpel-network-page.php ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Network_Page
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Network_Page extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * @var string
18
+ */
19
+ private $menu_slug = 'wpel-network-settings-page';
20
+
21
+ /**
22
+ * @var string
23
+ */
24
+ private $current_tab = null;
25
+
26
+ /**
27
+ * @var array
28
+ */
29
+ private $tabs = array();
30
+
31
+ /**
32
+ * Initialize
33
+ */
34
+ protected function init( array $fields_objects )
35
+ {
36
+ $this->tabs = array(
37
+ 'network-settings' => array(
38
+ 'title' => __( 'Multi Site Settings', 'wpel' ),
39
+ 'icon' => '<i class="fa fa-sitemap" aria-hidden="true"></i>',
40
+ 'fields' => $fields_objects[ 'network-settings' ],
41
+ ),
42
+ 'network-admin-settings' => array(
43
+ 'title' => __( 'Admin Settings', 'wpel' ),
44
+ 'icon' => '<i class="fa fa-cog" aria-hidden="true"></i>',
45
+ 'fields' => $fields_objects[ 'network-admin-settings' ],
46
+ ),
47
+ 'support' => array(
48
+ 'title' => __( 'Support', 'wpel' ),
49
+ 'icon' => '<i class="fa fa-smile-o" aria-hidden="true"></i>',
50
+ ),
51
+ );
52
+
53
+ // get current tab
54
+ $this->current_tab = filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_STRING );
55
+
56
+ // set default tab
57
+ if ( ! key_exists( $this->current_tab, $this->tabs ) ) {
58
+ reset( $this->tabs );
59
+ $this->current_tab = key( $this->tabs );
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Get option value
65
+ * @param string $key
66
+ * @param string $type
67
+ * @return string
68
+ * @triggers E_USER_NOTICE Option value cannot be found
69
+ */
70
+ public function get_option_value( $key, $type = null )
71
+ {
72
+ if ( null === $type ) {
73
+ foreach ( $this->tabs as $tab_key => $values ) {
74
+ if ( ! isset( $values[ 'fields' ] ) ) {
75
+ continue;
76
+ }
77
+
78
+ $option_values = $values[ 'fields' ]->get_option_values();
79
+
80
+ if ( ! isset( $option_values[ $key ] ) ) {
81
+ continue;
82
+ }
83
+
84
+ return $option_values[ $key ];
85
+ }
86
+ } else if ( isset( $this->tabs[ $type ][ 'fields' ] ) ) {
87
+ $option_values = $this->tabs[ $type ][ 'fields' ]->get_option_values();
88
+ return $option_values[ $key ];
89
+ }
90
+
91
+ trigger_error( 'Option value "'. $key .'" cannot be found.' );
92
+ }
93
+
94
+ /**
95
+ * Action for "network_admin_menu"
96
+ */
97
+ protected function action_network_admin_menu()
98
+ {
99
+ $own_admin_menu = $this->get_option_value( 'own_admin_menu' );
100
+
101
+ if ( '1' === $own_admin_menu ) {
102
+ $this->page_hook = add_menu_page(
103
+ __( 'WP External Links' , 'wpel' ) // page title
104
+ , __( 'External Links' , 'wpel' ) // menu title
105
+ , 'manage_network' // capability
106
+ , $this->menu_slug // menu slug
107
+ , $this->get_callback( 'show_network_page' ) // callback
108
+ , 'none' // icon
109
+ , null // position
110
+ );
111
+ } else {
112
+ $this->page_hook = add_submenu_page(
113
+ 'settings.php' // parent slug
114
+ , __( 'WP External Links' , 'wpel' ) // page title
115
+ , __( 'External Links' , 'wpel' ) // menu title
116
+ , 'manage_options' // capability
117
+ , $this->menu_slug // menu slug
118
+ , $this->get_callback( 'show_network_page' ) // callback
119
+ );
120
+ }
121
+
122
+ add_action( 'load-'. $this->page_hook, $this->get_callback( 'add_help_tabs' ) );
123
+ }
124
+
125
+ /**
126
+ * Action for "admin_enqueue_scripts"
127
+ */
128
+ protected function action_admin_enqueue_scripts()
129
+ {
130
+ // set admin style
131
+ wp_enqueue_style(
132
+ 'wpel-admin-style'
133
+ , plugins_url( '/public/css/wpel-admin.css', WPEL_Plugin::get_plugin_file() )
134
+ , array()
135
+ , null
136
+ );
137
+
138
+ // set wpel admin script
139
+ wp_enqueue_script(
140
+ 'wpel-admin-settings'
141
+ , plugins_url( '/public/js/wpel-admin.js', WPEL_Plugin::get_plugin_file() )
142
+ , array('jquery')
143
+ , false
144
+ , true
145
+ );
146
+
147
+ // set style
148
+ wp_enqueue_style(
149
+ 'font-awesome'
150
+ , 'https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'
151
+ , array()
152
+ , null
153
+ );
154
+ }
155
+
156
+ /**
157
+ * Show Admin Page
158
+ */
159
+ protected function show_network_page()
160
+ {
161
+ $template_file = WPEL_Plugin::get_plugin_dir( '/templates/network-page/main.php' );
162
+ $page = $this->get_option_value( 'own_admin_menu' ) ? 'admin.php' : 'settings.php';
163
+ $page_url = network_admin_url() . $page .'?page='. $this->menu_slug;
164
+
165
+ $template_vars = array(
166
+ 'tabs' => $this->tabs,
167
+ 'current_tab' => $this->current_tab,
168
+ 'page_url' => $page_url,
169
+ 'menu_slug' => $this->menu_slug,
170
+ 'own_admin_menu' => $this->get_option_value( 'own_admin_menu' ),
171
+ );
172
+
173
+ $this->show_template( $template_file, $template_vars );
174
+ }
175
+
176
+ /**
177
+ * Add help tabs
178
+ */
179
+ protected function add_help_tabs()
180
+ {
181
+ $screen = get_current_screen();
182
+
183
+ $screen->add_help_tab( array(
184
+ 'id' => 'under-construction',
185
+ 'title' => __( 'Under Construction', 'wpel' ),
186
+ 'callback' => $this->get_callback( 'show_help_tab' ),
187
+ ) );
188
+ }
189
+
190
+ /**
191
+ * @param WP_Screen $screen
192
+ * @param array $args
193
+ */
194
+ protected function show_help_tab( $screen, array $args )
195
+ {
196
+ $template_file = WPEL_Plugin::get_plugin_dir( '/templates/network-page/help-tabs/'. $args[ 'id' ] .'.php' );
197
+ $this->show_template( $template_file );
198
+ }
199
+
200
+ }
201
+
202
+ /*?>*/
includes/admin/class-wpel-settings-page.php ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Settings_Page
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Settings_Page extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * @var string
18
+ */
19
+ private $menu_slug = 'wpel-settings-page';
20
+
21
+ /**
22
+ * @var string
23
+ */
24
+ private $current_tab = null;
25
+
26
+ /**
27
+ * @var array
28
+ */
29
+ private $tabs = array();
30
+
31
+ /**
32
+ * @var WPEL_Network_Page
33
+ */
34
+ private $network_page = null;
35
+
36
+ /**
37
+ * Initialize
38
+ */
39
+ protected function init( $network_page, array $fields_objects )
40
+ {
41
+ $this->network_page = $network_page;
42
+
43
+ $this->tabs = array(
44
+ 'external-links' => array(
45
+ 'title' => __( 'External Links', 'wpel' ),
46
+ 'icon' => '<i class="fa fa-external-link-square" aria-hidden="true"></i>',
47
+ 'fields' => $fields_objects[ 'external-links' ],
48
+ ),
49
+ 'internal-links' => array(
50
+ 'title' => __( 'Internal Links', 'wpel' ),
51
+ 'icon' => '<i class="fa fa-arrow-circle-o-down" aria-hidden="true"></i>',
52
+ 'fields' => $fields_objects[ 'internal-links' ],
53
+ ),
54
+ 'exceptions' => array(
55
+ 'title' => __( 'Exceptions', 'wpel' ),
56
+ 'icon' => '<i class="fa fa-times-circle" aria-hidden="true"></i>',
57
+ 'fields' => $fields_objects[ 'exceptions' ],
58
+ ),
59
+ 'admin' => array(
60
+ 'title' => __( 'Admin Settings', 'wpel' ),
61
+ 'icon' => '<i class="fa fa-cog" aria-hidden="true"></i>',
62
+ 'fields' => $fields_objects[ 'admin' ],
63
+ ),
64
+ 'support' => array(
65
+ 'title' => __( 'Support', 'wpel' ),
66
+ 'icon' => '<i class="fa fa-question" aria-hidden="true"></i>',
67
+ ),
68
+ );
69
+
70
+ // get current tab
71
+ $this->current_tab = filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_STRING );
72
+
73
+ // set default tab
74
+ if ( ! key_exists( $this->current_tab, $this->tabs ) ) {
75
+ reset( $this->tabs );
76
+ $this->current_tab = key( $this->tabs );
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Get option value
82
+ * @param string $key
83
+ * @param string $type
84
+ * @return string
85
+ * @triggers E_USER_NOTICE Option value cannot be found
86
+ */
87
+ public function get_option_value( $key, $type = null )
88
+ {
89
+ if ( null === $type ) {
90
+ foreach ( $this->tabs as $tab_key => $values ) {
91
+ if ( ! isset( $values[ 'fields' ] ) ) {
92
+ continue;
93
+ }
94
+
95
+ $option_values = $values[ 'fields' ]->get_option_values();
96
+
97
+ if ( ! isset( $option_values[ $key ] ) ) {
98
+ continue;
99
+ }
100
+
101
+ return $option_values[ $key ];
102
+ }
103
+ } else if ( isset( $this->tabs[ $type ][ 'fields' ] ) ) {
104
+ $option_values = $this->tabs[ $type ][ 'fields' ]->get_option_values();
105
+ return $option_values[ $key ];
106
+ }
107
+
108
+ trigger_error( 'Option value "'. $key .'" cannot be found.' );
109
+ }
110
+
111
+ /**
112
+ * Action for "admin_menu"
113
+ */
114
+ protected function action_admin_menu()
115
+ {
116
+ $capability = $this->network_page->get_option_value( 'capability' );
117
+
118
+ $own_admin_menu = $this->get_option_value( 'own_admin_menu', 'admin' );
119
+
120
+ if ( '1' === $own_admin_menu ) {
121
+ $this->page_hook = add_menu_page(
122
+ __( 'WP External Links' , 'wpel' ) // page title
123
+ , __( 'External Links' , 'wpel' ) // menu title
124
+ , $capability // capability
125
+ , $this->menu_slug // id
126
+ , $this->get_callback( 'show_admin_page' ) // callback
127
+ , 'none' // icon
128
+ , null // position
129
+ );
130
+ } else {
131
+ $this->page_hook = add_options_page(
132
+ __( 'WP External Links' , 'wpel' ) // page title
133
+ , __( 'External Links' , 'wpel' ) // menu title
134
+ , $capability // capability
135
+ , $this->menu_slug // id
136
+ , $this->get_callback( 'show_admin_page' ) // callback
137
+ );
138
+ }
139
+
140
+ add_action( 'load-'. $this->page_hook, $this->get_callback( 'add_help_tabs' ) );
141
+ }
142
+
143
+ /**
144
+ * Set default option values for new created sites
145
+ * @param integer $blog_id
146
+ */
147
+ protected function action_wpmu_new_blog( $blog_id )
148
+ {
149
+ $default_site_id = $this->network_page->get_option_value( 'default_settings_site' );
150
+
151
+ foreach ( $this->tabs as $tab_key => $values ) {
152
+ if ( ! isset( $values[ 'fields' ] ) ) {
153
+ continue;
154
+ }
155
+
156
+ $option_name = $values[ 'fields' ]->get_setting( 'option_name' );
157
+
158
+ $default_option_values = get_blog_option( $default_site_id, $option_name, array() );
159
+ update_blog_option( $blog_id, $option_name, $default_option_values );
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Action for "admin_enqueue_scripts"
165
+ */
166
+ protected function action_admin_enqueue_scripts()
167
+ {
168
+ // set admin style
169
+ wp_enqueue_style(
170
+ 'wpel-admin-style'
171
+ , plugins_url( '/public/css/wpel-admin.css', WPEL_Plugin::get_plugin_file() )
172
+ , array()
173
+ , null
174
+ );
175
+
176
+ // set wpel admin script
177
+ wp_enqueue_script(
178
+ 'wpel-admin-settings'
179
+ , plugins_url( '/public/js/wpel-admin.js', WPEL_Plugin::get_plugin_file() )
180
+ , array('jquery')
181
+ , false
182
+ , true
183
+ );
184
+
185
+ // set style
186
+ wp_enqueue_style(
187
+ 'font-awesome'
188
+ , 'https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'
189
+ , array()
190
+ , null
191
+ );
192
+ }
193
+
194
+ /**
195
+ * Show Admin Page
196
+ */
197
+ protected function show_admin_page()
198
+ {
199
+ $template_file = WPEL_Plugin::get_plugin_dir( '/templates/settings-page/main.php' );
200
+ $page = $this->get_option_value( 'own_admin_menu' ) ? 'admin.php' : 'options-general.php';
201
+ $page_url = admin_url() . $page .'?page='. $this->menu_slug;
202
+
203
+ $template_vars = array(
204
+ 'tabs' => $this->tabs,
205
+ 'current_tab' => $this->current_tab,
206
+ 'page_url' => $page_url,
207
+ 'menu_slug' => $this->menu_slug,
208
+ 'own_admin_menu' => $this->get_option_value( 'own_admin_menu', 'admin' ),
209
+ );
210
+
211
+ $this->show_template( $template_file, $template_vars );
212
+ }
213
+
214
+ /**
215
+ * Add help tabs
216
+ */
217
+ protected function add_help_tabs()
218
+ {
219
+ $screen = get_current_screen();
220
+
221
+ $screen->add_help_tab( array(
222
+ 'id' => 'under-construction',
223
+ 'title' => __( 'Under Construction', 'wpel' ),
224
+ 'callback' => $this->get_callback( 'show_help_tab' ),
225
+ ) );
226
+ $screen->add_help_tab( array(
227
+ 'id' => 'data-attributes',
228
+ 'title' => __( 'Data Attributes', 'wpel' ),
229
+ 'callback' => $this->get_callback( 'show_help_tab' ),
230
+ ) );
231
+ }
232
+
233
+ /**
234
+ * @param WP_Screen $screen
235
+ * @param array $args
236
+ */
237
+ protected function show_help_tab( $screen, array $args )
238
+ {
239
+ $template_file = WPEL_Plugin::get_plugin_dir( '/templates/settings-page/help-tabs/'. $args[ 'id' ] .'.php' );
240
+ $this->show_template( $template_file );
241
+ }
242
+
243
+ }
244
+
245
+ /*?>*/
includes/admin/network-fields/class-wpel-network-admin-fields.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Network_Admin_Fields
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Network_Admin_Fields extends FWP_Settings_Section_Fields_1x0x0
14
+ {
15
+
16
+ /**
17
+ * Initialize
18
+ */
19
+ protected function init()
20
+ {
21
+ $this->set_settings( array(
22
+ 'section_id' => 'wpel-network-admin-fields',
23
+ 'page_id' => 'wpel-network-admin-fields',
24
+ 'option_name' => 'wpel-network-admin-settings',
25
+ 'option_group' => 'wpel-network-admin-settings',
26
+ 'title' => __( 'Network Admin Settings', 'wpel' ),
27
+ 'fields' => array(
28
+ 'own_admin_menu' => array(
29
+ 'label' => __( 'Main Network Admin Menu:', 'wpel' ),
30
+ 'default_value' => '1',
31
+ ),
32
+ ),
33
+ ) );
34
+
35
+ if ( is_network_admin() ) {
36
+ add_action( 'network_admin_edit_'. $this->get_setting( 'option_group' ) , $this->get_callback( 'save_network_settings' ) );
37
+ }
38
+ }
39
+
40
+ protected function save_network_settings()
41
+ {
42
+ // when calling 'settings_fields' but we must add the '-options' postfix
43
+ check_admin_referer( $this->get_setting( 'option_group' ) .'-options' );
44
+
45
+ global $new_whitelist_options;
46
+ $option_names = $new_whitelist_options[ $this->get_setting( 'option_group' ) ];
47
+
48
+ foreach ( $option_names as $option_name ) {
49
+ if ( isset( $_POST[ $option_name ] ) ) {
50
+ $post_values = $_POST[ $option_name ];
51
+ $sanitized_values = $this->sanitize( $post_values );
52
+
53
+ update_site_option( $option_name, $sanitized_values );
54
+ } else {
55
+ delete_site_option( $option_name );
56
+ }
57
+ }
58
+
59
+ $redirect_url = filter_input( INPUT_POST, '_wp_http_referer', FILTER_SANITIZE_STRING );
60
+
61
+ wp_redirect( add_query_arg(
62
+ array(
63
+ 'page' => 'wpel-network-settings-page',
64
+ 'updated' => true
65
+ )
66
+ , $redirect_url
67
+ ) );
68
+
69
+ exit;
70
+ }
71
+
72
+ /**
73
+ * Show field methods
74
+ */
75
+
76
+ protected function show_own_admin_menu( array $args )
77
+ {
78
+ $this->get_html_fields()->check_with_label(
79
+ $args[ 'key' ]
80
+ , __( 'Create own network admin menu for this plugin', 'wpel' )
81
+ , '1'
82
+ , ''
83
+ );
84
+
85
+ echo ' <p class="description">'
86
+ . __( 'Or else it will be added to the "Settings" menu', 'wpel' )
87
+ .'</p>';
88
+ }
89
+
90
+ /**
91
+ * Validate and sanitize user input before saving to databse
92
+ * @param array $new_values
93
+ * @param array $old_values
94
+ * @return array
95
+ */
96
+ protected function before_update( array $new_values, array $old_values )
97
+ {
98
+ $update_values = $new_values;
99
+ $is_valid = true;
100
+
101
+ $is_valid = $is_valid && in_array( $new_values[ 'own_admin_menu' ], array( '', '1' ) );
102
+
103
+ if ( false === $is_valid ) {
104
+ // error when user input is not valid conform the UI, probably tried to "hack"
105
+ $this->add_error( __( 'Something went wrong. One or more values were invalid.', 'wpel' ) );
106
+ return $old_values;
107
+ }
108
+
109
+ return $update_values;
110
+ }
111
+
112
+ }
113
+
114
+ /*?>*/
includes/admin/network-fields/class-wpel-network-fields.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Network_Fields
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Network_Fields extends FWP_Settings_Section_Fields_1x0x0
14
+ {
15
+
16
+ /**
17
+ * Initialize
18
+ */
19
+ protected function init()
20
+ {
21
+ $this->set_settings( array(
22
+ 'section_id' => 'wpel-network-fields',
23
+ 'page_id' => 'wpel-network-fields',
24
+ 'option_name' => 'wpel-network-settings',
25
+ 'option_group' => 'wpel-network-settings',
26
+ 'title' => __( 'Multi Site Settings', 'wpel' ),
27
+ 'fields' => array(
28
+ 'capability' => array(
29
+ 'label' => __( 'Capability for individual sites:', 'wpel' ),
30
+ 'default_value' => 'manage_options',
31
+ ),
32
+ 'default_settings_site' => array(
33
+ 'label' => __( 'Use the settings of this site as default for new sites:', 'wpel' ),
34
+ 'default_value' => '',
35
+ ),
36
+ ),
37
+ ) );
38
+
39
+ if ( is_network_admin() ) {
40
+ add_action( 'network_admin_edit_'. $this->get_setting( 'option_group' ) , $this->get_callback( 'save_network_settings' ) );
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Save network settings
46
+ * @global type $new_whitelist_options
47
+ */
48
+ protected function save_network_settings()
49
+ {
50
+ // when calling 'settings_fields' but we must add the '-options' postfix
51
+ check_admin_referer( $this->get_setting( 'option_group' ) .'-options' );
52
+
53
+ global $new_whitelist_options;
54
+ $option_names = $new_whitelist_options[ $this->get_setting( 'option_group' ) ];
55
+
56
+ foreach ( $option_names as $option_name ) {
57
+ if ( isset( $_POST[ $option_name ] ) ) {
58
+ $post_values = $_POST[ $option_name ];
59
+ $sanitized_values = $this->sanitize( $post_values );
60
+
61
+ update_site_option( $option_name, $sanitized_values );
62
+ } else {
63
+ delete_site_option( $option_name );
64
+ }
65
+ }
66
+
67
+ $redirect_url = filter_input( INPUT_POST, '_wp_http_referer', FILTER_SANITIZE_STRING );
68
+
69
+ wp_redirect( add_query_arg(
70
+ array(
71
+ 'page' => $this->get_setting( 'option_group' ) .'-page',
72
+ 'updated' => true
73
+ )
74
+ , $redirect_url
75
+ ) );
76
+
77
+ exit;
78
+ }
79
+
80
+ /**
81
+ * Show field methods
82
+ */
83
+
84
+ protected function show_capability( array $args )
85
+ {
86
+ $this->get_html_fields()->select(
87
+ $args[ 'key' ]
88
+ , array(
89
+ 'manage_options' => __( 'Site Admins and Super Admins', 'wpel' ),
90
+ 'manage_network' => __( 'Only Super Admins', 'wpel' ),
91
+ )
92
+ );
93
+ }
94
+
95
+ protected function show_default_settings_site( array $args )
96
+ {
97
+ $sites = wp_get_sites();
98
+
99
+ $values = array();
100
+ $values[ '' ] = __( '- none -', 'wpel' );
101
+
102
+ foreach ( $sites as $site ) {
103
+ $values[ $site[ 'blog_id' ] ] = 'blog_id: '. $site[ 'blog_id' ] .' - '. $site[ 'path' ];
104
+ }
105
+
106
+ $this->get_html_fields()->select(
107
+ $args[ 'key' ]
108
+ , $values
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Validate and sanitize user input before saving to databse
114
+ * @param array $new_values
115
+ * @param array $old_values
116
+ * @return array
117
+ */
118
+ protected function before_update( array $new_values, array $old_values )
119
+ {
120
+ $update_values = $new_values;
121
+
122
+ $update_values[ 'capability' ] = sanitize_text_field( $new_values[ 'capability' ] );
123
+ $update_values[ 'default_settings_site' ] = sanitize_text_field( $new_values[ 'default_settings_site' ] );
124
+
125
+ return $update_values;
126
+ }
127
+
128
+ }
129
+
130
+ /*?>*/
includes/admin/settings-fields/class-wpel-admin-fields.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Admin_Fields
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Admin_Fields extends FWP_Settings_Section_Fields_1x0x0
14
+ {
15
+
16
+ /**
17
+ * Initialize
18
+ */
19
+ protected function init()
20
+ {
21
+ $this->set_settings( array(
22
+ 'section_id' => 'wpel-admin-fields',
23
+ 'page_id' => 'wpel-admin-fields',
24
+ 'option_name' => 'wpel-admin-settings',
25
+ 'option_group' => 'wpel-admin-settings',
26
+ 'title' => __( 'Admin Settings', 'wpel' ),
27
+ 'fields' => array(
28
+ 'own_admin_menu' => array(
29
+ 'label' => __( 'Main Admin Menu:', 'wpel' ),
30
+ 'default_value' => '1',
31
+ ),
32
+ ),
33
+ ) );
34
+ }
35
+
36
+ /**
37
+ * Show field methods
38
+ */
39
+
40
+ protected function show_own_admin_menu( array $args )
41
+ {
42
+ $this->get_html_fields()->check_with_label(
43
+ $args[ 'key' ]
44
+ , __( 'Create own admin menu for this plugin', 'wpel' )
45
+ , '1'
46
+ , ''
47
+ );
48
+
49
+ echo ' <p class="description">'
50
+ . __( 'Or else it will be added to the "Settings" menu', 'wpel' )
51
+ .'</p>';
52
+ }
53
+
54
+ /**
55
+ * Validate and sanitize user input before saving to databse
56
+ * @param array $new_values
57
+ * @param array $old_values
58
+ * @return array
59
+ */
60
+ protected function before_update( array $new_values, array $old_values )
61
+ {
62
+ $update_values = $new_values;
63
+ $is_valid = true;
64
+
65
+ $is_valid = $is_valid && in_array( $new_values[ 'own_admin_menu' ], array( '', '1' ) );
66
+
67
+ if ( false === $is_valid ) {
68
+ // error when user input is not valid conform the UI, probably tried to "hack"
69
+ $this->add_error( __( 'Something went wrong. One or more values were invalid.', 'wpel' ) );
70
+ return $old_values;
71
+ }
72
+
73
+ return $update_values;
74
+ }
75
+
76
+ }
77
+
78
+ /*?>*/
includes/admin/settings-fields/class-wpel-exceptions-fields.php ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Exceptions_Fields
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Exceptions_Fields extends FWP_Settings_Section_Fields_1x0x0
14
+ {
15
+
16
+ /**
17
+ * Initialize
18
+ */
19
+ protected function init()
20
+ {
21
+ $this->set_settings( array(
22
+ 'section_id' => 'wpel-exceptions-fields',
23
+ 'page_id' => 'wpel-exceptions-fields',
24
+ 'option_name' => 'wpel-exceptions-settings',
25
+ 'option_group' => 'wpel-exceptions-settings',
26
+ 'title' => __( 'Exceptions', 'wpel' ),
27
+ 'fields' => array(
28
+ 'apply_all' => array(
29
+ 'label' => __( 'Apply settings on:', 'wpel' ),
30
+ 'class' => 'js-wpel-apply',
31
+ 'default_value' => '1',
32
+ ),
33
+ 'apply_post_content' => array(
34
+ 'class' => 'js-wpel-apply-child wpel-hidden wpel-no-label ',
35
+ 'default_value' => '1',
36
+ ),
37
+ 'apply_comments' => array(
38
+ 'class' => 'js-wpel-apply-child wpel-hidden wpel-no-label',
39
+ 'default_value' => '1',
40
+ ),
41
+ 'apply_widgets' => array(
42
+ 'class' => 'js-wpel-apply-child wpel-hidden wpel-no-label',
43
+ 'default_value' => '1',
44
+ ),
45
+ 'ignore_script_tags' => array(
46
+ 'label' => __( 'Skip <code>&lt;script&gt;</code>:', 'wpel' ),
47
+ 'default_value' => '1',
48
+ ),
49
+ 'subdomains_as_internal_links' => array(
50
+ 'label' => __( 'Make subdomains internal:', 'wpel' ),
51
+ ),
52
+ 'include_urls' => array(
53
+ 'label' => __( 'Include external links by URL:', 'wpel' ),
54
+ ),
55
+ 'exclude_urls' => array(
56
+ 'label' => __( 'Exclude external links by URL:', 'wpel' ),
57
+ ),
58
+ 'excludes_as_internal_links' => array(
59
+ 'label' => '',
60
+ 'class' => 'wpel-no-label',
61
+ ),
62
+ ),
63
+ ) );
64
+ }
65
+
66
+ /**
67
+ * Show field methods
68
+ */
69
+
70
+ protected function show_apply_all( array $args )
71
+ {
72
+ $this->get_html_fields()->check_with_label(
73
+ $args[ 'key' ]
74
+ , __( 'All contents (the whole page)', 'wpel' )
75
+ , '1'
76
+ , ''
77
+ );
78
+ }
79
+
80
+ protected function show_apply_post_content( array $args )
81
+ {
82
+ $this->get_html_fields()->check_with_label(
83
+ $args[ 'key' ]
84
+ , __( 'Post content', 'wpel' )
85
+ , '1'
86
+ , ''
87
+ );
88
+ }
89
+
90
+ protected function show_apply_comments( array $args )
91
+ {
92
+ $this->get_html_fields()->check_with_label(
93
+ $args[ 'key' ]
94
+ , __( 'Comments', 'wpel' )
95
+ , '1'
96
+ , ''
97
+ );
98
+ }
99
+
100
+ protected function show_apply_widgets( array $args )
101
+ {
102
+ $this->get_html_fields()->check_with_label(
103
+ $args[ 'key' ]
104
+ , __( 'All widgets', 'wpel' )
105
+ , '1'
106
+ , ''
107
+ );
108
+ }
109
+
110
+ protected function show_ignore_script_tags( array $args )
111
+ {
112
+ $this->get_html_fields()->check_with_label(
113
+ $args[ 'key' ]
114
+ , __( 'Ignore all links in <code>&lt;script&gt;</code> blocks', 'wpel' )
115
+ , '1'
116
+ , ''
117
+ );
118
+ }
119
+
120
+ protected function show_subdomains_as_internal_links( array $args )
121
+ {
122
+ $this->get_html_fields()->check_with_label(
123
+ $args[ 'key' ]
124
+ , __( 'Threat all links to the site\'s domain and subdomains as internal links', 'wpel' )
125
+ , '1'
126
+ , ''
127
+ );
128
+ }
129
+
130
+ protected function show_include_urls( array $args )
131
+ {
132
+ $this->show_urls_field( $args[ 'key' ] );
133
+ }
134
+
135
+ protected function show_exclude_urls( array $args )
136
+ {
137
+ $this->show_urls_field( $args[ 'key' ] );
138
+ }
139
+
140
+ protected function show_urls_field( $key )
141
+ {
142
+ $this->get_html_fields()->text_area( $key, array(
143
+ 'class' => 'large-text',
144
+ 'rows' => 4,
145
+ 'placeholder' => __( 'For example:'. "\n"
146
+ .'somedomain.org, sub.domain.net/some-slug'. "\n"
147
+ .'http://sub.moredomain.net, http://www.domain.com/other-slug', 'wpel' ),
148
+ ) );
149
+
150
+ echo '<p class="description">'
151
+ . __( 'Separate url\'s by comma and/or a line break. '
152
+ .'Write the url\'s as specific as you want them to match.', 'wpel' )
153
+ .'</p>';
154
+ }
155
+
156
+ protected function show_excludes_as_internal_links( array $args )
157
+ {
158
+ $this->get_html_fields()->check_with_label(
159
+ $args[ 'key' ]
160
+ , __( 'Treat excluded links as internal links', 'wpel' )
161
+ , '1'
162
+ , ''
163
+ );
164
+ }
165
+
166
+ /**
167
+ * Validate and sanitize user input before saving to databse
168
+ * @param array $new_values
169
+ * @param array $old_values
170
+ * @return array
171
+ */
172
+ protected function before_update( array $new_values, array $old_values )
173
+ {
174
+ $update_values = $new_values;
175
+ $is_valid = true;
176
+
177
+ $is_valid = $is_valid && in_array( $new_values[ 'apply_post_content' ], array( '', '1' ) );
178
+ $is_valid = $is_valid && in_array( $new_values[ 'apply_comments' ], array( '', '1' ) );
179
+ $is_valid = $is_valid && in_array( $new_values[ 'apply_widgets' ], array( '', '1' ) );
180
+ $is_valid = $is_valid && in_array( $new_values[ 'apply_all' ], array( '', '1' ) );
181
+ $is_valid = $is_valid && in_array( $new_values[ 'ignore_script_tags' ], array( '', '1' ) );
182
+ $is_valid = $is_valid && in_array( $new_values[ 'subdomains_as_internal_links' ], array( '', '1' ) );
183
+ $is_valid = $is_valid && in_array( $new_values[ 'excludes_as_internal_links' ], array( '', '1' ) );
184
+
185
+ if ( false === $is_valid ) {
186
+ // error when user input is not valid conform the UI, probably tried to "hack"
187
+ $this->add_error( __( 'Something went wrong. One or more values were invalid.', 'wpel' ) );
188
+ return $old_values;
189
+ }
190
+
191
+ if ( '' !== trim( $new_values[ 'include_urls' ] ) ) {
192
+ $update_values[ 'include_urls' ] = implode( "\n", array_map( 'sanitize_text_field', explode( "\n", $new_values[ 'include_urls' ] ) ) );
193
+ }
194
+
195
+ if ( '' !== trim( $new_values[ 'exclude_urls' ] ) ) {
196
+ $update_values[ 'exclude_urls' ] = implode( "\n", array_map( 'sanitize_text_field', explode( "\n", $new_values[ 'exclude_urls' ] ) ) );
197
+ }
198
+
199
+ return $update_values;
200
+ }
201
+
202
+ }
203
+
204
+ /*?>*/
includes/admin/settings-fields/class-wpel-external-link-fields.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_External_Link_Fields
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_External_Link_Fields extends WPEL_Link_Fields_Base
14
+ {
15
+
16
+ /**
17
+ * Initialize
18
+ */
19
+ protected function init()
20
+ {
21
+ $option_name = 'wpel-external-link-settings';
22
+ $fields = $this->get_general_fields( $option_name );
23
+
24
+ // specific field settings
25
+ $fields[ 'apply_settings' ][ 'label' ] = __( 'Settings for external links:', 'wpel' );
26
+ $fields[ 'apply_settings' ][ 'default_value' ] = '1';
27
+ $fields[ 'target' ][ 'label' ] = __( 'Open external links:', 'wpel' );
28
+
29
+ $this->set_settings( array(
30
+ 'section_id' => 'wpel-external-link-fields',
31
+ 'page_id' => 'wpel-external-link-fields',
32
+ 'option_name' => $option_name,
33
+ 'option_group' => $option_name,
34
+ 'title' => __( 'External Links', 'wpel' ),
35
+ 'fields' => $fields,
36
+ ) );
37
+ }
38
+
39
+ }
40
+
41
+ /*?>*/
includes/admin/settings-fields/class-wpel-internal-link-fields.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Internal_Link_Fields
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Internal_Link_Fields extends WPEL_Link_Fields_Base
14
+ {
15
+
16
+ /**
17
+ * Initialize
18
+ */
19
+ protected function init()
20
+ {
21
+ $option_name = 'wpel-internal-link-settings';
22
+ $fields = $this->get_general_fields( $option_name );
23
+
24
+ // change some specific field labels
25
+ $fields[ 'apply_settings' ][ 'label' ] = __( 'Settings for internal links:', 'wpel' );
26
+ $fields[ 'target' ][ 'label' ] = __( 'Open internal links:', 'wpel' );
27
+
28
+ $this->set_settings( array(
29
+ 'section_id' => 'wpel-internal-link-fields',
30
+ 'page_id' => 'wpel-internal-link-fields',
31
+ 'option_name' => $option_name,
32
+ 'option_group' => $option_name,
33
+ 'title' => __( 'Internal Links', 'wpel' ),
34
+ 'fields' => $fields,
35
+ ) );
36
+ }
37
+
38
+ }
39
+
40
+ /*?>*/
includes/admin/settings-fields/class-wpel-link-fields-base.php ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Link_Fields_Base
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ abstract class WPEL_Link_Fields_Base extends FWP_Settings_Section_Fields_1x0x0
14
+ {
15
+
16
+ /**
17
+ * Get general fields
18
+ * @param string $option_name
19
+ * @return array
20
+ */
21
+ final protected function get_general_fields( $option_name )
22
+ {
23
+ return array(
24
+ 'apply_settings' => array(
25
+ 'label' => __( 'Settings for links:', 'wpel' ),
26
+ 'class' => 'js-apply-settings',
27
+ ),
28
+ 'target' => array(
29
+ 'label' => __( 'Open links:', 'wpel' ),
30
+ 'class' => 'wpel-hidden',
31
+ 'default_value' => '',
32
+ ),
33
+ 'target_overwrite' => array(
34
+ 'label' => '',
35
+ 'class' => 'wpel-no-label wpel-hidden',
36
+ ),
37
+ 'rel_follow' => array(
38
+ 'label' => __( 'Set <code>follow</code> or <code>nofollow</code>:', 'wpel' ),
39
+ 'class' => 'wpel-hidden',
40
+ 'default_value' => '',
41
+ ),
42
+ 'rel_follow_overwrite' => array(
43
+ 'label' => '',
44
+ 'class' => 'wpel-no-label wpel-hidden',
45
+ ),
46
+ 'rel_external' => array(
47
+ 'label' => __( 'Also add to <code>rel</code> attribute:', 'wpel' ),
48
+ 'class' => 'wpel-hidden',
49
+ 'default_value' => '1',
50
+ ),
51
+ 'rel_noopener' => array(
52
+ 'label' => '',
53
+ 'class' => 'wpel-no-label wpel-hidden',
54
+ 'default_value' => '1',
55
+ ),
56
+ 'rel_noreferrer' => array(
57
+ 'label' => '',
58
+ 'class' => 'wpel-no-label wpel-hidden',
59
+ 'default_value' => '1',
60
+ ),
61
+ 'title' => array(
62
+ 'label' => __( 'Set <code>title</code>:', 'wpel' ),
63
+ 'class' => 'wpel-hidden',
64
+ 'default_value' => '{title}',
65
+ ),
66
+ 'class' => array(
67
+ 'label' => __( 'Add CSS class(es):', 'wpel' ),
68
+ 'class' => 'wpel-hidden',
69
+ ),
70
+ 'icon_type' => array(
71
+ 'label' => __( 'Choose icon type:', 'wpel' ),
72
+ 'class' => 'js-icon-type wpel-hidden',
73
+ ),
74
+ 'icon_image' => array(
75
+ 'label' => __( 'Choose icon image:', 'wpel' ),
76
+ 'class' => 'js-icon-type-child js-icon-type-image wpel-hidden',
77
+ 'default_value' => '1',
78
+ ),
79
+ 'icon_dashicon' => array(
80
+ 'label' => __( 'Choose dashicon:', 'wpel' ),
81
+ 'class' => 'js-icon-type-child js-icon-type-dashicon wpel-hidden',
82
+ ),
83
+ 'icon_fontawesome' => array(
84
+ 'label' => __( 'Choose FA icon:', 'wpel' ),
85
+ 'class' => 'js-icon-type-child js-icon-type-fontawesome wpel-hidden',
86
+ ),
87
+ 'icon_position' => array(
88
+ 'label' => __( 'Icon position:', 'wpel' ),
89
+ 'class' => 'js-icon-type-depend wpel-hidden',
90
+ 'default_value' => 'right',
91
+ ),
92
+ 'no_icon_for_img' => array(
93
+ 'label' => __( 'Skip icon with <code>&lt;img&gt;</code>:', 'wpel' ),
94
+ 'class' => 'js-icon-type-depend wpel-hidden',
95
+ 'default_value' => '1',
96
+ ),
97
+ );
98
+ }
99
+
100
+ /**
101
+ * Show field methods
102
+ */
103
+
104
+ protected function show_apply_settings( array $args )
105
+ {
106
+ $this->get_html_fields()->check_with_label(
107
+ $args[ 'key' ]
108
+ , __( 'Apply these settings', 'wpel' )
109
+ , '1'
110
+ , ''
111
+ );
112
+ }
113
+
114
+ protected function show_target( array $args )
115
+ {
116
+ $this->get_html_fields()->select(
117
+ $args[ 'key' ]
118
+ , array(
119
+ '' => __( '- keep as is -', 'wpel' ),
120
+ '_self' => __( 'in the same window, tab or frame', 'wpel' ),
121
+ '_blank' => __( 'each in a separate new window or tab', 'wpel' ),
122
+ '_new' => __( 'all in the same new window or tab', 'wpel' ),
123
+ '_top' => __( 'in the topmost frame', 'wpel' ),
124
+ )
125
+ );
126
+ }
127
+
128
+ protected function show_target_overwrite( array $args )
129
+ {
130
+ $this->get_html_fields()->check_with_label(
131
+ $args[ 'key' ]
132
+ , __( 'Overwrite existing values.', 'wpel' )
133
+ , '1'
134
+ , ''
135
+ );
136
+ }
137
+
138
+ protected function show_rel_follow( array $args )
139
+ {
140
+ $this->get_html_fields()->select(
141
+ $args[ 'key' ]
142
+ , array(
143
+ '' => __( '- keep as is -', 'wpel' ),
144
+ 'follow' => __( 'follow', 'wpel' ),
145
+ 'nofollow' => __( 'nofollow', 'wpel' ),
146
+ )
147
+ );
148
+ }
149
+
150
+ protected function show_rel_follow_overwrite( array $args )
151
+ {
152
+ $this->get_html_fields()->check_with_label(
153
+ $args[ 'key' ]
154
+ , __( 'Overwrite existing values.', 'wpel' )
155
+ , '1'
156
+ , ''
157
+ );
158
+ }
159
+
160
+ protected function show_rel_external( array $args )
161
+ {
162
+ $this->get_html_fields()->check_with_label(
163
+ $args[ 'key' ]
164
+ , __( 'Add <code>"external"</code>', 'wpel' )
165
+ , '1'
166
+ , ''
167
+ );
168
+ }
169
+
170
+ protected function show_rel_noopener( array $args )
171
+ {
172
+ $this->get_html_fields()->check_with_label(
173
+ $args[ 'key' ]
174
+ , __( 'Add <code>"noopener"</code>', 'wpel' )
175
+ , '1'
176
+ , ''
177
+ );
178
+ }
179
+
180
+ protected function show_rel_noreferrer( array $args )
181
+ {
182
+ $this->get_html_fields()->check_with_label(
183
+ $args[ 'key' ]
184
+ , __( 'Add <code>"noreferrer"</code>', 'wpel' )
185
+ , '1'
186
+ , ''
187
+ );
188
+ }
189
+
190
+ protected function show_title( array $args )
191
+ {
192
+ $this->get_html_fields()->text( $args[ 'key' ], array(
193
+ 'class' => 'regular-text',
194
+ ) );
195
+
196
+ echo '<p class="description">'
197
+ . __( 'Use this <code>{title}</code> for the original title value '
198
+ .'and <code>{text}</code> for the link text as shown on the page', 'wpel' )
199
+ .'</p>';
200
+ }
201
+
202
+ protected function show_class( array $args )
203
+ {
204
+ $this->get_html_fields()->text( $args[ 'key' ], array(
205
+ 'class' => 'regular-text',
206
+ ) );
207
+ }
208
+
209
+ protected function show_icon_type( array $args )
210
+ {
211
+ $this->get_html_fields()->select(
212
+ $args[ 'key' ]
213
+ , array(
214
+ '' => __( '- no icon -', 'wpel' ),
215
+ 'image' => __( 'Image', 'wpel' ),
216
+ 'dashicon' => __( 'Dashicon', 'wpel' ),
217
+ 'fontawesome' => __( 'Font Awesome', 'wpel' ),
218
+ )
219
+ );
220
+ }
221
+
222
+ protected function show_icon_image( array $args )
223
+ {
224
+ echo '<fieldset>';
225
+ echo '<div class="wpel-icon-type-image-column">';
226
+
227
+ for ( $x = 1; $x <= 20; $x++ ) {
228
+ echo '<label>';
229
+ echo $this->get_html_fields()->radio( $args[ 'key' ], strval( $x ) );
230
+ echo '<img src="'. plugins_url( '/public/images/wpel-icons/icon-'. esc_attr( $x ) .'.png', WPEL_Plugin::get_plugin_file() ) .'">';
231
+ echo '</label>';
232
+ echo '<br>';
233
+
234
+ if ( $x % 5 === 0 ) {
235
+ echo '</div>';
236
+ echo '<div class="wpel-icon-type-image-column">';
237
+ }
238
+ }
239
+
240
+ echo '</div>';
241
+ echo '</fieldset>';
242
+ }
243
+
244
+ protected function show_icon_dashicon( array $args )
245
+ {
246
+ $dashicons_str = file_get_contents( WPEL_Plugin::get_plugin_dir( '/data/json/dashicons.json' ) );
247
+ $dashicons_json = json_decode( $dashicons_str, true );
248
+ $dashicons = $dashicons_json[ 'icons' ];
249
+
250
+ $options = array();
251
+ foreach ( $dashicons as $icon ) {
252
+ $options[ $icon[ 'className' ] ] = '&#x'. $icon[ 'unicode' ];
253
+ }
254
+
255
+ $this->get_html_fields()->select( $args[ 'key' ], $options, array(
256
+ 'style' => 'font-family:dashicons',
257
+ ) );
258
+ }
259
+
260
+ protected function show_icon_fontawesome( array $args )
261
+ {
262
+ $fa_icons_str = file_get_contents( WPEL_Plugin::get_plugin_dir( '/data/json/fontawesome.json' ) );
263
+ $fa_icons_json = json_decode( $fa_icons_str, true );
264
+ $fa_icons = $fa_icons_json[ 'icons' ];
265
+
266
+ $options = array();
267
+ foreach ( $fa_icons as $icon ) {
268
+ $options[ $icon[ 'className' ] ] = '&#x'. $icon[ 'unicode' ];
269
+ }
270
+
271
+ $this->get_html_fields()->select( $args[ 'key' ], $options, array(
272
+ 'style' => 'font-family:FontAwesome',
273
+ ) );
274
+ }
275
+
276
+ protected function show_icon_position( array $args )
277
+ {
278
+ $this->get_html_fields()->select(
279
+ $args[ 'key' ]
280
+ , array(
281
+ 'left' => __( 'Left side of the link', 'wpel' ),
282
+ 'right' => __( 'Right side of the link', 'wpel' ),
283
+ )
284
+ );
285
+ }
286
+
287
+ protected function show_no_icon_for_img( array $args )
288
+ {
289
+ $this->get_html_fields()->check_with_label(
290
+ $args[ 'key' ]
291
+ , __( 'No icon for links already containing an <code>&lt;img&gt;</code>-tag.', 'wpel' )
292
+ , '1'
293
+ , ''
294
+ );
295
+ }
296
+
297
+ /**
298
+ * Validate and sanitize user input before saving to databse
299
+ * @param array $new_values
300
+ * @param array $old_values
301
+ * @return array
302
+ */
303
+ protected function before_update( array $new_values, array $old_values )
304
+ {
305
+ $update_values = $new_values;
306
+ $is_valid = true;
307
+
308
+ $is_valid = $is_valid && in_array( $new_values[ 'apply_settings' ], array( '', '1' ) );
309
+ $is_valid = $is_valid && in_array( $new_values[ 'target_overwrite' ], array( '', '1' ) );
310
+ $is_valid = $is_valid && in_array( $new_values[ 'rel_follow_overwrite' ], array( '', '1' ) );
311
+ $is_valid = $is_valid && in_array( $new_values[ 'rel_external' ], array( '', '1' ) );
312
+ $is_valid = $is_valid && in_array( $new_values[ 'rel_noopener' ], array( '', '1' ) );
313
+ $is_valid = $is_valid && in_array( $new_values[ 'rel_noreferrer' ], array( '', '1' ) );
314
+ $is_valid = $is_valid && in_array( $new_values[ 'no_icon_for_img' ], array( '', '1' ) );
315
+
316
+ if ( false === $is_valid ) {
317
+ // error when user input is not valid conform the UI, probably tried to "hack"
318
+ $this->add_error( __( 'Something went wrong. One or more values were invalid.', 'wpel' ) );
319
+ return $old_values;
320
+ }
321
+
322
+ $update_values[ 'target' ] = sanitize_text_field( $new_values[ 'target' ] );
323
+ $update_values[ 'rel_follow' ] = sanitize_text_field( $new_values[ 'rel_follow' ] );
324
+ $update_values[ 'title' ] = sanitize_text_field( $new_values[ 'title' ] );
325
+ $update_values[ 'class' ] = sanitize_text_field( $new_values[ 'class' ] );
326
+ $update_values[ 'icon_type' ] = sanitize_text_field( $new_values[ 'icon_type' ] );
327
+ $update_values[ 'icon_image' ] = sanitize_text_field( $new_values[ 'icon_image' ] );
328
+ $update_values[ 'icon_dashicon' ] = sanitize_text_field( $new_values[ 'icon_dashicon' ] );
329
+ $update_values[ 'icon_fontawesome' ] = sanitize_text_field( $new_values[ 'icon_fontawesome' ] );
330
+ $update_values[ 'icon_position' ] = sanitize_text_field( $new_values[ 'icon_position' ] );
331
+
332
+ return $update_values;
333
+ }
334
+
335
+ }
336
+
337
+ /*?>*/
includes/class-admin-external-links.php DELETED
@@ -1,608 +0,0 @@
1
- <?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
2
- if ( ! class_exists( 'Admin_External_Links' ) ):
3
-
4
- /**
5
- * Class Admin_External_Links
6
- * @category WordPress Plugins
7
- */
8
- final class Admin_External_Links {
9
-
10
- /**
11
- * Options to be saved and their default values
12
- * @var array
13
- */
14
- public $save_options = array(
15
- 'meta' => array(
16
- 'version' => NULL,
17
- ),
18
- 'main' => array(
19
- 'target' => '_none',
20
- 'filter_page' => 1,
21
- 'filter_posts' => 1,
22
- 'filter_comments' => 1,
23
- 'filter_widgets' => 1,
24
- 'ignore' => '//twitter.com/share',
25
- 'ignore_selectors' => '',
26
- 'ignore_subdomains' => 0,
27
- ),
28
- 'seo' => array(
29
- 'external' => 1,
30
- 'nofollow' => 1,
31
- 'overwrite_follow' => 0,
32
- 'title' => '%title%',
33
- 'use_js' => 1,
34
- 'load_in_footer' => 1,
35
- ),
36
- 'style' => array(
37
- 'class_name' => 'ext-link',
38
- 'icon' => 0,
39
- 'image_no_icon' => 1,
40
- 'no_icon_class' => 'no-ext-icon',
41
- 'no_icon_same_window' => 0,
42
- ),
43
- 'extra' => array(
44
- 'fix_js' => 0,
45
- ),
46
- 'screen' => array(
47
- 'menu_position' => NULL,
48
- ),
49
- );
50
-
51
- /**
52
- * Meta box page object
53
- * @var WP_Meta_Box_Page
54
- */
55
- public $meta_box_page = NULL;
56
-
57
- /**
58
- * Ajax form object
59
- * @var WP_Ajax_Option_Form
60
- */
61
- public $form = NULL;
62
- static public $staticForm = NULL;
63
-
64
-
65
- /**
66
- * Constructor
67
- */
68
- public function __construct() {
69
- $this->check_version_update();
70
-
71
- // set meta box page
72
- $this->meta_box_page = new WP_Meta_Box_Page_01();
73
-
74
- // set ajax forms (also used by front-end)
75
- $this->form = new WP_Option_Forms_01( WP_EXTERNAL_LINKS_KEY, $this->save_options );
76
- self::$staticForm = $this->form;
77
-
78
- // init admin
79
- add_action( 'admin_init', array( $this, 'admin_init' ) );
80
-
81
- if ( is_admin() ) {
82
- // set options for add_page_method
83
- $menu_pos = $this->form->set_current_option( 'screen' )->value( 'menu_position' );
84
-
85
- // init meta box page
86
- $this->meta_box_page->init(
87
- // settings
88
- array(
89
- 'page_title' => $this->__( 'WP External Links' ),
90
- 'menu_title' => $this->__( 'External Links' ),
91
- 'page_slug' => strtolower( WP_EXTERNAL_LINKS_KEY ),
92
- 'add_page_method' => ( ! empty( $menu_pos ) AND $menu_pos != 'admin.php' ) ? 'add_submenu_page' : 'add_menu_page',
93
- 'parent_slug' => ( ! empty( $menu_pos ) AND $menu_pos != 'admin.php' ) ? $menu_pos : NULL,
94
- 'column_widths' => array(
95
- 1 => array( 99 ),
96
- 2 => array( 69, 29 ),
97
- ),
98
- 'icon_url' => plugins_url( 'images/icon-wp-external-links-16.png', WP_EXTERNAL_LINKS_FILE ),
99
- ),
100
- // load callback
101
- array( $this, 'call_load_meta_box' )
102
- );
103
- }
104
- }
105
-
106
- /**
107
- * Initialize Admin
108
- */
109
- public function admin_init() {
110
- // set uninstall hook
111
- register_uninstall_hook( WP_EXTERNAL_LINKS_FILE, array( 'Admin_External_Links', 'call_uninstall' ) );
112
-
113
- // load text domain for translations
114
- load_plugin_textdomain( WP_EXTERNAL_LINKS_KEY, FALSE, dirname( plugin_basename( WP_EXTERNAL_LINKS_FILE ) ) . '/lang/' );
115
- }
116
-
117
- /**
118
- * Add to head of Admin page
119
- */
120
- public function admin_head() {
121
- echo <<< style
122
- <style type="text/css">
123
- /* WP External Links */
124
- .postbox-container { margin-left:1%; }
125
- .tooltip-help { text-decoration: none; }
126
- .tipsy { padding: 5px; }
127
- .tipsy-inner { padding: 5px 8px 4px 8px; color: white; max-width: 200px; text-align: center; text-shadow: 0 -1px 0 #333;
128
- border-top:1px solid #808080; border-botom:1px solid #6d6d6d; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px;
129
- background-color:#777; background-image:-ms-linear-gradient(bottom,#6d6d6d,#808080); background-image:-moz-linear-gradient(bottom,#6d6d6d,#808080); background-image:-o-linear-gradient(bottom,#6d6d6d,#808080); background-image:-webkit-gradient(linear,left bottom,left top,from(#6d6d6d),to(#808080)); background-image:-webkit-linear-gradient(bottom,#6d6d6d,#808080); background-image:linear-gradient(bottom,#6d6d6d,#808080);
130
- }
131
- .tipsy-north { background-position: top center; }
132
- .tipsy-south { background-position: bottom center; }
133
- .tipsy-east { background-position: right center; }
134
- .tipsy-west { background-position: center bottom; }
135
- <style>
136
- style;
137
- }
138
-
139
- /**
140
- * Translate text in current domain
141
- * @param string $text
142
- * @return string
143
- */
144
- public function __( $text ) {
145
- return translate( $text, WP_EXTERNAL_LINKS_KEY );
146
- }
147
-
148
- /**
149
- * Translate text in current domain
150
- * @param string $text
151
- * @return string
152
- */
153
- public function _e( $text ) {
154
- echo translate( $text, WP_EXTERNAL_LINKS_KEY );
155
- }
156
-
157
- /**
158
- * Load meta box action
159
- */
160
- public function call_load_meta_box( $meta_box ) {
161
- add_action( 'admin_head', array($this, 'admin_head') );
162
-
163
- // add filters
164
- $meta_box->add_title_filter( array( $this, 'call_page_title' ) )
165
- ->add_contextual_help_filter( array( $this, 'call_contextual_help' ) );
166
-
167
- // add meta boxes
168
- // add_meta_box( $title, $callback, $context = 'normal', $id = NULL, $priority = 'default', $callback_args = NULL )
169
- $meta_box->add_meta_box( $this->__( 'General Settings' ), array( $this, 'call_box_general_settings' ), 1 )
170
- ->add_meta_box( $this->__( 'SEO Settings' ), array( $this, 'call_box_seo_settings' ), 1 )
171
- ->add_meta_box( $this->__( 'Style Settings' ), array( $this, 'call_box_style_settings' ), 1 )
172
- ->add_meta_box( $this->__( 'Extra Settings' ), array( $this, 'call_box_extra_settings' ), 1 )
173
- ->add_meta_box( $this->__( 'Admin Settings' ), array( $this, 'call_box_admin_settings' ), 1 )
174
- //->add_meta_box( $this->__( 'About this Plugin' ), array( $this, 'call_box_about' ), 2 )
175
- ->add_meta_box( $this->__( 'Other Plugins' ), array( $this, 'call_box_other_plugins' ), 2 );
176
-
177
- // scripts
178
- wp_enqueue_script( 'admin-wp-external-links', plugins_url( '/js/admin-wp-external-links.js', WP_EXTERNAL_LINKS_FILE ), array( 'jquery', 'postbox' ), WP_EXTERNAL_LINKS_VERSION, true );
179
- }
180
-
181
- /**
182
- * Contextual_help (callback)
183
- * @param string $content
184
- * @return string
185
- */
186
- public function call_contextual_help( $content ) {
187
- $help = '';
188
- $help .= $this->meta_box_page->get_ob_callback( array( $this, 'call_box_about' ) );
189
- return $help . $content;
190
- }
191
-
192
- /**
193
- * Add icon to page title
194
- * @return string
195
- */
196
- public function call_page_title( $title ) {
197
- // when updated set the update message
198
- if ( isset($_GET[ 'settings-updated' ]) && $_GET[ 'settings-updated' ] == 'true' ) {
199
- $title .= '<div class="updated settings-error" id="setting-error-settings_updated">'
200
- . '<p><strong>' . __( 'Settings saved.' ) .'</strong></p>'
201
- . '</div>';
202
- }
203
-
204
- $title = '<div class="icon32" id="icon-options-custom" style="background:url( '. plugins_url( 'images/icon-wp-external-links-32.png', WP_EXTERNAL_LINKS_FILE ) .' ) no-repeat 50% 50%"><br></div>'
205
- . $title;
206
-
207
- return $title;
208
- }
209
-
210
- /**
211
- * Meta Box: General Settings
212
- */
213
- public function call_box_general_settings() {
214
- echo $this->form->set_current_option( 'main' )->open_form();
215
- ?>
216
- <fieldset class="options">
217
- <table class="form-table">
218
- <tr>
219
- <th style="width:250px;"><?php $this->_e( 'Open external links in...' ) ?>
220
- <?php echo $this->tooltip_help( 'Specify the target (window or tab) for opening external links.' ) ?></th>
221
- <td class="target_external_links">
222
- <label><?php echo $this->form->radio( 'target', '_none', array( 'class' => 'field_target' ) ); ?>
223
- <span><?php $this->_e( 'Same window or tab (<code>_none</code>)' ) ?></span></label>
224
- <?php echo $this->tooltip_help( 'Open in current window or tab, when framed in the same frame.' ) ?>
225
- <br/>
226
- <label><?php echo $this->form->radio( 'target', '_blank', array( 'class' => 'field_target' ) ); ?>
227
- <span><?php $this->_e( 'New window or tab (<code>_blank</code>)' ) ?></span></label>
228
- <?php echo $this->tooltip_help( 'Open every external link in a new window or tab.' ) ?>
229
- <br style="margin-bottom:15px;" />
230
- <label><?php echo $this->form->radio( 'target', '_top', array( 'class' => 'field_target' ) ); ?>
231
- <span><?php $this->_e( 'Topmost frame (<code>_top</code>)' ) ?></span></label>
232
- <?php echo $this->tooltip_help( 'Open in current window or tab, when framed in the topmost frame.' ) ?>
233
- <br/>
234
- <label><?php echo $this->form->radio( 'target', '_new', array( 'class' => 'field_target' ) ); ?>
235
- <span><?php $this->_e( 'Seperate window or tab (<code>_new</code>)' ) ?></span></label>
236
- <?php echo $this->tooltip_help( 'Open new window the first time and use this window for each external link.' ) ?>
237
- </td>
238
- </tr>
239
- <tr>
240
- <th style="width:250px;"><?php $this->_e( 'Apply plugin settings on...' ) ?>
241
- <?php echo $this->tooltip_help( 'Choose contents for applying settings to external links.' ) ?></th>
242
- <td>
243
- <label><?php echo $this->form->checkbox( 'filter_page', 1 ); ?>
244
- <span><?php $this->_e( 'All contents' ) ?></span> <span class="description"><?php $this->_e('(the whole <code>&lt;body&gt;</code>)') ?></span></label>
245
- <br/>&nbsp;&nbsp;&nbsp;<label><?php echo $this->form->checkbox( 'filter_posts', 1 ); ?>
246
- <span><?php $this->_e( 'Post contents' ) ?></span></label>
247
- <br/>&nbsp;&nbsp;&nbsp;<label><?php echo $this->form->checkbox( 'filter_comments', 1 ); ?>
248
- <span><?php $this->_e( 'Comments' ) ?></span></label>
249
- <br/>&nbsp;&nbsp;&nbsp;<label><?php echo $this->form->checkbox( 'filter_widgets', 1 ); ?>
250
- <span><?php
251
- if ( self::check_widget_content_filter() ):
252
- $this->_e( 'All widgets' );
253
- echo $this->tooltip_help( 'Applied to all widgets by using the "widget_content" filter of the Widget Logic plugin' );
254
- else:
255
- $this->_e( 'All text widgets' );
256
- echo $this->tooltip_help( 'Only the text widget will be applied. To apply to all widget you should select "All contents" option.' );
257
- endif;
258
- ?></span></label>
259
- </td>
260
- </tr>
261
- <tr>
262
- <th style="width:250px;"><?php $this->_e( 'Make subdomains internal' ) ?>
263
- <?php echo $this->tooltip_help( 'Treat all links to the site\'s domain and subdomains as internal links.' ) ?></th>
264
- <td>
265
- <label><?php echo $this->form->checkbox( 'ignore_subdomains', 1 ); ?>
266
- <span><?php $this->_e( 'Treat all links to the site\'s domain and subdomains as internal links' ) ?></span></label>
267
- </td>
268
- </tr>
269
- <tr>
270
- <th><?php $this->_e( 'Ignore links with URL\'s containing...' ) ?>
271
- <?php echo $this->tooltip_help( 'This plugin will completely ignore links that contain one of the given texts in the URL. Use enter to seperate each text. This check is not case sensitive.' ) ?></th>
272
- <td><label><?php echo $this->form->textarea( 'ignore' ); ?>
273
- <span class="description"><?php _e( 'Seperate each by an enter. No wildcards nescessary, f.e. <code>wordpress.org</code> will be treaded as <code>*wordpress.org*</code>' ) ?></span></label>
274
- </td>
275
- </tr>
276
- <?php
277
- // only supported for users who were using phpquery
278
- $extra = get_option( 'wp_external_links-extra' );
279
- if (isset($extra['phpquery']) && $extra['phpquery']):
280
- ?>
281
- <tr>
282
- <th><?php $this->_e( 'Ignore links by selectors...' ) ?>
283
- <?php echo $this->tooltip_help( 'The external links of these selection will be excluded for the settings of this plugin. Define the selection by using CSS selectors.' ) ?></th>
284
- <td><label><?php echo $this->form->textarea( 'ignore_selectors' ); ?>
285
- <span class="description"><?php _e( 'Define selection by using CSS selectors, f.e.: <code>.excl-ext-link, .entry-title, #comments-title</code> (look <a href="http://code.google.com/p/phpquery/wiki/Selectors" target="_blank">here</a> for available selectors).' ) ?></span></label>
286
- </td>
287
- </tr>
288
- <?php endif; ?>
289
- </table>
290
- </fieldset>
291
-
292
- <?php
293
- echo $this->form->submit();
294
- echo $this->form->close_form();
295
- }
296
-
297
- /**
298
- * Meta Box: SEO Settings
299
- */
300
- public function call_box_seo_settings() {
301
- echo $this->form->set_current_option( 'seo' )->open_form();
302
- ?>
303
- <fieldset class="options">
304
- <table class="form-table">
305
- <tr>
306
- <th style="width:250px;"><?php $this->_e( 'Add to <code>rel</code>-attribute' ) ?>
307
- <?php echo $this->tooltip_help( 'Set values for the "rel"-atribute of external links.' ) ?></th>
308
- <td><label><?php echo $this->form->checkbox( 'nofollow', 1 ); ?>
309
- <span><?php $this->_e( 'Add <code>"nofollow"</code> to external links' ) ?></span></label>
310
- <?php echo $this->tooltip_help( 'Add "nofollow" to the "rel"-attribute of external links (unless link already has "follow").' ) ?>
311
- <br/>&nbsp;&nbsp;&nbsp;<label><?php echo $this->form->checkbox( 'overwrite_follow', 1 ); ?>
312
- <span><?php $this->_e( 'Also apply to external links containing <code>"follow"</code>' ) ?></span></label>
313
- <?php echo $this->tooltip_help( 'Also change external links with "follow" to "nofollow".' ) ?>
314
-
315
- <br/><br/>
316
- <label><?php echo $this->form->checkbox( 'external', 1 ); ?>
317
- <span><?php $this->_e( 'Add <code>"external"</code>' ) ?></span></label>
318
- <?php echo $this->tooltip_help( 'Add "external" to the "rel"-attribute of external links.' ) ?>
319
- </td>
320
- </tr>
321
- <tr>
322
- <th><?php $this->_e( 'Set <code>title</code>-attribute' ) ?>
323
- <?php echo $this->tooltip_help( 'Set title attribute for external links. Use %title% for the original title value.' ) ?></th>
324
- <td><label><?php echo $this->form->text( 'title' ); ?>
325
- <br/><span class="description"><?php _e( 'Use <code>%title%</code> for the original title value.' ) ?></span></label></td>
326
- </tr>
327
- <tr>
328
- <th><?php $this->_e( 'Use JavaScript method' ) ?>
329
- <?php echo $this->tooltip_help( 'Enable this option to use the JavaScript method for opening links, which prevents adding target attribute in the HTML code.' ) ?></label>
330
- </th>
331
- <td>
332
- <label><?php echo $this->form->checkbox( 'use_js', 1, array( 'class' => 'field_use_js' ) ); ?>
333
- <span><?php $this->_e( 'Use JavaScript for opening links' ) ?></span> <span class="description"><?php $this->_e( '(valid xhtml strict)' ) ?></span>
334
- <br/>
335
- &nbsp;&nbsp;&nbsp;<label><?php echo $this->form->checkbox( 'load_in_footer', 1, array( 'class' => 'load_in_footer' ) ); ?>
336
- <span><?php $this->_e( 'Load JS file in footer' ) ?></span>
337
- </td>
338
- </tr>
339
- </table>
340
- </fieldset>
341
- <?php
342
- echo $this->form->submit();
343
- echo $this->form->close_form();
344
- }
345
-
346
- /**
347
- * Meta Box: Style Settings
348
- */
349
- public function call_box_style_settings() {
350
- echo $this->form->set_current_option( 'style' )->open_form();
351
- ?>
352
- <fieldset class="options">
353
- <table class="form-table">
354
- <tr>
355
- <th style="width:250px;"><?php $this->_e( 'Set icon for external link' ) ?>
356
- <?php echo $this->tooltip_help( 'Set an icon that wll be shown for external links. See example on the right side.' ) ?></th>
357
- <td>
358
- <div>
359
- <div style="width:15%;float:left">
360
- <label><?php echo $this->form->radio( 'icon', 0 ); ?>
361
- <span><?php $this->_e( 'No icon' ) ?></span></label>
362
- <?php for ( $x = 1; $x <= 20; $x++ ): ?>
363
- <br/>
364
- <label title="<?php echo sprintf( $this->__( 'Icon %1$s: choose this icon to show for all external links or add the class \'ext-icon-%1$s\' to a specific link.' ), $x ) ?>">
365
- <?php echo $this->form->radio( 'icon', $x ); ?>
366
- <img src="<?php echo plugins_url('images/ext-icons/ext-icon-'. $x .'.png', WP_EXTERNAL_LINKS_FILE) ?>" /></label>
367
- <?php if ( $x % 5 == 0 ): ?>
368
- </div>
369
- <div style="width:15%;float:left">
370
- <?php endif; ?>
371
- <?php endfor; ?>
372
- </div>
373
- <div style="width:29%;float:left;"><span class="description"><?php $this->_e( 'Example:' ) ?></span>
374
- <br/><img src="<?php echo plugins_url( 'images/link-icon-example.png', WP_EXTERNAL_LINKS_FILE ) ?>" />
375
- </div>
376
- <br style="clear:both" />
377
- </div>
378
- </td>
379
- </tr>
380
- <tr>
381
- <th><?php $this->_e( 'Skip images' ) ?>
382
- <?php echo $this->tooltip_help( 'Don\'t show icon for external links containing images.' ) ?></th>
383
- <td><label><?php echo $this->form->checkbox( 'image_no_icon', 1 ); ?>
384
- <span><?php $this->_e( 'No icon for extenal links with images' ) ?></span></label>
385
- </td>
386
- </tr>
387
- <tr>
388
- <th style="width:250px;"><?php $this->_e( 'Set no-icon class' ) ?>
389
- <?php echo $this->tooltip_help( 'Set this class for links, that should not have the external link icon.' ) ?></th>
390
- <td><label><?php echo $this->form->text( 'no_icon_class', array( 'class' => '' ) ); ?></label>
391
- <br/><label><?php echo $this->form->checkbox( 'no_icon_same_window', 1 ); ?>
392
- <span><?php $this->_e( 'Always open links with no-icon class in same window or tab' ) ?></span></label>
393
- <?php echo $this->tooltip_help( 'When enabled external links containing the no-icon class will always be opened in the current window or tab. No matter which target is set.' ) ?>
394
- </td>
395
- </tr>
396
- <tr>
397
- <th><?php $this->_e( 'Add to <code>class</code>-attribute' ) ?>
398
- <?php echo $this->tooltip_help( 'Add one or more extra classes to the external links, seperated by a space. It is optional, else just leave field blank.' ) ?></th>
399
- <td><label><?php echo $this->form->text( 'class_name' ); ?></label></td>
400
- </tr>
401
- </table>
402
- </fieldset>
403
- <?php
404
- echo $this->form->submit();
405
- echo $this->form->close_form();
406
- }
407
-
408
- /**
409
- * Meta Box: Extra Settings
410
- */
411
- public function call_box_extra_settings() {
412
- echo $this->form->set_current_option( 'extra' )->open_form();
413
- ?>
414
- <fieldset class="options">
415
- <table class="form-table">
416
- <tr>
417
- <th style="width:250px;"><?php $this->_e( 'Solving problems' ) ?>
418
- <?php echo $this->tooltip_help( 'Some options to try when a problem occurs. These options can also cause other problems, so be carefull.' ) ?></th>
419
- <td><label><?php echo $this->form->checkbox( 'fix_js', 1 ); ?>
420
- <span><?php $this->_e( 'Replacing <code>&lt;/a&gt;</code> with <code>&lt;\/a&gt;</code> in JavaScript code.' ) ?></span></label>
421
- <?php echo $this->tooltip_help( 'By replacing </a> with <\/a> in JavaScript code these tags will not be processed by the plugin.' ) ?>
422
- </td>
423
- </tr>
424
- </table>
425
- </fieldset>
426
- <?php
427
- echo $this->form->submit();
428
- echo $this->form->close_form();
429
- }
430
-
431
- /**
432
- * Meta Box: Extra Settings
433
- */
434
- public function call_box_admin_settings() {
435
- echo $this->form->set_current_option( 'screen' )->open_form();
436
- ?>
437
- <fieldset class="options">
438
- <table class="form-table">
439
- <tr>
440
- <th><?php $this->_e('Admin menu position') ?>
441
- <?php echo $this->tooltip_help( 'Change the menu position of this plugin in "Screen Options".' ) ?></th>
442
- <td><label>
443
- <?php
444
- echo $this->form->select( 'menu_position', array(
445
- 'admin.php' => 'Main menu',
446
- 'index.php' => $this->__( 'Subitem of Dashboard' ),
447
- 'edit.php' => $this->__( 'Subitem of Posts' ),
448
- 'upload.php' => $this->__( 'Subitem of Media' ),
449
- 'link-manager.php' => $this->__( 'Subitem of Links' ),
450
- 'edit.php?post_type=page' => $this->__( 'Subitem of Pages' ),
451
- 'edit-comments.php' => $this->__( 'Subitem of Comments' ),
452
- 'themes.php' => $this->__( 'Subitem of Appearance' ),
453
- 'plugins.php' => $this->__( 'Subitem of Plugins' ),
454
- 'users.php' => $this->__( 'Subitem of Users' ),
455
- 'tools.php' => $this->__( 'Subitem of Tools' ),
456
- 'options-general.php' => $this->__( 'Subitem of Settings' ),
457
- ));
458
- ?>
459
- </label></td>
460
- </tr>
461
- </table>
462
- </fieldset>
463
- <?php
464
- echo $this->form->submit();
465
- echo $this->form->close_form();
466
- }
467
-
468
- /**
469
- * Meta Box: About...
470
- */
471
- public function call_box_about() {
472
- ?>
473
- <h4><img src="<?php echo plugins_url( 'images/icon-wp-external-links-16.png', WP_EXTERNAL_LINKS_FILE ) ?>" width="16" height="16" /> <?php $this->_e( 'WP External Links' ) ?></h4>
474
- <div>
475
- <p><?php printf( $this->__( 'Current version: <strong>%1$s</strong>' ), WP_EXTERNAL_LINKS_VERSION ) ?></p>
476
- <p><?php $this->_e( 'Manage external links on your site: open in new window/tab, set link icon, add "external", add "nofollow" and more.' ) ?></p>
477
- <p><a href="http://www.freelancephp.net/contact/" target="_blank"><?php $this->_e( 'Questions or suggestions?' ) ?></a></p>
478
- <p><?php $this->_e( 'If you like this plugin please send your rating at WordPress.org.' ) ?></p>
479
- <p><?php _e( 'More info' ) ?>: <a href="http://wordpress.org/extend/plugins/wp-external-links/" target="_blank">WordPress.org</a> | <a href="http://www.freelancephp.net/wp-external-links-plugin/" target="_blank">FreelancePHP.net</a></p>
480
- </div>
481
- <?php
482
- }
483
-
484
- /**
485
- * Meta Box: Other Plugins
486
- */
487
- public function call_box_other_plugins() {
488
- ?>
489
- <h4><img src="<?php echo plugins_url( 'images/icon-email-encoder-bundle-16.png', WP_EXTERNAL_LINKS_FILE ); ?>" width="16" height="16" /> Email Encoder Bundle</h4>
490
- <div>
491
- <?php if ( is_plugin_active( 'email-encoder-bundle/email-encoder-bundle.php' ) ): ?>
492
- <p><?php $this->_e( 'This plugin is already activated.' ) ?> <a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/options-general.php?page=email-encoder-bundle/email-encoder-bundle.php"><?php $this->_e( 'Settings' ) ?></a></p>
493
- <?php elseif( file_exists( WP_PLUGIN_DIR . '/email-encoder-bundle/email-encoder-bundle.php' ) ): ?>
494
- <p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugins.php?plugin_status=inactive"><?php $this->_e( 'Activate this plugin.' ) ?></a></p>
495
- <?php else: ?>
496
- <p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugin-install.php?tab=search&type=term&s=Email+Encoder+Bundle+freelancephp&plugin-search-input=Search+Plugins"><?php $this->_e( 'Get this plugin now' ) ?></a></p>
497
- <?php endif; ?>
498
-
499
- <p><?php $this->_e( 'Protect email addresses on your site from spambots and being used for spamming by using one of the encoding methods.' ) ?></p>
500
- <p><?php _e( 'More info' ) ?>: <a href="http://wordpress.org/extend/plugins/email-encoder-bundle/" target="_blank">WordPress.org</a> | <a href="http://www.freelancephp.net/email-encoder-php-class-wp-plugin/" target="_blank">FreelancePHP.net</a></p>
501
- </div>
502
-
503
- <?php echo $this->hr(); ?>
504
-
505
- <h4><img src="<?php echo plugins_url( 'images/icon-wp-mailto-links-16.png', WP_EXTERNAL_LINKS_FILE ); ?>" width="16" height="16" /> WP Mailto Links</h4>
506
- <div>
507
- <?php if ( is_plugin_active( 'wp-mailto-links/wp-mailto-links.php' ) ): ?>
508
- <p><?php $this->_e( 'This plugin is already activated.' ) ?> <a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/options-general.php?page=wp-mailto-links/wp-mailto-links.php"><?php $this->_e( 'Settings' ) ?></a></p>
509
- <?php elseif( file_exists( WP_PLUGIN_DIR . '/wp-mailto-links/wp-mailto-links.php' ) ): ?>
510
- <p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugins.php?plugin_status=inactive"><?php $this->_e( 'Activate this plugin.' ) ?></a></p>
511
- <?php else: ?>
512
- <p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugin-install.php?tab=search&type=term&s=WP+Mailto+Links+freelancephp&plugin-search-input=Search+Plugins"><?php $this->_e( 'Get this plugin now' ) ?></a></p>
513
- <?php endif; ?>
514
-
515
- <p><?php $this->_e( 'Manage mailto links on your site and protect emails from spambots, set mail icon and more.' ) ?></p>
516
- <p><?php _e( 'More info' ) ?>: <a href="http://wordpress.org/extend/plugins/wp-mailto-links/" target="_blank">WordPress.org</a> | <a href="http://www.freelancephp.net/wp-mailto-links-plugin/" target="_blank">FreelancePHP.net</a></p>
517
- </div>
518
- <?php
519
- }
520
-
521
- /**
522
- * Activation callback
523
- */
524
- public function check_version_update() {
525
- // check for version
526
- $meta = get_option( 'wp_external_links-meta' );
527
- if ( $meta[ 'version' ] == WP_EXTERNAL_LINKS_VERSION )
528
- return;
529
-
530
- // set new version
531
- $meta[ 'version' ] = WP_EXTERNAL_LINKS_VERSION;
532
- update_option( 'wp_external_links-meta', $meta );
533
- $this->save_options['meta'] = $meta;
534
-
535
- // changed options in v1.80
536
- $extra = get_option( 'wp_external_links-extra' );
537
-
538
- if (isset($extra['phpquery']) && $extra['phpquery']) {
539
- $main = get_option('wp_external_links-main');
540
- $main['ignore_selectors'] = $extra['filter_excl_sel'];
541
-
542
- update_option( 'wp_external_links-main', $main );
543
- $this->save_options['main'] = $main;
544
- }
545
- }
546
-
547
- /**
548
- * Method for test purpuses
549
- */
550
- public function __options($values = null) {
551
- if (class_exists('Test_WP_Mailto_Links') && constant('WP_DEBUG') === true) {
552
- if ($values !== null) {
553
- $this->set_options($values);
554
- }
555
-
556
- return $this->options;
557
- }
558
- }
559
-
560
- /**
561
- * Uninstall callback
562
- */
563
- static public function call_uninstall() {
564
- self::$staticForm->delete_options();
565
- }
566
-
567
- /**
568
- * Set tooltip help
569
- * @param string $text
570
- * @return string
571
- */
572
- public function tooltip_help( $text ) {
573
- $text = $this->__( $text );
574
- $text = htmlentities( $text );
575
-
576
- $html = '<a href="#" class="tooltip-help" title="'. $text .'"><sup>(?)</sup></a>';
577
- return $html;
578
- }
579
-
580
- /**
581
- * Get html seperator
582
- * @return string
583
- */
584
- protected function hr() {
585
- return '<hr style="border:1px solid #FFF; border-top:1px solid #EEE;" />';
586
- }
587
-
588
-
589
- /**
590
- * Check if widget_content filter is available (Widget Logic Plugin)
591
- * @return boolean
592
- * @static
593
- */
594
- public static function check_widget_content_filter() {
595
- // set widget_content filter of Widget Logic plugin
596
- $widget_logic_opts = get_option( 'widget_logic' );
597
-
598
- if ( function_exists( 'widget_logic_expand_control' ) AND is_array( $widget_logic_opts ) AND key_exists( 'widget_logic-options-filter', $widget_logic_opts ) )
599
- return ( $widget_logic_opts[ 'widget_logic-options-filter' ] == 'checked' );
600
-
601
- return FALSE;
602
- }
603
-
604
- } // End Admin_External_Links Class
605
-
606
- endif;
607
-
608
- /* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class-wp-external-links.php DELETED
@@ -1,548 +0,0 @@
1
- <?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
2
- if ( ! class_exists( 'WP_External_Links' ) ):
3
-
4
- /**
5
- * Class WP_External_Links
6
- * @package WordPress
7
- * @since
8
- * @category WordPress Plugins
9
- */
10
- final class WP_External_Links {
11
-
12
- /**
13
- * Admin object
14
- * @var Admin_External_Links
15
- */
16
- public $admin = NULL;
17
-
18
- /**
19
- * Array of ignored links
20
- * @var type
21
- */
22
- private $ignored = array();
23
-
24
-
25
- /**
26
- * Constructor
27
- */
28
- public function __construct() {
29
- // set admin object
30
- $this->admin = new Admin_External_Links();
31
-
32
- // add actions
33
- add_action( 'wp', array( $this, 'call_wp' ) );
34
- }
35
-
36
- /**
37
- * Get domain name
38
- * @return string
39
- */
40
- private function get_domain() {
41
- if ( empty($this->_domain_name) ) {
42
- $url = get_bloginfo('wpurl');
43
- preg_match("/[a-z0-9\-]{1,63}\.[a-z\.]{2,6}$/", parse_url($url, PHP_URL_HOST), $domain_tld);
44
- $this->_domain_name = count($domain_tld) > 0 ? $domain_tld[0] : $_SERVER['SERVER_NAME'];
45
- }
46
-
47
- return $this->_domain_name;
48
- }
49
-
50
- /**
51
- * Quick helper method for getting saved option values
52
- * @param string $key
53
- * @return mixed
54
- */
55
- public function get_opt( $key ) {
56
- $lookup = $this->admin->save_options;
57
-
58
- foreach ( $lookup as $option_name => $values ) {
59
- $value = $this->admin->form->value( $key, '___NONE___', $option_name );
60
-
61
- if ($value !== '___NONE___')
62
- return $value;
63
- }
64
-
65
- throw new Exception('Option with key "' . $key . '" does not exist.');
66
- }
67
-
68
- /**
69
- * wp callback
70
- */
71
- public function call_wp() {
72
- if ( ! is_admin() && ! is_feed() ) {
73
- // add wp_head for setting js vars and css style
74
- add_action( 'wp_head', array( $this, 'call_wp_head' ) );
75
-
76
- // set js file
77
- if ( $this->get_opt( 'use_js' ) )
78
- wp_enqueue_script( 'wp-external-links', plugins_url( 'js/wp-external-links.js', WP_EXTERNAL_LINKS_FILE ), array('jquery'), WP_EXTERNAL_LINKS_VERSION, (bool) $this->get_opt( 'load_in_footer' ) );
79
-
80
- // set ignored
81
- $ignored = $this->get_opt( 'ignore' );
82
- $ignored = trim( $ignored );
83
- $ignored = explode( "\n", $ignored );
84
- $ignored = array_map( 'trim', $ignored );
85
- $ignored = array_map( 'strtolower', $ignored );
86
- $this->ignored = $ignored;
87
-
88
- // filters
89
- if ( $this->get_opt( 'filter_page' ) ) {
90
- // filter body
91
- ob_start( array( $this, 'call_filter_content' ) );
92
-
93
- // set ob flush
94
- add_action('wp_footer', array($this, 'callback_flush_buffer'), 10000);
95
-
96
- } else {
97
- // set filter priority
98
- $priority = 1000000000;
99
-
100
- // content
101
- if ( $this->get_opt( 'filter_posts' ) ) {
102
- add_filter( 'the_title', array( $this, 'call_filter_content' ), $priority );
103
- add_filter( 'the_content', array( $this, 'call_filter_content' ), $priority );
104
- add_filter( 'get_the_excerpt', array( $this, 'call_filter_content' ), $priority );
105
- // redundant:
106
- //add_filter( 'the_excerpt', array( $this, 'call_filter_content' ), $priority );
107
- }
108
-
109
- // comments
110
- if ( $this->get_opt( 'filter_comments' ) ) {
111
- add_filter( 'get_comment_text', array( $this, 'call_filter_content' ), $priority );
112
- // redundant:
113
- //add_filter( 'comment_text', array( $this, 'call_filter_content' ), $priority );
114
-
115
- add_filter( 'comment_excerpt', array( $this, 'call_filter_content' ), $priority );
116
- // redundant:
117
- //add_filter( 'get_comment_excerpt', array( $this, 'call_filter_content' ), $priority );
118
-
119
- add_filter( 'comment_url', array( $this, 'call_filter_content' ), $priority );
120
- add_filter( 'get_comment_author_url', array( $this, 'call_filter_content' ), $priority );
121
- add_filter( 'get_comment_author_link', array( $this, 'call_filter_content' ), $priority );
122
- add_filter( 'get_comment_author_url_link', array( $this, 'call_filter_content' ), $priority );
123
- }
124
-
125
- // widgets
126
- if ( $this->get_opt( 'filter_widgets' ) ) {
127
- if ( $this->admin->check_widget_content_filter() ) {
128
- // only if Widget Logic plugin is installed and 'widget_content' option is activated
129
- add_filter( 'widget_content', array( $this, 'call_filter_content' ), $priority );
130
- } else {
131
- // filter text widgets
132
- add_filter( 'widget_title', array( $this, 'call_filter_content' ), $priority );
133
- add_filter( 'widget_text', array( $this, 'call_filter_content' ), $priority );
134
- }
135
- }
136
- }
137
- }
138
-
139
- // hook
140
- do_action('wpel_ready', array($this, 'call_filter_content'), $this);
141
- }
142
-
143
- /**
144
- * End output buffer
145
- */
146
- public function callback_flush_buffer() {
147
- ob_end_flush();
148
- }
149
-
150
- /**
151
- * wp_head callback
152
- */
153
- public function call_wp_head() {
154
- $icon = $this->get_opt('icon');
155
-
156
- if ($icon) {
157
- $padding = ($icon < 20) ? 15 : 12;
158
- ?>
159
- <style type="text/css" media="screen">
160
- /* WP External Links Plugin */
161
- .ext-icon-<?php echo esc_html( $icon ) ?> { background:url(<?php echo plugins_url('/images/ext-icons/ext-icon-' . esc_html( $icon ) . '.png', WP_EXTERNAL_LINKS_FILE) ?>) no-repeat 100% 50%; padding-right:<?php echo $padding ?>px; };
162
- </style>
163
- <?php
164
- }
165
- }
166
-
167
- /**
168
- * Filter content
169
- * @param string $content
170
- * @return string
171
- */
172
- public function call_filter_content( $content ) {
173
- if ( $this->get_opt( 'fix_js' ) ) {
174
- // fix js problem by replacing </a> by <\/a>
175
- $content = preg_replace_callback( '/<script([^>]*)>(.*?)<\/script[^>]*>/is', array( $this, 'call_fix_js' ), $content );
176
- }
177
-
178
- if ( $this->get_opt( 'filter_page' ) && $this->get_opt( 'ignore_selectors' ) ) {
179
- $content = $this->set_ignored_by_selectors( $content );
180
- }
181
-
182
- return $this->filter( $content );
183
- }
184
-
185
- /**
186
- * Fix </a> in JavaScript blocks (callback for regexp)
187
- * @param array $matches Result of a preg call in filter_content()
188
- * @return string Clean code
189
- */
190
- public function call_fix_js( $matches ) {
191
- return str_replace( '</a>', '<\/a>', $matches[ 0 ] );
192
- }
193
-
194
- /**
195
- * Check if link is external
196
- * @param string $href
197
- * @return boolean
198
- */
199
- private function is_external( $href ) {
200
- $wpurl = strtolower( get_bloginfo( 'wpurl' ) );
201
-
202
- // relative url's are internal
203
- // so just check absolute url's starting with these protocols
204
- if ( substr( $href, 0, 7 ) !== 'http://'
205
- && substr( $href, 0, 8 ) !== 'https://'
206
- && substr( $href, 0, 6 ) !== 'ftp://'
207
- && substr( $href, 0, 2 ) !== '//' ) {
208
- return false;
209
- }
210
-
211
- if ( $this->get_opt( 'ignore_subdomains' ) ) {
212
- $is_external = ( strpos( $href, $this->get_domain() ) === FALSE );
213
- } else {
214
- $is_external = ( strpos( $href, $wpurl ) === FALSE );
215
- }
216
-
217
- return $is_external;
218
- }
219
-
220
- /**
221
- * Is an ignored link
222
- * @param string $href
223
- * @return boolean
224
- */
225
- private function is_ignored_by_url( $href ) {
226
- // check if this links should be ignored
227
- for ( $x = 0, $count = count($this->ignored); $x < $count; $x++ ) {
228
- if ( strrpos( $href, $this->ignored[ $x ] ) !== FALSE )
229
- return TRUE;
230
- }
231
-
232
- return FALSE;
233
- }
234
-
235
- /**
236
- * Set ignored external links selections
237
- * @param string $content
238
- * @return string
239
- */
240
- private function set_ignored_by_selectors( $content ) {
241
- // Include phpQuery
242
- if ( ! class_exists( 'phpQuery' ) ) {
243
- require_once( 'phpQuery.php' );
244
- }
245
-
246
- try {
247
- // set document
248
- //phpQuery::$debug = true;
249
- $doc = phpQuery::newDocument( $content );
250
-
251
- $ignore_selectors = $this->get_opt( 'ignore_selectors' );
252
-
253
- // set ignored by selectors
254
- if ( ! empty( $ignore_selectors ) ) {
255
- $excludes = $doc->find( $ignore_selectors );
256
-
257
- // links containing selector
258
- $excludes->filter( 'a' )->attr( 'data-wpel-ignored', 'true' );
259
-
260
- // links as descendant of element containing selector
261
- $excludes->find( 'a' )->attr( 'data-wpel-ignored', 'true' );
262
- }
263
-
264
- $doc = (string) $doc;
265
- } catch (Exception $e) {
266
- $doc = '';
267
- }
268
-
269
- if (empty($doc)) {
270
- return $content;
271
- }
272
-
273
- return $doc;
274
- }
275
-
276
- /**
277
- * Filter content
278
- * @param string $content
279
- * @return string
280
- */
281
- private function filter( $content ) {
282
- // replace links
283
- $content = preg_replace_callback( '/<a[^A-Za-z](.*?)>(.*?)<\/a[\s+]*>/is', array( $this, 'call_parse_link' ), $content );
284
-
285
- // remove style when no icon classes are found
286
- if ( strpos( $content, 'ext-icon-' ) === FALSE ) {
287
- // remove style with id wp-external-links-css
288
- $content = preg_replace( '/<link ([^>]*)wp-external-links-css([^>]*)\/>[\s+]*/i', '', $content );
289
- }
290
-
291
- return $content;
292
- }
293
-
294
- /**
295
- * Parse an attributes string into an array. If the string starts with a tag,
296
- * then the attributes on the first tag are parsed. This parses via a manual
297
- * loop and is designed to be safer than using DOMDocument.
298
- *
299
- * @param string|* $attrs
300
- * @return array
301
- *
302
- * @example parse_attrs( 'src="example.jpg" alt="example"' )
303
- * @example parse_attrs( '<img src="example.jpg" alt="example">' )
304
- * @example parse_attrs( '<a href="example"></a>' )
305
- * @example parse_attrs( '<a href="example">' )
306
- *
307
- * @link http://dev.airve.com/demo/speed_tests/php/parse_attrs.php
308
- */
309
- private function parse_attrs ($attrs) {
310
- if ( ! is_scalar($attrs) )
311
- return (array) $attrs;
312
-
313
- $attrs = str_split( trim($attrs) );
314
-
315
- if ( '<' === $attrs[0] ) # looks like a tag so strip the tagname
316
- while ( $attrs && ! ctype_space($attrs[0]) && $attrs[0] !== '>' )
317
- array_shift($attrs);
318
-
319
- $arr = array(); # output
320
- $name = ''; # for the current attr being parsed
321
- $value = ''; # for the current attr being parsed
322
- $mode = 0; # whether current char is part of the name (-), the value (+), or neither (0)
323
- $stop = false; # delimiter for the current $value being parsed
324
- $space = ' '; # a single space
325
-
326
- foreach ( $attrs as $j => $curr ) {
327
- if ( $mode < 0 ) {# name
328
- if ( '=' === $curr ) {
329
- $mode = 1;
330
- $stop = false;
331
- } elseif ( '>' === $curr ) {
332
- '' === $name or $arr[ $name ] = $value;
333
- break;
334
- } elseif ( ! ctype_space($curr) ) {
335
- if ( ctype_space( $attrs[ $j - 1 ] ) ) { # previous char
336
- '' === $name or $arr[ $name ] = ''; # previous name
337
- $name = $curr; # initiate new
338
- } else {
339
- $name .= $curr;
340
- }
341
- }
342
- } elseif ( $mode > 0 ) {# value
343
-
344
- if ( $stop === false ) {
345
- if ( ! ctype_space($curr) ) {
346
- if ( '"' === $curr || "'" === $curr ) {
347
- $value = '';
348
- $stop = $curr;
349
- } else {
350
- $value = $curr;
351
- $stop = $space;
352
- }
353
- }
354
- } elseif ( $stop === $space ? ctype_space($curr) : $curr === $stop ) {
355
- $arr[ $name ] = $value;
356
- $mode = 0;
357
- $name = $value = '';
358
- } else {
359
- $value .= $curr;
360
- }
361
- } else {# neither
362
-
363
- if ( '>' === $curr )
364
- break;
365
- if ( ! ctype_space( $curr ) ) {
366
- # initiate
367
- $name = $curr;
368
- $mode = -1;
369
- }
370
- }
371
- }
372
-
373
- # incl the final pair if it was quoteless
374
- '' === $name or $arr[ $name ] = $value;
375
-
376
- return $arr;
377
- }
378
-
379
- /**
380
- * Make a clean <a> code (callback for regexp)
381
- * @param array $matches Result of a preg call in filter_content()
382
- * @return string Clean <a> code
383
- */
384
- public function call_parse_link( $matches ) {
385
- $link = $matches[ 0 ];
386
- $label = $matches[ 2 ];
387
-
388
- // parse attributes
389
- $original_attrs = $matches[ 1 ];
390
- $original_attrs = stripslashes( $original_attrs );
391
- $original_attrs = $this->parse_attrs( $original_attrs );
392
- $attrs = $original_attrs;
393
-
394
- $rel = ( isset( $attrs[ 'rel' ] ) ) ? strtolower( $attrs[ 'rel' ] ) : '';
395
-
396
- // href preperation
397
- if (isset( $attrs[ 'href' ])) {
398
- $href = $attrs[ 'href' ];
399
- $href = strtolower( $href );
400
- $href = trim( $href );
401
- } else {
402
- $href = '';
403
- }
404
-
405
- // is an internal link?
406
- // rel=external will be treaded as external link
407
- $is_external = $this->is_external( $href );
408
- $has_rel_external = (strpos( $rel, 'external' ) !== FALSE);
409
-
410
- if ( ! $is_external && ! $has_rel_external) {
411
- return apply_filters('wpel_internal_link', $link, $label, $attrs);
412
- }
413
-
414
- // is an ignored link?
415
- $is_ignored = $this->is_ignored_by_url( $href ) || isset($attrs['data-wpel-ignored']);
416
-
417
- if ( $is_ignored ) {
418
- self::add_attr_value( $attrs, 'data-wpel-ignored', 'true' );
419
- $created_link = self::create_link($label, $attrs);
420
- return apply_filters('wpel_ignored_external_link', $created_link, $label, $attrs);
421
- }
422
-
423
- // set rel="external" (when not already set)
424
- if ( $this->get_opt( 'external' ) )
425
- self::add_attr_value( $attrs, 'rel', 'external' );
426
-
427
- // set rel="nofollow"
428
- if ( $this->get_opt( 'nofollow' ) ) {
429
- $has_follow = (strpos( $rel, 'follow' ) !== FALSE);
430
-
431
- // when doesn't have "follow" (or already "nofollow")
432
- if (! $has_follow || $this->get_opt( 'overwrite_follow' )) {
433
- if ($has_follow) {
434
- // remove "follow"
435
- //$attrs[ 'rel' ] = ;
436
- }
437
-
438
- self::add_attr_value( $attrs, 'rel', 'nofollow' );
439
- }
440
- }
441
-
442
- // set title
443
- $title_format = $this->get_opt( 'title' );
444
- $title = ( isset( $attrs[ 'title' ] ) ) ? $attrs[ 'title' ] : '';
445
- $attrs[ 'title' ] = str_replace( '%title%', $title, esc_attr( $title_format ) );
446
-
447
- // set user-defined class
448
- $class = $this->get_opt( 'class_name' );
449
- if ( $class )
450
- self::add_attr_value( $attrs, 'class', $class );
451
-
452
- // set icon class, unless no-icon class isset or another icon class ('ext-icon-...') is found or content contains image
453
- if ( $this->get_opt( 'icon' ) > 0
454
- AND ( ! $this->get_opt( 'no_icon_class' ) OR strpos( $attrs[ 'class' ], $this->get_opt( 'no_icon_class' ) ) === FALSE )
455
- AND strpos( $attrs[ 'class' ], 'ext-icon-' ) === FALSE
456
- AND !( $this->get_opt( 'image_no_icon' ) AND (bool) preg_match( '/<img([^>]*)>/is', $label )) ){
457
- $icon_class = 'ext-icon-'. $this->get_opt( 'icon', 'style' );
458
- self::add_attr_value( $attrs, 'class', $icon_class );
459
- }
460
-
461
- // set target
462
- $no_icon_class = $this->get_opt( 'no_icon_class' );
463
- $target = $this->get_opt( 'target' );
464
-
465
- // remove target
466
- unset($attrs[ 'target' ]);
467
-
468
- if ($this->get_opt( 'no_icon_same_window' )
469
- AND $no_icon_class AND strpos( $attrs[ 'class' ], $no_icon_class ) !== FALSE) {
470
- // open in same window
471
- } elseif ($target && $target !== '_none') {
472
- if ($this->get_opt( 'use_js' )) {
473
- // add data-attr for javascript
474
- $attrs['data-wpel-target'] = esc_attr( $target );
475
- } else {
476
- // set target value
477
- $attrs[ 'target' ] = esc_attr( $target );
478
- }
479
- }
480
-
481
- // filter hook for changing attributes
482
- $attrs = apply_filters('wpel_external_link_attrs', $attrs, $original_attrs, $label);
483
-
484
- // create element code
485
- $created_link = self::create_link($label, $attrs);
486
-
487
- // filter
488
- $created_link = apply_filters('wpel_external_link', $created_link, $link, $label, $attrs, FALSE /* only used for backwards compatibility */);
489
-
490
- return $created_link;
491
- }
492
-
493
- /**
494
- * Create a HTML link <a>
495
- * @param string $label
496
- * @param array $attrs
497
- * @return string
498
- */
499
- public static function create_link($label, $attrs) {
500
- $created_link = '<a';
501
-
502
- foreach ( $attrs AS $key => $value ) {
503
- $created_link .= ' '. $key .'="'. $value .'"';
504
- }
505
-
506
- $created_link .= '>'. $label .'</a>';
507
- return $created_link;
508
- }
509
-
510
- /**
511
- * Add value to attribute
512
- * @param array &$attrs
513
- * @param string $attr_name
514
- * @param string $value
515
- * @param string $default Optional, default NULL which means the attribute will be removed when (new) value is empty
516
- * @return New value
517
- */
518
- public static function add_attr_value( &$attrs, $attr_name, $value, $default = NULL ) {
519
- if ( key_exists( $attr_name, $attrs ) )
520
- $old_value = $attrs[ $attr_name ];
521
-
522
- if ( empty( $old_value ) )
523
- $old_value = '';
524
-
525
- $split = explode( ' ', strtolower( $old_value ) );
526
-
527
- if ( in_array( $value, $split ) ) {
528
- $value = $old_value;
529
- } else {
530
- $value = ( empty( $old_value ) )
531
- ? $value
532
- : $old_value .' '. $value;
533
- }
534
-
535
- if ( empty( $value ) AND $default === NULL ) {
536
- unset( $attrs[ $attr_name ] );
537
- } else {
538
- $attrs[ $attr_name ] = esc_attr( $value );
539
- }
540
-
541
- return $value;
542
- }
543
-
544
- } // End WP_External_Links Class
545
-
546
- endif;
547
-
548
- /* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class-wpel-front-ignore.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Front_Ignore
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Front_Ignore extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * @var array
18
+ */
19
+ private $content_placeholders = array();
20
+
21
+ /**
22
+ * @var WPEL_Settings_Page
23
+ */
24
+ private $settings_page = null;
25
+
26
+ /**
27
+ * Initialize
28
+ * @param WPEL_Settings_Page $settings_page
29
+ */
30
+ protected function init( WPEL_Settings_Page $settings_page )
31
+ {
32
+ $this->settings_page = $settings_page;
33
+ }
34
+
35
+ /**
36
+ * Get option value
37
+ * @param string $key
38
+ * @param string|null $type
39
+ * @return string
40
+ * @triggers E_USER_NOTICE Option value cannot be found
41
+ */
42
+ protected function opt( $key, $type = null )
43
+ {
44
+ return $this->settings_page->get_option_value( $key, $type );
45
+ }
46
+
47
+ /**
48
+ * Filter for "wpel_before_filter"
49
+ * @param string $content
50
+ * @return string
51
+ */
52
+ protected function filter_wpel_before_filter_10000000000( $content )
53
+ {
54
+ $content = preg_replace_callback(
55
+ $this->get_tag_regexp( 'head' )
56
+ , $this->get_callback( 'skip_tag' )
57
+ , $content
58
+ );
59
+
60
+ if ( $this->opt( 'ignore_script_tags' ) ) {
61
+ $content = preg_replace_callback(
62
+ $this->get_tag_regexp( 'script' )
63
+ , $this->get_callback( 'skip_tag' )
64
+ , $content
65
+ );
66
+ }
67
+
68
+ return $content;
69
+ }
70
+
71
+ /**
72
+ *
73
+ * @param type $tag_name
74
+ * @return type
75
+ */
76
+ protected function get_tag_regexp( $tag_name )
77
+ {
78
+ return '/<'. $tag_name .'([^>]*)>(.*?)<\/'. $tag_name .'[^>]*>/is';
79
+ }
80
+
81
+ /**
82
+ * Filter for "wpel_after_filter"
83
+ * @param string $content
84
+ * @return string
85
+ */
86
+ protected function filter_wpel_after_filter_10000000000( $content )
87
+ {
88
+ return $this->restore_content_placeholders( $content );
89
+ }
90
+
91
+ // protected function action_wp()
92
+ // {
93
+ // global $post;
94
+ //// debug( gettype( $post->ID ) );
95
+ //// add_filter( 'wpel_apply_settings', '__return_false' );
96
+ // add_filter( 'wpel_apply_settings', function () use ( $post ) {
97
+ // $excluded_posts = array( 1, 2, 4 );
98
+ //
99
+ // if ( in_array( $post->ID, $excluded_posts ) ) {
100
+ // return false;
101
+ // }
102
+ //
103
+ // return true;
104
+ // } );
105
+ // }
106
+
107
+ // protected function action_wpel_link( $link_object )
108
+ // {
109
+ // if ( $link_object->isExternal() ) {
110
+ // $url = $link_object->getAttribute( 'href' );
111
+ // $redirect_url = '//somedom.com?url='. urlencode( $url );
112
+ // $link_object->setAttribute( 'href', $redirect_url );
113
+ // }
114
+ // }
115
+
116
+ /**
117
+ * Pregmatch callback
118
+ * @param array $matches
119
+ * @return string
120
+ */
121
+ protected function skip_tag( $matches )
122
+ {
123
+ $skip_content = $matches[ 0 ];
124
+ return $this->get_placeholder( $skip_content );
125
+ }
126
+
127
+ /**
128
+ * Return placeholder text for given content
129
+ * @param string $placeholding_content
130
+ * @return string
131
+ */
132
+ protected function get_placeholder( $placeholding_content )
133
+ {
134
+ $placeholder = '<!--- WPEL PLACEHOLDER '. count( $this->content_placeholders ) .' --->';
135
+ $this->content_placeholders[ $placeholder ] = $placeholding_content;
136
+ return $placeholder;
137
+ }
138
+
139
+ /**
140
+ * Restore placeholders with original content
141
+ * @param string $content
142
+ * @return string
143
+ */
144
+ protected function restore_content_placeholders( $content )
145
+ {
146
+ foreach ( $this->content_placeholders as $placeholder => $placeholding_content ) {
147
+ $content = str_replace( $placeholder, $placeholding_content, $content );
148
+ }
149
+
150
+ return $content;
151
+ }
152
+
153
+ }
154
+
155
+ /*?>*/
includes/class-wpel-front.php ADDED
@@ -0,0 +1,443 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Front
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Front extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * @var WPEL_Settings_Page
18
+ */
19
+ private $settings_page = null;
20
+
21
+ /**
22
+ * Initialize
23
+ * @param WPEL_Settings_Page $settings_page
24
+ */
25
+ protected function init( WPEL_Settings_Page $settings_page )
26
+ {
27
+ $this->settings_page = $settings_page;
28
+
29
+ // apply page sections
30
+ if ( $this->opt( 'apply_all' ) ) {
31
+ add_action( 'final_output', $this->get_callback( 'scan' ) );
32
+ } else {
33
+ $filter_hooks = array();
34
+
35
+ if ( $this->opt( 'apply_post_content' ) ) {
36
+ array_push( $filter_hooks, 'the_title', 'the_content', 'the_excerpt', 'get_the_excerpt' );
37
+ }
38
+
39
+ if ( $this->opt( 'apply_comments' ) ) {
40
+ array_push( $filter_hooks, 'comment_text', 'comment_excerpt' );
41
+ }
42
+
43
+ if ( $this->opt( 'apply_widgets' ) ) {
44
+ array_push( $filter_hooks, 'widget_output' );
45
+ }
46
+
47
+ foreach ( $filter_hooks as $hook ) {
48
+ add_filter( $hook, $this->get_callback( 'scan' ) );
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Get option value
55
+ * @param string $key
56
+ * @param string|null $type
57
+ * @return string
58
+ * @triggers E_USER_NOTICE Option value cannot be found
59
+ */
60
+ protected function opt( $key, $type = null )
61
+ {
62
+ return $this->settings_page->get_option_value( $key, $type );
63
+ }
64
+
65
+ /**
66
+ * Enqueue scripts and styles
67
+ */
68
+ protected function action_wp_enqueue_scripts()
69
+ {
70
+ $icon_type_int = $this->opt( 'icon_type', 'internal-links' );
71
+ $icon_type_ext = $this->opt( 'icon_type', 'external-links' );
72
+
73
+ if ( 'dashicon' === $icon_type_int || 'dashicon' === $icon_type_ext ) {
74
+ wp_enqueue_style( 'dashicons' );
75
+ }
76
+
77
+ if ( 'fontawesome' === $icon_type_int || 'fontawesome' === $icon_type_ext ) {
78
+ wp_enqueue_style(
79
+ 'font-awesome'
80
+ , 'https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'
81
+ , array()
82
+ , null
83
+ );
84
+ }
85
+
86
+ if ( $this->opt( 'icon_type', 'external-links' ) || $this->opt( 'icon_type', 'internal-links' ) ) {
87
+ wp_enqueue_style(
88
+ 'wpel-style'
89
+ , plugins_url( '/public/css/wpel.css', WPEL_Plugin::get_plugin_file() )
90
+ , array()
91
+ , null
92
+ );
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Scan content for links
98
+ * @param string $content
99
+ * @return string
100
+ */
101
+ public function scan( $content )
102
+ {
103
+ /**
104
+ * Filter whether the plugin settings will be applied on links
105
+ * @param boolean $apply_settings
106
+ */
107
+ $apply_settings = apply_filters( 'wpel_apply_settings', true );
108
+
109
+ if ( false === $apply_settings ) {
110
+ return $content;
111
+ }
112
+
113
+ /**
114
+ * Filters before scanning content
115
+ * @param string $content
116
+ */
117
+ $content = apply_filters( 'wpel_before_filter', $content );
118
+
119
+ $regexp_link = '/<a[^A-Za-z](.*?)>(.*?)<\/a[\s+]*>/is';
120
+
121
+ /**
122
+ * Filters for changing regular expression for getting html links
123
+ * @param string $regexp_link
124
+ */
125
+ $regexp_link = apply_filters( 'wpel_regexp_link', $regexp_link );
126
+
127
+ $content = preg_replace_callback( $regexp_link, $this->get_callback( 'match_link' ), $content );
128
+
129
+ /**
130
+ * Filters after scanning content
131
+ * @param string $content
132
+ */
133
+ $content = apply_filters( 'wpel_after_filter', $content );
134
+
135
+ return $content;
136
+ }
137
+
138
+ /**
139
+ * Pregmatch callback for handling link
140
+ * @param array $matches [ 0 ] => link, [ 1 ] => atts_string, [ 2 ] => label
141
+ * @return string
142
+ */
143
+ protected function match_link( $matches )
144
+ {
145
+ $original_link = $matches[ 0 ];
146
+ $atts = $this->parse_atts( $matches[ 1 ] ) ?: array();
147
+ $label = $matches[ 2 ];
148
+
149
+ $created_link = $this->get_created_link( $label, $atts );
150
+
151
+ if ( false === $created_link ) {
152
+ return $original_link;
153
+ }
154
+
155
+ return $created_link;
156
+ }
157
+
158
+ /**
159
+ * Parse html attributes
160
+ * @param string $atts
161
+ * @return array
162
+ */
163
+ protected function parse_atts( $atts )
164
+ {
165
+ $regexp_atts = '/([\w\-]+)=([^"\'> ]+|([\'"]?)(?:[^\3]|\3+)+?\3)/';
166
+ preg_match_all( $regexp_atts, $atts, $matches );
167
+
168
+ $atts_arr = array();
169
+
170
+ for ( $x = 0, $count = count( $matches[ 0 ] ); $x < $count; $x += 1 ) {
171
+ $attr_name = $matches[ 1 ][ $x ];
172
+ $attr_value = str_replace( $matches[ 3 ][ $x ], '', $matches[ 2 ][ $x ] );
173
+
174
+ $atts_arr[ $attr_name ] = $attr_value;
175
+ }
176
+
177
+ return $atts_arr;
178
+ }
179
+
180
+ /**
181
+ * Create html link
182
+ * @param string $label
183
+ * @param array $atts
184
+ * @return string
185
+ */
186
+ protected function get_created_link( $label, array $atts )
187
+ {
188
+ $link = WPEL_Link::create( 'a', $label, $atts );
189
+
190
+ if ( $link->isIgnore() ) {
191
+ return false;
192
+ }
193
+
194
+ $this->set_link( $link );
195
+
196
+ return $link->getHTML();
197
+ }
198
+
199
+ /**
200
+ * Set link
201
+ * @param WPEL_Link $link
202
+ */
203
+ protected function set_link( WPEL_Link $link )
204
+ {
205
+ $url = $link->getAttribute( 'href' );
206
+
207
+ $excludes_as_internal_links = $this->opt( 'excludes_as_internal_links' );
208
+
209
+ // exceptions
210
+ $is_included = $link->isExternal() || $this->is_included_url( $url );
211
+ $is_excluded = $link->isExclude() || $this->is_excluded_url( $url );
212
+
213
+ // is internal or external
214
+ $is_internal = ( $link->isInternal() || $this->is_internal_url( $url ) ) || ( $is_excluded && $excludes_as_internal_links );
215
+ $is_external = ( $link->isExternal() || $is_included ) || ( ! $is_internal && ! $is_excluded );
216
+
217
+ if ( $is_external ) {
218
+ $link->setExternal();
219
+ $this->apply_link_settings( $link, 'external-links' );
220
+ } else if ( $is_internal ) {
221
+ $link->setInternal();
222
+ $this->apply_link_settings( $link, 'internal-links' );
223
+ } else {
224
+ $link->setExclude();
225
+ }
226
+
227
+ /**
228
+ * Action for changing link object
229
+ * @param WPEL_Link $link
230
+ * @return void
231
+ */
232
+ do_action( 'wpel_link', $link );
233
+ }
234
+
235
+ /**
236
+ * @param WPEL_Link $link
237
+ * @param string $type
238
+ */
239
+ protected function apply_link_settings( WPEL_Link $link, $type )
240
+ {
241
+ if ( ! $this->opt( 'apply_settings', $type ) ) {
242
+ return;
243
+ }
244
+
245
+ // set target
246
+ $target = $this->opt( 'target', $type );
247
+ $target_overwrite = $this->opt( 'target_overwrite', $type );
248
+ $has_target = $link->hasAttribute( 'target' );
249
+
250
+ if ( $target && ( ! $has_target || $target_overwrite ) ) {
251
+ $link->setAttribute( 'target', $target );
252
+ }
253
+
254
+ // add "follow" / "nofollow"
255
+ $follow = $this->opt( 'rel_follow', $type );
256
+ $follow_overwrite = $this->opt( 'rel_follow_overwrite', $type );
257
+ $has_follow = $link->hasAttributeValue( 'rel', 'follow' ) || $link->hasAttributeValue( 'rel', 'nofollow' );
258
+
259
+ if ( $follow && ( ! $has_follow || $follow_overwrite ) ) {
260
+ if ( $has_follow ) {
261
+ $link->removeFromAttribute( 'rel', 'follow' );
262
+ $link->removeFromAttribute( 'rel', 'nofollow' );
263
+ }
264
+
265
+ $link->addToAttribute( 'rel', $follow );
266
+ }
267
+
268
+ // add "external"
269
+ if ( $this->opt( 'rel_external', $type ) ) {
270
+ $link->addToAttribute( 'rel', 'external' );
271
+ }
272
+
273
+ // add "noopener"
274
+ if ( $this->opt( 'rel_noopener', $type ) ) {
275
+ $link->addToAttribute( 'rel', 'noopener' );
276
+ }
277
+
278
+ // add "noreferrer"
279
+ if ( $this->opt( 'rel_noreferrer', $type ) ) {
280
+ $link->addToAttribute( 'rel', 'noreferrer' );
281
+ }
282
+
283
+ // set title
284
+ $title_format = $this->opt( 'title', $type );
285
+
286
+ if ( $title_format ) {
287
+ $title = $link->getAttribute( 'title' );
288
+ $text = esc_attr( $link->getContent() );
289
+ $new_title = str_replace( array( '{title}', '{text}' ), array( $title, $text ), $title_format );
290
+
291
+ if ( $new_title ) {
292
+ $link->setAttribute( 'title', $new_title );
293
+ }
294
+ }
295
+
296
+ // add classes
297
+ $class = $this->opt( 'class', $type );
298
+
299
+ if ( $class ) {
300
+ $link->addToAttribute( 'class', $class );
301
+ }
302
+
303
+ // add icon
304
+ $icon_type = $this->opt( 'icon_type', $type );
305
+ $no_icon_for_img = $this->opt( 'no_icon_for_img', $type );
306
+ $has_img = preg_match( '/<img([^>]*)>/is', $link->getContent() );
307
+
308
+ if ( $icon_type && ! ( $has_img && $no_icon_for_img ) ) {
309
+ if ( 'dashicon' === $icon_type ) {
310
+ $dashicon = $this->opt( 'icon_dashicon', $type );
311
+ $icon = FWP_DOM_Element_1x0x0::create( 'i', '', array(
312
+ 'class' => 'wpel-icon dashicons-before '. $dashicon,
313
+ 'aria-hidden' => 'true',
314
+ ) );
315
+ } else if ( 'fontawesome' === $icon_type ) {
316
+ $fa = $this->opt( 'icon_fontawesome', $type );
317
+ $icon = FWP_DOM_Element_1x0x0::create( 'i', '', array(
318
+ 'class' => 'wpel-icon fa '. $fa,
319
+ 'aria-hidden' => 'true',
320
+ ) );
321
+ } else if ( 'image' === $icon_type ) {
322
+ $image = $this->opt( 'icon_image', $type );
323
+ $icon = FWP_DOM_Element_1x0x0::create( 'span', null, array(
324
+ 'class' => 'wpel-icon wpel-image wpel-icon-'. $image,
325
+ ) );
326
+ }
327
+
328
+ if ( 'left' === $this->opt( 'icon_position', $type ) ) {
329
+ $link->addToAttribute( 'class', 'wpel-icon-left' );
330
+ $link->prependChild( $icon );
331
+ } else if ( 'right' === $this->opt( 'icon_position', $type ) ) {
332
+ $link->addToAttribute( 'class', 'wpel-icon-right' );
333
+ $link->appendChild( $icon );
334
+ }
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Check if url is included as external link
340
+ * @param string $url
341
+ * @return boolean
342
+ */
343
+ protected function is_included_url( $url )
344
+ {
345
+ // should be using private property, but static is more practical
346
+ static $include_urls_arr = null;
347
+
348
+ if ( null === $include_urls_arr ) {
349
+ $include_urls = $this->opt( 'include_urls' );
350
+ $include_urls = str_replace( "\n", ',', $include_urls );
351
+ $include_urls_arr = array_map( 'trim', explode( ',', $include_urls ) );
352
+ }
353
+
354
+ foreach ( $include_urls_arr as $include_url ) {
355
+ if ( false !== strrpos( $url, $include_url ) ) {
356
+ return true;
357
+ }
358
+ }
359
+
360
+ return false;
361
+ }
362
+
363
+ /**
364
+ * Check if url is excluded as external link
365
+ * @param string $url
366
+ * @return boolean
367
+ */
368
+ protected function is_excluded_url( $url )
369
+ {
370
+ // should be using private property, but static is more practical
371
+ static $exclude_urls_arr = null;
372
+
373
+ if ( null === $exclude_urls_arr ) {
374
+ $exclude_urls = $this->opt( 'exclude_urls' );
375
+ $exclude_urls = str_replace( "\n", ',', $exclude_urls );
376
+ $exclude_urls_arr = array_map( 'trim', explode( ',', $exclude_urls ) );
377
+ }
378
+
379
+ foreach ( $exclude_urls_arr as $exclude_url ) {
380
+ if ( false !== strrpos( $url, $exclude_url ) ) {
381
+ return true;
382
+ }
383
+ }
384
+
385
+ return false;
386
+ }
387
+
388
+ /**
389
+ * Check url is internal
390
+ * @param string $url
391
+ * @return boolean
392
+ */
393
+ protected function is_internal_url( $url )
394
+ {
395
+ // all relative url's are internal
396
+ if ( substr( $url, 0, 7 ) !== 'http://'
397
+ && substr( $url, 0, 8 ) !== 'https://'
398
+ && substr( $url, 0, 6 ) !== 'ftp://'
399
+ && substr( $url, 0, 2 ) !== '//' ) {
400
+ return true;
401
+ }
402
+
403
+ // is internal
404
+ if ( false !== strpos( $url, home_url() ) ) {
405
+ return true;
406
+ }
407
+
408
+ // check subdomains
409
+ if ( $this->opt( 'subdomains_as_internal_links' ) && false !== strpos( $url, $this->get_domain() ) ) {
410
+ return true;
411
+ }
412
+
413
+ return false;
414
+ }
415
+
416
+ /**
417
+ * Get domain name
418
+ * @return string
419
+ */
420
+ protected function get_domain() {
421
+ // should be using private property, but static is more practical
422
+ static $domain_name = null;
423
+
424
+ if ( null === $domain_name ) {
425
+ preg_match(
426
+ '/[a-z0-9\-]{1,63}\.[a-z\.]{2,6}$/'
427
+ , parse_url( home_url(), PHP_URL_HOST )
428
+ , $domain_tld
429
+ );
430
+
431
+ if ( count( $domain_tld ) > 0 ) {
432
+ $domain_name = $domain_tld[ 0 ];
433
+ } else {
434
+ $domain_name = $_SERVER[ 'SERVER_NAME' ];
435
+ }
436
+ }
437
+
438
+ return $domain_name;
439
+ }
440
+
441
+ }
442
+
443
+ /*?>*/
includes/class-wpel-link.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Link
4
+ *
5
+ * This class extends DOMElement which uses the camelCase naming style.
6
+ * Therefore this class also contains camelCase names.
7
+ *
8
+ * @package WPEL
9
+ * @category WordPress Plugin
10
+ * @version 2.0.0
11
+ * @author Victor Villaverde Laan
12
+ * @link http://www.finewebdev.com
13
+ * @link https://github.com/freelancephp/WP-External-Links
14
+ * @license Dual licensed under the MIT and GPLv2+ licenses
15
+ */
16
+ class WPEL_Link extends FWP_DOM_Element_1x0x0
17
+ {
18
+
19
+ /**
20
+ * Mark as external link (by setting data attribute)
21
+ */
22
+ public function setExternal()
23
+ {
24
+ $this->setAttribute( 'data-wpel-link', 'external' );
25
+ }
26
+
27
+ /**
28
+ * Is marked as external link
29
+ * @return boolean
30
+ */
31
+ public function isExternal()
32
+ {
33
+ return 'external' === $this->getAttribute( 'data-wpel-link' );
34
+ }
35
+
36
+ /**
37
+ * Mark as internal link (by setting data attribute)
38
+ */
39
+ public function setInternal()
40
+ {
41
+ $this->setAttribute( 'data-wpel-link', 'internal' );
42
+ }
43
+
44
+ /**
45
+ * Is marked as internal link
46
+ * @return boolean
47
+ */
48
+ public function isInternal()
49
+ {
50
+ return 'internal' === $this->getAttribute( 'data-wpel-link' );
51
+ }
52
+
53
+ /**
54
+ * Mark as excluded link (by setting data attribute)
55
+ */
56
+ public function setExclude()
57
+ {
58
+ $this->setAttribute( 'data-wpel-link', 'exclude' );
59
+ }
60
+
61
+ /**
62
+ * Is marked as excluded link
63
+ * @return boolean
64
+ */
65
+ public function isExclude()
66
+ {
67
+ return 'exclude' === $this->getAttribute( 'data-wpel-link' );
68
+ }
69
+
70
+ /**
71
+ * Is marked as ignored link
72
+ * @return boolean
73
+ */
74
+ public function isIgnore()
75
+ {
76
+ return 'ignore' === $this->getAttribute( 'data-wpel-link' );
77
+ }
78
+
79
+ }
80
+
81
+ /*?>*/
includes/class-wpel-plugin.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Plugin
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Plugin extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * @var string
18
+ */
19
+ private static $plugin_file = null;
20
+
21
+ /**
22
+ * @var string
23
+ */
24
+ private static $plugin_dir = null;
25
+
26
+ /**
27
+ * Initialize plugin
28
+ * @param string $plugin_file
29
+ * @param string $plugin_dir
30
+ */
31
+ protected function init( $plugin_file, $plugin_dir )
32
+ {
33
+ self::$plugin_file = $plugin_file;
34
+ self::$plugin_dir = untrailingslashit( $plugin_dir );
35
+
36
+ WPEL_Registerhooks::create();
37
+ WPEL_Textdomain::create();
38
+
39
+ // network admin page
40
+ $network_page = WPEL_Network_Page::create( array(
41
+ 'network-settings' => WPEL_Network_Fields::create(),
42
+ 'network-admin-settings' => WPEL_Network_Admin_Fields::create(),
43
+ ) );
44
+
45
+ // admin settings page
46
+ $settings_page = WPEL_Settings_Page::create( $network_page, array(
47
+ 'external-links' => WPEL_External_Link_Fields::create(),
48
+ 'internal-links' => WPEL_Internal_Link_Fields::create(),
49
+ 'admin' => WPEL_Admin_Fields::create(),
50
+ 'exceptions' => WPEL_Exceptions_Fields::create(),
51
+ ) );
52
+
53
+ // front site
54
+ if ( ! is_admin() ) {
55
+ // filter hooks
56
+ FWP_Final_Output_1x0x0::create();
57
+ FWP_Widget_Output_1x0x0::create();
58
+
59
+ // front site
60
+ WPEL_Front::create( $settings_page );
61
+ WPEL_Front_Ignore::create( $settings_page );
62
+
63
+ WPEL_Template_Tags::create();
64
+ }
65
+ }
66
+
67
+ /**
68
+ * @return string
69
+ */
70
+ public static function get_plugin_file()
71
+ {
72
+ return self::$plugin_file;
73
+ }
74
+
75
+ /**
76
+ * @param string $path Optional
77
+ * @return string
78
+ */
79
+ public static function get_plugin_dir( $path = '' )
80
+ {
81
+ return self::$plugin_dir . $path;
82
+ }
83
+
84
+ }
85
+
86
+ /*?>*/
includes/class-wpel-registerhooks.php ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_RegisterHooks
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Registerhooks extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * Initialize
18
+ */
19
+ protected function init()
20
+ {
21
+ register_activation_hook(
22
+ WPEL_Plugin::get_plugin_file()
23
+ , $this->get_callback( 'activate' )
24
+ );
25
+
26
+ register_uninstall_hook(
27
+ WPEL_Plugin::get_plugin_file()
28
+ , $this->get_callback( 'uninstall' )
29
+ );
30
+ }
31
+
32
+ /**
33
+ * Plugin activation procedure
34
+ */
35
+ protected function activate( $networkwide )
36
+ {
37
+ global $wpdb;
38
+
39
+ if ( is_multisite() && $networkwide ) {
40
+ // network activation
41
+ $sites = wp_get_sites();
42
+ $active_blog = $wpdb->blogid;
43
+
44
+ foreach ( $sites as $site ) {
45
+ switch_to_blog( $site[ 'blog_id' ] );
46
+ $this->activate_site();
47
+ }
48
+
49
+ // switch back to active blog
50
+ switch_to_blog( $active_blog );
51
+
52
+ $this->activate_network();
53
+ } else {
54
+ // single site activation
55
+ $this->activate_site();
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Activate network
61
+ * @return void
62
+ */
63
+ private function activate_network()
64
+ {
65
+ $network_already_set = get_site_option( 'wpel-network-settings' );
66
+
67
+ if ( $network_already_set ) {
68
+ return;
69
+ }
70
+
71
+ // network default settings
72
+ $network_values = WPEL_Network_Fields::get_instance()->get_default_values();
73
+ $network_admin_values = WPEL_Network_Admin_Fields::get_instance()->get_default_values();
74
+
75
+ update_site_option( 'wpel-network-settings', $network_values );
76
+ update_site_option( 'wpel-network-admin-settings', $network_admin_values );
77
+ }
78
+
79
+ /**
80
+ * Activate site
81
+ * @return void
82
+ */
83
+ private function activate_site()
84
+ {
85
+ $site_already_set = get_option( 'wpel-external-link-settings' );
86
+
87
+ if ( $site_already_set ) {
88
+ return;
89
+ }
90
+
91
+ // get default values
92
+ $external_link_values = WPEL_External_Link_Fields::get_instance()->get_default_values();
93
+ $internal_link_values = WPEL_Internal_Link_Fields::get_instance()->get_default_values();
94
+ $exceptions_link_values = WPEL_Exceptions_Fields::get_instance()->get_default_values();
95
+ $admin_link_values = WPEL_Admin_Fields::get_instance()->get_default_values();
96
+
97
+ // check for old option values version < 2.0.0
98
+ $old_main = get_option( 'wp_external_links-main' );
99
+
100
+ // convert old to new db option values
101
+ if ( ! empty( $old_main ) ) {
102
+ // get other old values
103
+ $old_meta = get_option( 'wp_external_links-meta' );
104
+ $old_seo = get_option( 'wp_external_links-seo' );
105
+ $old_style = get_option( 'wp_external_links-style' );
106
+ $old_extra = get_option( 'wp_external_links-extra' );
107
+ $old_screen = get_option( 'wp_external_links-screen' );
108
+
109
+ // helper function
110
+ $val = function ( $arr, $key, $default = '' ) {
111
+ if ( ! isset( $arr[ $key ] ) ) {
112
+ return $default;
113
+ }
114
+
115
+ return (string) $arr[ $key ];
116
+ };
117
+
118
+ // mapping
119
+ if ( ! empty( $old_main ) ) {
120
+ $target = $val( $old_main, 'target' );
121
+ $external_link_values[ 'target' ] = str_replace( '_none', '_self', $target );
122
+
123
+ $exceptions_link_values[ 'apply_all' ] = $val( $old_main, 'filter_page' );
124
+ $exceptions_link_values[ 'apply_post_content' ] = $val( $old_main, 'filter_posts' );
125
+ $exceptions_link_values[ 'apply_comments' ] = $val( $old_main, 'filter_comments' );
126
+ $exceptions_link_values[ 'apply_widgets' ] = $val( $old_main, 'filter_widgets' );
127
+ $exceptions_link_values[ 'exclude_urls' ] = $val( $old_main, 'ignore' );
128
+ $exceptions_link_values[ 'subdomains_as_internal_links' ] = $val( $old_main, 'ignore_subdomains' );
129
+ }
130
+ if ( ! empty( $old_seo ) ) {
131
+ $external_link_values[ 'rel_follow' ] = ( '1' == $val( $old_seo, 'nofollow' ) ) ? 'nofollow' : 'follow';
132
+ $external_link_values[ 'rel_follow_overwrite' ] = $val( $old_seo, 'overwrite_follow' );
133
+ $external_link_values[ 'rel_external' ] = $val( $old_seo, 'external' );
134
+
135
+ $title = $val( $old_seo, 'title' );
136
+ $external_link_values[ 'title' ] = str_replace( '%title%', '{title}', $title );
137
+ }
138
+ if ( ! empty( $old_style ) ) {
139
+ if ( $old_style[ 'icon' ] ) {
140
+ $external_link_values[ 'icon_type' ] = 'image';
141
+ $external_link_values[ 'icon_image' ] = $val( $old_style, 'icon', '1' );
142
+ }
143
+ $external_link_values[ 'class' ] = $val( $old_style, 'class_name' );
144
+ $external_link_values[ 'no_icon_for_img' ] = $val( $old_style, 'image_no_icon' );
145
+ }
146
+ if ( ! empty( $old_extra ) ) {
147
+ // nothing
148
+ }
149
+ if ( ! empty( $old_screen ) ) {
150
+ $admin_link_values[ 'own_admin_menu' ] = ( 'admin.php' == $val( $old_screen, 'menu_position' ) ) ? '1' : '';
151
+ }
152
+
153
+ // delete old values
154
+ delete_option( 'wp_external_links-meta' );
155
+ delete_option( 'wp_external_links-main' );
156
+ delete_option( 'wp_external_links-seo' );
157
+ delete_option( 'wp_external_links-style' );
158
+ delete_option( 'wp_external_links-extra' );
159
+ delete_option( 'wp_external_links-screen' );
160
+ }
161
+
162
+ // update new values
163
+ update_option( 'wpel-external-link-settings', $external_link_values );
164
+ update_option( 'wpel-internal-link-settings', $internal_link_values );
165
+ update_option( 'wpel-exceptions-settings', $exceptions_link_values );
166
+ update_option( 'wpel-admin-settings', $admin_link_values );
167
+
168
+ // update meta data
169
+ $plugin_data = get_plugin_data( WPEL_Plugin::get_plugin_file() );
170
+ update_option( 'wpel-version', $plugin_data[ 'Version' ] );
171
+ update_option( 'wpel-show-notice', true );
172
+ }
173
+
174
+ /**
175
+ * Uninstall site
176
+ */
177
+ protected function uninstall()
178
+ {
179
+ global $wpdb;
180
+
181
+ if ( is_multisite() ) {
182
+ // network activation
183
+ $sites = wp_get_sites();
184
+ $active_blog = $wpdb->blogid;
185
+ foreach ( $sites as $site ) {
186
+ switch_to_blog( $site[ 'blog_id' ] );
187
+ $this->uninstall_site();
188
+ }
189
+
190
+ // switch back to active blog
191
+ switch_to_blog( $active_blog );
192
+
193
+ // network settings
194
+ delete_site_option( 'wpel-network-settings' );
195
+ delete_site_option( 'wpel-network-admin-settings' );
196
+ } else {
197
+ // single site activation
198
+ $this->activate_site();
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Plugin uninstall procedure
204
+ */
205
+ protected function uninstall_site()
206
+ {
207
+ // delete options
208
+ delete_option( 'wpel-external-link-settings' );
209
+ delete_option( 'wpel-internal-link-settings' );
210
+ delete_option( 'wpel-exceptions-settings' );
211
+ delete_option( 'wpel-admin-settings' );
212
+
213
+ delete_option( 'wpel-version' );
214
+ delete_option( 'wpel-show-notice' );
215
+ }
216
+
217
+ }
218
+
219
+ /*?>*/
includes/class-wpel-template-tags.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Template_Tags
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ final class WPEL_Template_Tags extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * Create template tag(s)
18
+ */
19
+ protected function init()
20
+ {
21
+ $this->create_templatetag();
22
+ }
23
+
24
+ /**
25
+ * Create template tag
26
+ * @return void
27
+ */
28
+ protected function create_templatetag()
29
+ {
30
+ if ( function_exists( 'wpel_filter' ) ) {
31
+ return;
32
+ }
33
+
34
+ /**
35
+ * Template tag to apply plugin settings on given content
36
+ * @return string
37
+ */
38
+ function wpel_filter( $content ) {
39
+ // hidden dependency to WPEL_Front::scan()
40
+ return WPEL_Front::get_instance()->scan( $content );
41
+ }
42
+ }
43
+
44
+ }
45
+
46
+ /*?>*/
includes/class-wpel-textdomain.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPEL_Textdomain
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ class WPEL_Textdomain extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * Action for "plugins_loaded"
18
+ */
19
+ protected function action_plugins_loaded()
20
+ {
21
+ load_plugin_textdomain( 'wpel', false, WPEL_Plugin::get_plugin_dir( '/languages/' ) );
22
+ }
23
+
24
+ }
25
+
26
+ /*?>*/
includes/phpQuery.php DELETED
@@ -1,5702 +0,0 @@
1
- <?php
2
- /**
3
- * phpQuery is a server-side, chainable, CSS3 selector driven
4
- * Document Object Model (DOM) API based on jQuery JavaScript Library.
5
- *
6
- * @version 0.9.5
7
- * @link http://code.google.com/p/phpquery/
8
- * @link http://phpquery-library.blogspot.com/
9
- * @link http://jquery.com/
10
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
11
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
12
- * @package phpQuery
13
- */
14
-
15
- // class names for instanceof
16
- // TODO move them as class constants into phpQuery
17
- define('DOMDOCUMENT', 'DOMDocument');
18
- define('DOMELEMENT', 'DOMElement');
19
- define('DOMNODELIST', 'DOMNodeList');
20
- define('DOMNODE', 'DOMNode');
21
-
22
- /**
23
- * DOMEvent class.
24
- *
25
- * Based on
26
- * @link http://developer.mozilla.org/En/DOM:event
27
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
28
- * @package phpQuery
29
- * @todo implement ArrayAccess ?
30
- */
31
- class DOMEvent {
32
- /**
33
- * Returns a boolean indicating whether the event bubbles up through the DOM or not.
34
- *
35
- * @var unknown_type
36
- */
37
- public $bubbles = true;
38
- /**
39
- * Returns a boolean indicating whether the event is cancelable.
40
- *
41
- * @var unknown_type
42
- */
43
- public $cancelable = true;
44
- /**
45
- * Returns a reference to the currently registered target for the event.
46
- *
47
- * @var unknown_type
48
- */
49
- public $currentTarget;
50
- /**
51
- * Returns detail about the event, depending on the type of event.
52
- *
53
- * @var unknown_type
54
- * @link http://developer.mozilla.org/en/DOM/event.detail
55
- */
56
- public $detail; // ???
57
- /**
58
- * Used to indicate which phase of the event flow is currently being evaluated.
59
- *
60
- * NOT IMPLEMENTED
61
- *
62
- * @var unknown_type
63
- * @link http://developer.mozilla.org/en/DOM/event.eventPhase
64
- */
65
- public $eventPhase; // ???
66
- /**
67
- * The explicit original target of the event (Mozilla-specific).
68
- *
69
- * NOT IMPLEMENTED
70
- *
71
- * @var unknown_type
72
- */
73
- public $explicitOriginalTarget; // moz only
74
- /**
75
- * The original target of the event, before any retargetings (Mozilla-specific).
76
- *
77
- * NOT IMPLEMENTED
78
- *
79
- * @var unknown_type
80
- */
81
- public $originalTarget; // moz only
82
- /**
83
- * Identifies a secondary target for the event.
84
- *
85
- * @var unknown_type
86
- */
87
- public $relatedTarget;
88
- /**
89
- * Returns a reference to the target to which the event was originally dispatched.
90
- *
91
- * @var unknown_type
92
- */
93
- public $target;
94
- /**
95
- * Returns the time that the event was created.
96
- *
97
- * @var unknown_type
98
- */
99
- public $timeStamp;
100
- /**
101
- * Returns the name of the event (case-insensitive).
102
- */
103
- public $type;
104
- public $runDefault = true;
105
- public $data = null;
106
- public function __construct($data) {
107
- foreach($data as $k => $v) {
108
- $this->$k = $v;
109
- }
110
- if (! $this->timeStamp)
111
- $this->timeStamp = time();
112
- }
113
- /**
114
- * Cancels the event (if it is cancelable).
115
- *
116
- */
117
- public function preventDefault() {
118
- $this->runDefault = false;
119
- }
120
- /**
121
- * Stops the propagation of events further along in the DOM.
122
- *
123
- */
124
- public function stopPropagation() {
125
- $this->bubbles = false;
126
- }
127
- }
128
-
129
-
130
- /**
131
- * DOMDocumentWrapper class simplifies work with DOMDocument.
132
- *
133
- * Know bug:
134
- * - in XHTML fragments, <br /> changes to <br clear="none" />
135
- *
136
- * @todo check XML catalogs compatibility
137
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
138
- * @package phpQuery
139
- */
140
- class DOMDocumentWrapper {
141
- /**
142
- * @var DOMDocument
143
- */
144
- public $document;
145
- public $id;
146
- /**
147
- * @todo Rewrite as method and quess if null.
148
- * @var unknown_type
149
- */
150
- public $contentType = '';
151
- public $xpath;
152
- public $uuid = 0;
153
- public $data = array();
154
- public $dataNodes = array();
155
- public $events = array();
156
- public $eventsNodes = array();
157
- public $eventsGlobal = array();
158
- /**
159
- * @TODO iframes support http://code.google.com/p/phpquery/issues/detail?id=28
160
- * @var unknown_type
161
- */
162
- public $frames = array();
163
- /**
164
- * Document root, by default equals to document itself.
165
- * Used by documentFragments.
166
- *
167
- * @var DOMNode
168
- */
169
- public $root;
170
- public $isDocumentFragment;
171
- public $isXML = false;
172
- public $isXHTML = false;
173
- public $isHTML = false;
174
- public $charset;
175
- public function __construct($markup = null, $contentType = null, $newDocumentID = null) {
176
- if (isset($markup))
177
- $this->load($markup, $contentType, $newDocumentID);
178
- $this->id = $newDocumentID
179
- ? $newDocumentID
180
- : md5(microtime());
181
- }
182
- public function load($markup, $contentType = null, $newDocumentID = null) {
183
- // phpQuery::$documents[$id] = $this;
184
- $this->contentType = strtolower($contentType);
185
- if ($markup instanceof DOMDOCUMENT) {
186
- $this->document = $markup;
187
- $this->root = $this->document;
188
- $this->charset = $this->document->encoding;
189
- // TODO isDocumentFragment
190
- } else {
191
- $loaded = $this->loadMarkup($markup);
192
- }
193
- if ($loaded) {
194
- // $this->document->formatOutput = true;
195
- $this->document->preserveWhiteSpace = true;
196
- $this->xpath = new DOMXPath($this->document);
197
- $this->afterMarkupLoad();
198
- return true;
199
- // remember last loaded document
200
- // return phpQuery::selectDocument($id);
201
- }
202
- return false;
203
- }
204
- protected function afterMarkupLoad() {
205
- if ($this->isXHTML) {
206
- $this->xpath->registerNamespace("html", "http://www.w3.org/1999/xhtml");
207
- }
208
- }
209
- protected function loadMarkup($markup) {
210
- $loaded = false;
211
- if ($this->contentType) {
212
- self::debug("Load markup for content type {$this->contentType}");
213
- // content determined by contentType
214
- list($contentType, $charset) = $this->contentTypeToArray($this->contentType);
215
- switch($contentType) {
216
- case 'text/html':
217
- phpQuery::debug("Loading HTML, content type '{$this->contentType}'");
218
- $loaded = $this->loadMarkupHTML($markup, $charset);
219
- break;
220
- case 'text/xml':
221
- case 'application/xhtml+xml':
222
- phpQuery::debug("Loading XML, content type '{$this->contentType}'");
223
- $loaded = $this->loadMarkupXML($markup, $charset);
224
- break;
225
- default:
226
- // for feeds or anything that sometimes doesn't use text/xml
227
- if (strpos('xml', $this->contentType) !== false) {
228
- phpQuery::debug("Loading XML, content type '{$this->contentType}'");
229
- $loaded = $this->loadMarkupXML($markup, $charset);
230
- } else
231
- phpQuery::debug("Could not determine document type from content type '{$this->contentType}'");
232
- }
233
- } else {
234
- // content type autodetection
235
- if ($this->isXML($markup)) {
236
- phpQuery::debug("Loading XML, isXML() == true");
237
- $loaded = $this->loadMarkupXML($markup);
238
- if (! $loaded && $this->isXHTML) {
239
- phpQuery::debug('Loading as XML failed, trying to load as HTML, isXHTML == true');
240
- $loaded = $this->loadMarkupHTML($markup);
241
- }
242
- } else {
243
- phpQuery::debug("Loading HTML, isXML() == false");
244
- $loaded = $this->loadMarkupHTML($markup);
245
- }
246
- }
247
- return $loaded;
248
- }
249
- protected function loadMarkupReset() {
250
- $this->isXML = $this->isXHTML = $this->isHTML = false;
251
- }
252
- protected function documentCreate($charset, $version = '1.0') {
253
- if (! $version)
254
- $version = '1.0';
255
- $this->document = new DOMDocument($version, $charset);
256
- $this->charset = $this->document->encoding;
257
- // $this->document->encoding = $charset;
258
- $this->document->formatOutput = true;
259
- $this->document->preserveWhiteSpace = true;
260
- }
261
- protected function loadMarkupHTML($markup, $requestedCharset = null) {
262
- if (phpQuery::$debug)
263
- phpQuery::debug('Full markup load (HTML): '.substr($markup, 0, 250));
264
- $this->loadMarkupReset();
265
- $this->isHTML = true;
266
- if (!isset($this->isDocumentFragment))
267
- $this->isDocumentFragment = self::isDocumentFragmentHTML($markup);
268
- $charset = null;
269
- $documentCharset = $this->charsetFromHTML($markup);
270
- $addDocumentCharset = false;
271
- if ($documentCharset) {
272
- $charset = $documentCharset;
273
- $markup = $this->charsetFixHTML($markup);
274
- } else if ($requestedCharset) {
275
- $charset = $requestedCharset;
276
- }
277
- if (! $charset)
278
- $charset = phpQuery::$defaultCharset;
279
- // HTTP 1.1 says that the default charset is ISO-8859-1
280
- // @see http://www.w3.org/International/O-HTTP-charset
281
- if (! $documentCharset) {
282
- $documentCharset = 'ISO-8859-1';
283
- $addDocumentCharset = true;
284
- }
285
- // Should be careful here, still need 'magic encoding detection' since lots of pages have other 'default encoding'
286
- // Worse, some pages can have mixed encodings... we'll try not to worry about that
287
- $requestedCharset = strtoupper($requestedCharset);
288
- $documentCharset = strtoupper($documentCharset);
289
- phpQuery::debug("DOC: $documentCharset REQ: $requestedCharset");
290
- if ($requestedCharset && $documentCharset && $requestedCharset !== $documentCharset) {
291
- phpQuery::debug("CHARSET CONVERT");
292
- // Document Encoding Conversion
293
- // http://code.google.com/p/phpquery/issues/detail?id=86
294
- if (function_exists('mb_detect_encoding')) {
295
- $possibleCharsets = array($documentCharset, $requestedCharset, 'AUTO');
296
- $docEncoding = mb_detect_encoding($markup, implode(', ', $possibleCharsets));
297
- if (! $docEncoding)
298
- $docEncoding = $documentCharset; // ok trust the document
299
- phpQuery::debug("DETECTED '$docEncoding'");
300
- // Detected does not match what document says...
301
- if ($docEncoding !== $documentCharset) {
302
- // Tricky..
303
- }
304
- if ($docEncoding !== $requestedCharset) {
305
- phpQuery::debug("CONVERT $docEncoding => $requestedCharset");
306
- $markup = mb_convert_encoding($markup, $requestedCharset, $docEncoding);
307
- $markup = $this->charsetAppendToHTML($markup, $requestedCharset);
308
- $charset = $requestedCharset;
309
- }
310
- } else {
311
- phpQuery::debug("TODO: charset conversion without mbstring...");
312
- }
313
- }
314
- $return = false;
315
- if ($this->isDocumentFragment) {
316
- phpQuery::debug("Full markup load (HTML), DocumentFragment detected, using charset '$charset'");
317
- $return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
318
- } else {
319
- if ($addDocumentCharset) {
320
- phpQuery::debug("Full markup load (HTML), appending charset: '$charset'");
321
- $markup = $this->charsetAppendToHTML($markup, $charset);
322
- }
323
- phpQuery::debug("Full markup load (HTML), documentCreate('$charset')");
324
- $this->documentCreate($charset);
325
- $return = phpQuery::$debug === 2
326
- ? $this->document->loadHTML($markup)
327
- : @$this->document->loadHTML($markup);
328
- if ($return)
329
- $this->root = $this->document;
330
- }
331
- if ($return && ! $this->contentType)
332
- $this->contentType = 'text/html';
333
- return $return;
334
- }
335
- protected function loadMarkupXML($markup, $requestedCharset = null) {
336
- if (phpQuery::$debug)
337
- phpQuery::debug('Full markup load (XML): '.substr($markup, 0, 250));
338
- $this->loadMarkupReset();
339
- $this->isXML = true;
340
- // check agains XHTML in contentType or markup
341
- $isContentTypeXHTML = $this->isXHTML();
342
- $isMarkupXHTML = $this->isXHTML($markup);
343
- if ($isContentTypeXHTML || $isMarkupXHTML) {
344
- self::debug('Full markup load (XML), XHTML detected');
345
- $this->isXHTML = true;
346
- }
347
- // determine document fragment
348
- if (! isset($this->isDocumentFragment))
349
- $this->isDocumentFragment = $this->isXHTML
350
- ? self::isDocumentFragmentXHTML($markup)
351
- : self::isDocumentFragmentXML($markup);
352
- // this charset will be used
353
- $charset = null;
354
- // charset from XML declaration @var string
355
- $documentCharset = $this->charsetFromXML($markup);
356
- if (! $documentCharset) {
357
- if ($this->isXHTML) {
358
- // this is XHTML, try to get charset from content-type meta header
359
- $documentCharset = $this->charsetFromHTML($markup);
360
- if ($documentCharset) {
361
- phpQuery::debug("Full markup load (XML), appending XHTML charset '$documentCharset'");
362
- $this->charsetAppendToXML($markup, $documentCharset);
363
- $charset = $documentCharset;
364
- }
365
- }
366
- if (! $documentCharset) {
367
- // if still no document charset...
368
- $charset = $requestedCharset;
369
- }
370
- } else if ($requestedCharset) {
371
- $charset = $requestedCharset;
372
- }
373
- if (! $charset) {
374
- $charset = phpQuery::$defaultCharset;
375
- }
376
- if ($requestedCharset && $documentCharset && $requestedCharset != $documentCharset) {
377
- // TODO place for charset conversion
378
- // $charset = $requestedCharset;
379
- }
380
- $return = false;
381
- if ($this->isDocumentFragment) {
382
- phpQuery::debug("Full markup load (XML), DocumentFragment detected, using charset '$charset'");
383
- $return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
384
- } else {
385
- // FIXME ???
386
- if ($isContentTypeXHTML && ! $isMarkupXHTML)
387
- if (! $documentCharset) {
388
- phpQuery::debug("Full markup load (XML), appending charset '$charset'");
389
- $markup = $this->charsetAppendToXML($markup, $charset);
390
- }
391
- // see http://pl2.php.net/manual/en/book.dom.php#78929
392
- // LIBXML_DTDLOAD (>= PHP 5.1)
393
- // does XML ctalogues works with LIBXML_NONET
394
- // $this->document->resolveExternals = true;
395
- // TODO test LIBXML_COMPACT for performance improvement
396
- // create document
397
- $this->documentCreate($charset);
398
- if (phpversion() < 5.1) {
399
- $this->document->resolveExternals = true;
400
- $return = phpQuery::$debug === 2
401
- ? $this->document->loadXML($markup)
402
- : @$this->document->loadXML($markup);
403
- } else {
404
- /** @link http://pl2.php.net/manual/en/libxml.constants.php */
405
- $libxmlStatic = phpQuery::$debug === 2
406
- ? LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET
407
- : LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOERROR;
408
- $return = $this->document->loadXML($markup, $libxmlStatic);
409
- // if (! $return)
410
- // $return = $this->document->loadHTML($markup);
411
- }
412
- if ($return)
413
- $this->root = $this->document;
414
- }
415
- if ($return) {
416
- if (! $this->contentType) {
417
- if ($this->isXHTML)
418
- $this->contentType = 'application/xhtml+xml';
419
- else
420
- $this->contentType = 'text/xml';
421
- }
422
- return $return;
423
- } else {
424
- throw new Exception("Error loading XML markup");
425
- }
426
- }
427
- protected function isXHTML($markup = null) {
428
- if (! isset($markup)) {
429
- return strpos($this->contentType, 'xhtml') !== false;
430
- }
431
- // XXX ok ?
432
- return strpos($markup, "<!DOCTYPE html") !== false;
433
- // return stripos($doctype, 'xhtml') !== false;
434
- // $doctype = isset($dom->doctype) && is_object($dom->doctype)
435
- // ? $dom->doctype->publicId
436
- // : self::$defaultDoctype;
437
- }
438
- protected function isXML($markup) {
439
- // return strpos($markup, '<?xml') !== false && stripos($markup, 'xhtml') === false;
440
- return strpos(substr($markup, 0, 100), '<'.'?xml') !== false;
441
- }
442
- protected function contentTypeToArray($contentType) {
443
- $matches = explode(';', trim(strtolower($contentType)));
444
- if (isset($matches[1])) {
445
- $matches[1] = explode('=', $matches[1]);
446
- // strip 'charset='
447
- $matches[1] = isset($matches[1][1]) && trim($matches[1][1])
448
- ? $matches[1][1]
449
- : $matches[1][0];
450
- } else
451
- $matches[1] = null;
452
- return $matches;
453
- }
454
- /**
455
- *
456
- * @param $markup
457
- * @return array contentType, charset
458
- */
459
- protected function contentTypeFromHTML($markup) {
460
- $matches = array();
461
- // find meta tag
462
- preg_match('@<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
463
- $markup, $matches
464
- );
465
- if (! isset($matches[0]))
466
- return array(null, null);
467
- // get attr 'content'
468
- preg_match('@content\\s*=\\s*(["|\'])(.+?)\\1@', $matches[0], $matches);
469
- if (! isset($matches[0]))
470
- return array(null, null);
471
- return $this->contentTypeToArray($matches[2]);
472
- }
473
- protected function charsetFromHTML($markup) {
474
- $contentType = $this->contentTypeFromHTML($markup);
475
- return $contentType[1];
476
- }
477
- protected function charsetFromXML($markup) {
478
- $matches;
479
- // find declaration
480
- preg_match('@<'.'?xml[^>]+encoding\\s*=\\s*(["|\'])(.*?)\\1@i',
481
- $markup, $matches
482
- );
483
- return isset($matches[2])
484
- ? strtolower($matches[2])
485
- : null;
486
- }
487
- /**
488
- * Repositions meta[type=charset] at the start of head. Bypasses DOMDocument bug.
489
- *
490
- * @link http://code.google.com/p/phpquery/issues/detail?id=80
491
- * @param $html
492
- */
493
- protected function charsetFixHTML($markup) {
494
- $matches = array();
495
- // find meta tag
496
- preg_match('@\s*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
497
- $markup, $matches, PREG_OFFSET_CAPTURE
498
- );
499
- if (! isset($matches[0]))
500
- return;
501
- $metaContentType = $matches[0][0];
502
- $markup = substr($markup, 0, $matches[0][1])
503
- .substr($markup, $matches[0][1]+strlen($metaContentType));
504
- $headStart = stripos($markup, '<head>');
505
- $markup = substr($markup, 0, $headStart+6).$metaContentType
506
- .substr($markup, $headStart+6);
507
- return $markup;
508
- }
509
- protected function charsetAppendToHTML($html, $charset, $xhtml = false) {
510
- // remove existing meta[type=content-type]
511
- $html = preg_replace('@\s*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i', '', $html);
512
- $meta = '<meta http-equiv="Content-Type" content="text/html;charset='
513
- .$charset.'" '
514
- .($xhtml ? '/' : '')
515
- .'>';
516
- if (strpos($html, '<head') === false) {
517
- if (strpos($hltml, '<html') === false) {
518
- return $meta.$html;
519
- } else {
520
- return preg_replace(
521
- '@<html(.*?)(?(?<!\?)>)@s',
522
- "<html\\1><head>{$meta}</head>",
523
- $html
524
- );
525
- }
526
- } else {
527
- return preg_replace(
528
- '@<head(.*?)(?(?<!\?)>)@s',
529
- '<head\\1>'.$meta,
530
- $html
531
- );
532
- }
533
- }
534
- protected function charsetAppendToXML($markup, $charset) {
535
- $declaration = '<'.'?xml version="1.0" encoding="'.$charset.'"?'.'>';
536
- return $declaration.$markup;
537
- }
538
- public static function isDocumentFragmentHTML($markup) {
539
- return stripos($markup, '<html') === false && stripos($markup, '<!doctype') === false;
540
- }
541
- public static function isDocumentFragmentXML($markup) {
542
- return stripos($markup, '<'.'?xml') === false;
543
- }
544
- public static function isDocumentFragmentXHTML($markup) {
545
- return self::isDocumentFragmentHTML($markup);
546
- }
547
- public function importAttr($value) {
548
- // TODO
549
- }
550
- /**
551
- *
552
- * @param $source
553
- * @param $target
554
- * @param $sourceCharset
555
- * @return array Array of imported nodes.
556
- */
557
- public function import($source, $sourceCharset = null) {
558
- // TODO charset conversions
559
- $return = array();
560
- if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
561
- $source = array($source);
562
- // if (is_array($source)) {
563
- // foreach($source as $node) {
564
- // if (is_string($node)) {
565
- // // string markup
566
- // $fake = $this->documentFragmentCreate($node, $sourceCharset);
567
- // if ($fake === false)
568
- // throw new Exception("Error loading documentFragment markup");
569
- // else
570
- // $return = array_merge($return,
571
- // $this->import($fake->root->childNodes)
572
- // );
573
- // } else {
574
- // $return[] = $this->document->importNode($node, true);
575
- // }
576
- // }
577
- // return $return;
578
- // } else {
579
- // // string markup
580
- // $fake = $this->documentFragmentCreate($source, $sourceCharset);
581
- // if ($fake === false)
582
- // throw new Exception("Error loading documentFragment markup");
583
- // else
584
- // return $this->import($fake->root->childNodes);
585
- // }
586
- if (is_array($source) || $source instanceof DOMNODELIST) {
587
- // dom nodes
588
- self::debug('Importing nodes to document');
589
- foreach($source as $node)
590
- $return[] = $this->document->importNode($node, true);
591
- } else {
592
- // string markup
593
- $fake = $this->documentFragmentCreate($source, $sourceCharset);
594
- if ($fake === false)
595
- throw new Exception("Error loading documentFragment markup");
596
- else
597
- return $this->import($fake->root->childNodes);
598
- }
599
- return $return;
600
- }
601
- /**
602
- * Creates new document fragment.
603
- *
604
- * @param $source
605
- * @return DOMDocumentWrapper
606
- */
607
- protected function documentFragmentCreate($source, $charset = null) {
608
- $fake = new DOMDocumentWrapper();
609
- $fake->contentType = $this->contentType;
610
- $fake->isXML = $this->isXML;
611
- $fake->isHTML = $this->isHTML;
612
- $fake->isXHTML = $this->isXHTML;
613
- $fake->root = $fake->document;
614
- if (! $charset)
615
- $charset = $this->charset;
616
- // $fake->documentCreate($this->charset);
617
- if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
618
- $source = array($source);
619
- if (is_array($source) || $source instanceof DOMNODELIST) {
620
- // dom nodes
621
- // load fake document
622
- if (! $this->documentFragmentLoadMarkup($fake, $charset))
623
- return false;
624
- $nodes = $fake->import($source);
625
- foreach($nodes as $node)
626
- $fake->root->appendChild($node);
627
- } else {
628
- // string markup
629
- $this->documentFragmentLoadMarkup($fake, $charset, $source);
630
- }
631
- return $fake;
632
- }
633
- /**
634
- *
635
- * @param $document DOMDocumentWrapper
636
- * @param $markup
637
- * @return $document
638
- */
639
- private function documentFragmentLoadMarkup($fragment, $charset, $markup = null) {
640
- // TODO error handling
641
- // TODO copy doctype
642
- // tempolary turn off
643
- $fragment->isDocumentFragment = false;
644
- if ($fragment->isXML) {
645
- if ($fragment->isXHTML) {
646
- // add FAKE element to set default namespace
647
- $fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?>'
648
- .'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
649
- .'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
650
- .'<fake xmlns="http://www.w3.org/1999/xhtml">'.$markup.'</fake>');
651
- $fragment->root = $fragment->document->firstChild->nextSibling;
652
- } else {
653
- $fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?><fake>'.$markup.'</fake>');
654
- $fragment->root = $fragment->document->firstChild;
655
- }
656
- } else {
657
- $markup2 = phpQuery::$defaultDoctype.'<html><head><meta http-equiv="Content-Type" content="text/html;charset='
658
- .$charset.'"></head>';
659
- $noBody = strpos($markup, '<body') === false;
660
- if ($noBody)
661
- $markup2 .= '<body>';
662
- $markup2 .= $markup;
663
- if ($noBody)
664
- $markup2 .= '</body>';
665
- $markup2 .= '</html>';
666
- $fragment->loadMarkupHTML($markup2);
667
- // TODO resolv body tag merging issue
668
- $fragment->root = $noBody
669
- ? $fragment->document->firstChild->nextSibling->firstChild->nextSibling
670
- : $fragment->document->firstChild->nextSibling->firstChild->nextSibling;
671
- }
672
- if (! $fragment->root)
673
- return false;
674
- $fragment->isDocumentFragment = true;
675
- return true;
676
- }
677
- protected function documentFragmentToMarkup($fragment) {
678
- phpQuery::debug('documentFragmentToMarkup');
679
- $tmp = $fragment->isDocumentFragment;
680
- $fragment->isDocumentFragment = false;
681
- $markup = $fragment->markup();
682
- if ($fragment->isXML) {
683
- $markup = substr($markup, 0, strrpos($markup, '</fake>'));
684
- if ($fragment->isXHTML) {
685
- $markup = substr($markup, strpos($markup, '<fake')+43);
686
- } else {
687
- $markup = substr($markup, strpos($markup, '<fake>')+6);
688
- }
689
- } else {
690
- $markup = substr($markup, strpos($markup, '<body>')+6);
691
- $markup = substr($markup, 0, strrpos($markup, '</body>'));
692
- }
693
- $fragment->isDocumentFragment = $tmp;
694
- if (phpQuery::$debug)
695
- phpQuery::debug('documentFragmentToMarkup: '.substr($markup, 0, 150));
696
- return $markup;
697
- }
698
- /**
699
- * Return document markup, starting with optional $nodes as root.
700
- *
701
- * @param $nodes DOMNode|DOMNodeList
702
- * @return string
703
- */
704
- public function markup($nodes = null, $innerMarkup = false) {
705
- if (isset($nodes) && count($nodes) == 1 && $nodes[0] instanceof DOMDOCUMENT)
706
- $nodes = null;
707
- if (isset($nodes)) {
708
- $markup = '';
709
- if (!is_array($nodes) && !($nodes instanceof DOMNODELIST) )
710
- $nodes = array($nodes);
711
- if ($this->isDocumentFragment && ! $innerMarkup)
712
- foreach($nodes as $i => $node)
713
- if ($node->isSameNode($this->root)) {
714
- // var_dump($node);
715
- $nodes = array_slice($nodes, 0, $i)
716
- + phpQuery::DOMNodeListToArray($node->childNodes)
717
- + array_slice($nodes, $i+1);
718
- }
719
- if ($this->isXML && ! $innerMarkup) {
720
- self::debug("Getting outerXML with charset '{$this->charset}'");
721
- // we need outerXML, so we can benefit from
722
- // $node param support in saveXML()
723
- foreach($nodes as $node)
724
- $markup .= $this->document->saveXML($node);
725
- } else {
726
- $loop = array();
727
- if ($innerMarkup)
728
- foreach($nodes as $node) {
729
- if ($node->childNodes)
730
- foreach($node->childNodes as $child)
731
- $loop[] = $child;
732
- else
733
- $loop[] = $node;
734
- }
735
- else
736
- $loop = $nodes;
737
- self::debug("Getting markup, moving selected nodes (".count($loop).") to new DocumentFragment");
738
- $fake = $this->documentFragmentCreate($loop);
739
- $markup = $this->documentFragmentToMarkup($fake);
740
- }
741
- if ($this->isXHTML) {
742
- self::debug("Fixing XHTML");
743
- $markup = self::markupFixXHTML($markup);
744
- }
745
- self::debug("Markup: ".substr($markup, 0, 250));
746
- return $markup;
747
- } else {
748
- if ($this->isDocumentFragment) {
749
- // documentFragment, html only...
750
- self::debug("Getting markup, DocumentFragment detected");
751
- // return $this->markup(
752
- //// $this->document->getElementsByTagName('body')->item(0)
753
- // $this->document->root, true
754
- // );
755
- $markup = $this->documentFragmentToMarkup($this);
756
- // no need for markupFixXHTML, as it's done thought markup($nodes) method
757
- return $markup;
758
- } else {
759
- self::debug("Getting markup (".($this->isXML?'XML':'HTML')."), final with charset '{$this->charset}'");
760
- $markup = $this->isXML
761
- ? $this->document->saveXML()
762
- : $this->document->saveHTML();
763
- if ($this->isXHTML) {
764
- self::debug("Fixing XHTML");
765
- $markup = self::markupFixXHTML($markup);
766
- }
767
- self::debug("Markup: ".substr($markup, 0, 250));
768
- return $markup;
769
- }
770
- }
771
- }
772
- protected static function markupFixXHTML($markup) {
773
- $markup = self::expandEmptyTag('script', $markup);
774
- $markup = self::expandEmptyTag('select', $markup);
775
- $markup = self::expandEmptyTag('textarea', $markup);
776
- return $markup;
777
- }
778
- public static function debug($text) {
779
- phpQuery::debug($text);
780
- }
781
- /**
782
- * expandEmptyTag
783
- *
784
- * @param $tag
785
- * @param $xml
786
- * @return unknown_type
787
- * @author mjaque at ilkebenson dot com
788
- * @link http://php.net/manual/en/domdocument.savehtml.php#81256
789
- */
790
- public static function expandEmptyTag($tag, $xml){
791
- $indice = 0;
792
- while ($indice< strlen($xml)){
793
- $pos = strpos($xml, "<$tag ", $indice);
794
- if ($pos){
795
- $posCierre = strpos($xml, ">", $pos);
796
- if ($xml[$posCierre-1] == "/"){
797
- $xml = substr_replace($xml, "></$tag>", $posCierre-1, 2);
798
- }
799
- $indice = $posCierre;
800
- }
801
- else break;
802
- }
803
- return $xml;
804
- }
805
- }
806
-
807
- /**
808
- * Event handling class.
809
- *
810
- * @author Tobiasz Cudnik
811
- * @package phpQuery
812
- * @static
813
- */
814
- abstract class phpQueryEvents {
815
- /**
816
- * Trigger a type of event on every matched element.
817
- *
818
- * @param DOMNode|phpQueryObject|string $document
819
- * @param unknown_type $type
820
- * @param unknown_type $data
821
- *
822
- * @TODO exclusive events (with !)
823
- * @TODO global events (test)
824
- * @TODO support more than event in $type (space-separated)
825
- */
826
- public static function trigger($document, $type, $data = array(), $node = null) {
827
- // trigger: function(type, data, elem, donative, extra) {
828
- $documentID = phpQuery::getDocumentID($document);
829
- $namespace = null;
830
- if (strpos($type, '.') !== false)
831
- list($name, $namespace) = explode('.', $type);
832
- else
833
- $name = $type;
834
- if (! $node) {
835
- if (self::issetGlobal($documentID, $type)) {
836
- $pq = phpQuery::getDocument($documentID);
837
- // TODO check add($pq->document)
838
- $pq->find('*')->add($pq->document)
839
- ->trigger($type, $data);
840
- }
841
- } else {
842
- if (isset($data[0]) && $data[0] instanceof DOMEvent) {
843
- $event = $data[0];
844
- $event->relatedTarget = $event->target;
845
- $event->target = $node;
846
- $data = array_slice($data, 1);
847
- } else {
848
- $event = new DOMEvent(array(
849
- 'type' => $type,
850
- 'target' => $node,
851
- 'timeStamp' => time(),
852
- ));
853
- }
854
- $i = 0;
855
- while($node) {
856
- // TODO whois
857
- phpQuery::debug("Triggering ".($i?"bubbled ":'')."event '{$type}' on "
858
- ."node \n");//.phpQueryObject::whois($node)."\n");
859
- $event->currentTarget = $node;
860
- $eventNode = self::getNode($documentID, $node);
861
- if (isset($eventNode->eventHandlers)) {
862
- foreach($eventNode->eventHandlers as $eventType => $handlers) {
863
- $eventNamespace = null;
864
- if (strpos($type, '.') !== false)
865
- list($eventName, $eventNamespace) = explode('.', $eventType);
866
- else
867
- $eventName = $eventType;
868
- if ($name != $eventName)
869
- continue;
870
- if ($namespace && $eventNamespace && $namespace != $eventNamespace)
871
- continue;
872
- foreach($handlers as $handler) {
873
- phpQuery::debug("Calling event handler\n");
874
- $event->data = $handler['data']
875
- ? $handler['data']
876
- : null;
877
- $params = array_merge(array($event), $data);
878
- $return = phpQuery::callbackRun($handler['callback'], $params);
879
- if ($return === false) {
880
- $event->bubbles = false;
881
- }
882
- }
883
- }
884
- }
885
- // to bubble or not to bubble...
886
- if (! $event->bubbles)
887
- break;
888
- $node = $node->parentNode;
889
- $i++;
890
- }
891
- }
892
- }
893
- /**
894
- * Binds a handler to one or more events (like click) for each matched element.
895
- * Can also bind custom events.
896
- *
897
- * @param DOMNode|phpQueryObject|string $document
898
- * @param unknown_type $type
899
- * @param unknown_type $data Optional
900
- * @param unknown_type $callback
901
- *
902
- * @TODO support '!' (exclusive) events
903
- * @TODO support more than event in $type (space-separated)
904
- * @TODO support binding to global events
905
- */
906
- public static function add($document, $node, $type, $data, $callback = null) {
907
- phpQuery::debug("Binding '$type' event");
908
- $documentID = phpQuery::getDocumentID($document);
909
- // if (is_null($callback) && is_callable($data)) {
910
- // $callback = $data;
911
- // $data = null;
912
- // }
913
- $eventNode = self::getNode($documentID, $node);
914
- if (! $eventNode)
915
- $eventNode = self::setNode($documentID, $node);
916
- if (!isset($eventNode->eventHandlers[$type]))
917
- $eventNode->eventHandlers[$type] = array();
918
- $eventNode->eventHandlers[$type][] = array(
919
- 'callback' => $callback,
920
- 'data' => $data,
921
- );
922
- }
923
- /**
924
- * Enter description here...
925
- *
926
- * @param DOMNode|phpQueryObject|string $document
927
- * @param unknown_type $type
928
- * @param unknown_type $callback
929
- *
930
- * @TODO namespace events
931
- * @TODO support more than event in $type (space-separated)
932
- */
933
- public static function remove($document, $node, $type = null, $callback = null) {
934
- $documentID = phpQuery::getDocumentID($document);
935
- $eventNode = self::getNode($documentID, $node);
936
- if (is_object($eventNode) && isset($eventNode->eventHandlers[$type])) {
937
- if ($callback) {
938
- foreach($eventNode->eventHandlers[$type] as $k => $handler)
939
- if ($handler['callback'] == $callback)
940
- unset($eventNode->eventHandlers[$type][$k]);
941
- } else {
942
- unset($eventNode->eventHandlers[$type]);
943
- }
944
- }
945
- }
946
- protected static function getNode($documentID, $node) {
947
- foreach(phpQuery::$documents[$documentID]->eventsNodes as $eventNode) {
948
- if ($node->isSameNode($eventNode))
949
- return $eventNode;
950
- }
951
- }
952
- protected static function setNode($documentID, $node) {
953
- phpQuery::$documents[$documentID]->eventsNodes[] = $node;
954
- return phpQuery::$documents[$documentID]->eventsNodes[
955
- count(phpQuery::$documents[$documentID]->eventsNodes)-1
956
- ];
957
- }
958
- protected static function issetGlobal($documentID, $type) {
959
- return isset(phpQuery::$documents[$documentID])
960
- ? in_array($type, phpQuery::$documents[$documentID]->eventsGlobal)
961
- : false;
962
- }
963
- }
964
-
965
-
966
- interface ICallbackNamed {
967
- function hasName();
968
- function getName();
969
- }
970
- /**
971
- * Callback class introduces currying-like pattern.
972
- *
973
- * Example:
974
- * function foo($param1, $param2, $param3) {
975
- * var_dump($param1, $param2, $param3);
976
- * }
977
- * $fooCurried = new Callback('foo',
978
- * 'param1 is now statically set',
979
- * new CallbackParam, new CallbackParam
980
- * );
981
- * phpQuery::callbackRun($fooCurried,
982
- * array('param2 value', 'param3 value'
983
- * );
984
- *
985
- * Callback class is supported in all phpQuery methods which accepts callbacks.
986
- *
987
- * @link http://code.google.com/p/phpquery/wiki/Callbacks#Param_Structures
988
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
989
- *
990
- * @TODO??? return fake forwarding function created via create_function
991
- * @TODO honor paramStructure
992
- */
993
- class Callback
994
- implements ICallbackNamed {
995
- public $callback = null;
996
- public $params = null;
997
- protected $name;
998
- public function __construct($callback, $param1 = null, $param2 = null,
999
- $param3 = null) {
1000
- $params = func_get_args();
1001
- $params = array_slice($params, 1);
1002
- if ($callback instanceof Callback) {
1003
- // TODO implement recurention
1004
- } else {
1005
- $this->callback = $callback;
1006
- $this->params = $params;
1007
- }
1008
- }
1009
- public function getName() {
1010
- return 'Callback: '.$this->name;
1011
- }
1012
- public function hasName() {
1013
- return isset($this->name) && $this->name;
1014
- }
1015
- public function setName($name) {
1016
- $this->name = $name;
1017
- return $this;
1018
- }
1019
- // TODO test me
1020
- // public function addParams() {
1021
- // $params = func_get_args();
1022
- // return new Callback($this->callback, $this->params+$params);
1023
- // }
1024
- }
1025
- /**
1026
- * Shorthand for new Callback(create_function(...), ...);
1027
- *
1028
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
1029
- */
1030
- class CallbackBody extends Callback {
1031
- public function __construct($paramList, $code, $param1 = null, $param2 = null,
1032
- $param3 = null) {
1033
- $params = func_get_args();
1034
- $params = array_slice($params, 2);
1035
- $this->callback = create_function($paramList, $code);
1036
- $this->params = $params;
1037
- }
1038
- }
1039
- /**
1040
- * Callback type which on execution returns reference passed during creation.
1041
- *
1042
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
1043
- */
1044
- class CallbackReturnReference extends Callback
1045
- implements ICallbackNamed {
1046
- protected $reference;
1047
- public function __construct(&$reference, $name = null){
1048
- $this->reference =& $reference;
1049
- $this->callback = array($this, 'callback');
1050
- }
1051
- public function callback() {
1052
- return $this->reference;
1053
- }
1054
- public function getName() {
1055
- return 'Callback: '.$this->name;
1056
- }
1057
- public function hasName() {
1058
- return isset($this->name) && $this->name;
1059
- }
1060
- }
1061
- /**
1062
- * Callback type which on execution returns value passed during creation.
1063
- *
1064
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
1065
- */
1066
- class CallbackReturnValue extends Callback
1067
- implements ICallbackNamed {
1068
- protected $value;
1069
- protected $name;
1070
- public function __construct($value, $name = null){
1071
- $this->value =& $value;
1072
- $this->name = $name;
1073
- $this->callback = array($this, 'callback');
1074
- }
1075
- public function callback() {
1076
- return $this->value;
1077
- }
1078
- public function __toString() {
1079
- return $this->getName();
1080
- }
1081
- public function getName() {
1082
- return 'Callback: '.$this->name;
1083
- }
1084
- public function hasName() {
1085
- return isset($this->name) && $this->name;
1086
- }
1087
- }
1088
- /**
1089
- * CallbackParameterToReference can be used when we don't really want a callback,
1090
- * only parameter passed to it. CallbackParameterToReference takes first
1091
- * parameter's value and passes it to reference.
1092
- *
1093
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
1094
- */
1095
- class CallbackParameterToReference extends Callback {
1096
- /**
1097
- * @param $reference
1098
- * @TODO implement $paramIndex;
1099
- * param index choose which callback param will be passed to reference
1100
- */
1101
- public function __construct(&$reference){
1102
- $this->callback =& $reference;
1103
- }
1104
- }
1105
- //class CallbackReference extends Callback {
1106
- // /**
1107
- // *
1108
- // * @param $reference
1109
- // * @param $paramIndex
1110
- // * @todo implement $paramIndex; param index choose which callback param will be passed to reference
1111
- // */
1112
- // public function __construct(&$reference, $name = null){
1113
- // $this->callback =& $reference;
1114
- // }
1115
- //}
1116
- class CallbackParam {}
1117
-
1118
- /**
1119
- * Class representing phpQuery objects.
1120
- *
1121
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
1122
- * @package phpQuery
1123
- * @method phpQueryObject clone() clone()
1124
- * @method phpQueryObject empty() empty()
1125
- * @method phpQueryObject next() next($selector = null)
1126
- * @method phpQueryObject prev() prev($selector = null)
1127
- * @property Int $length
1128
- */
1129
- class phpQueryObject
1130
- implements Iterator, Countable, ArrayAccess {
1131
- public $documentID = null;
1132
- /**
1133
- * DOMDocument class.
1134
- *
1135
- * @var DOMDocument
1136
- */
1137
- public $document = null;
1138
- public $charset = null;
1139
- /**
1140
- *
1141
- * @var DOMDocumentWrapper
1142
- */
1143
- public $documentWrapper = null;
1144
- /**
1145
- * XPath interface.
1146
- *
1147
- * @var DOMXPath
1148
- */
1149
- public $xpath = null;
1150
- /**
1151
- * Stack of selected elements.
1152
- * @TODO refactor to ->nodes
1153
- * @var array
1154
- */
1155
- public $elements = array();
1156
- /**
1157
- * @access private
1158
- */
1159
- protected $elementsBackup = array();
1160
- /**
1161
- * @access private
1162
- */
1163
- protected $previous = null;
1164
- /**
1165
- * @access private
1166
- * @TODO deprecate
1167
- */
1168
- protected $root = array();
1169
- /**
1170
- * Indicated if doument is just a fragment (no <html> tag).
1171
- *
1172
- * Every document is realy a full document, so even documentFragments can
1173
- * be queried against <html>, but getDocument(id)->htmlOuter() will return
1174
- * only contents of <body>.
1175
- *
1176
- * @var bool
1177
- */
1178
- public $documentFragment = true;
1179
- /**
1180
- * Iterator interface helper
1181
- * @access private
1182
- */
1183
- protected $elementsInterator = array();
1184
- /**
1185
- * Iterator interface helper
1186
- * @access private
1187
- */
1188
- protected $valid = false;
1189
- /**
1190
- * Iterator interface helper
1191
- * @access private
1192
- */
1193
- protected $current = null;
1194
- /**
1195
- * Enter description here...
1196
- *
1197
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1198
- */
1199
- public function __construct($documentID) {
1200
- // if ($documentID instanceof self)
1201
- // var_dump($documentID->getDocumentID());
1202
- $id = $documentID instanceof self
1203
- ? $documentID->getDocumentID()
1204
- : $documentID;
1205
- // var_dump($id);
1206
- if (! isset(phpQuery::$documents[$id] )) {
1207
- // var_dump(phpQuery::$documents);
1208
- throw new Exception("Document with ID '{$id}' isn't loaded. Use phpQuery::newDocument(\$html) or phpQuery::newDocumentFile(\$file) first.");
1209
- }
1210
- $this->documentID = $id;
1211
- $this->documentWrapper =& phpQuery::$documents[$id];
1212
- $this->document =& $this->documentWrapper->document;
1213
- $this->xpath =& $this->documentWrapper->xpath;
1214
- $this->charset =& $this->documentWrapper->charset;
1215
- $this->documentFragment =& $this->documentWrapper->isDocumentFragment;
1216
- // TODO check $this->DOM->documentElement;
1217
- // $this->root = $this->document->documentElement;
1218
- $this->root =& $this->documentWrapper->root;
1219
- // $this->toRoot();
1220
- $this->elements = array($this->root);
1221
- }
1222
- /**
1223
- *
1224
- * @access private
1225
- * @param $attr
1226
- * @return unknown_type
1227
- */
1228
- public function __get($attr) {
1229
- switch($attr) {
1230
- // FIXME doesnt work at all ?
1231
- case 'length':
1232
- return $this->size();
1233
- break;
1234
- default:
1235
- return $this->$attr;
1236
- }
1237
- }
1238
- /**
1239
- * Saves actual object to $var by reference.
1240
- * Useful when need to break chain.
1241
- * @param phpQueryObject $var
1242
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1243
- */
1244
- public function toReference(&$var) {
1245
- return $var = $this;
1246
- }
1247
- public function documentFragment($state = null) {
1248
- if ($state) {
1249
- phpQuery::$documents[$this->getDocumentID()]['documentFragment'] = $state;
1250
- return $this;
1251
- }
1252
- return $this->documentFragment;
1253
- }
1254
- /**
1255
- * @access private
1256
- * @TODO documentWrapper
1257
- */
1258
- protected function isRoot( $node) {
1259
- // return $node instanceof DOMDOCUMENT || $node->tagName == 'html';
1260
- return $node instanceof DOMDOCUMENT
1261
- || ($node instanceof DOMELEMENT && $node->tagName == 'html')
1262
- || $this->root->isSameNode($node);
1263
- }
1264
- /**
1265
- * @access private
1266
- */
1267
- protected function stackIsRoot() {
1268
- return $this->size() == 1 && $this->isRoot($this->elements[0]);
1269
- }
1270
- /**
1271
- * Enter description here...
1272
- * NON JQUERY METHOD
1273
- *
1274
- * Watch out, it doesn't creates new instance, can be reverted with end().
1275
- *
1276
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1277
- */
1278
- public function toRoot() {
1279
- $this->elements = array($this->root);
1280
- return $this;
1281
- // return $this->newInstance(array($this->root));
1282
- }
1283
- /**
1284
- * Saves object's DocumentID to $var by reference.
1285
- * <code>
1286
- * $myDocumentId;
1287
- * phpQuery::newDocument('<div/>')
1288
- * ->getDocumentIDRef($myDocumentId)
1289
- * ->find('div')->...
1290
- * </code>
1291
- *
1292
- * @param unknown_type $domId
1293
- * @see phpQuery::newDocument
1294
- * @see phpQuery::newDocumentFile
1295
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1296
- */
1297
- public function getDocumentIDRef(&$documentID) {
1298
- $documentID = $this->getDocumentID();
1299
- return $this;
1300
- }
1301
- /**
1302
- * Returns object with stack set to document root.
1303
- *
1304
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1305
- */
1306
- public function getDocument() {
1307
- return phpQuery::getDocument($this->getDocumentID());
1308
- }
1309
- /**
1310
- *
1311
- * @return DOMDocument
1312
- */
1313
- public function getDOMDocument() {
1314
- return $this->document;
1315
- }
1316
- /**
1317
- * Get object's Document ID.
1318
- *
1319
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1320
- */
1321
- public function getDocumentID() {
1322
- return $this->documentID;
1323
- }
1324
- /**
1325
- * Unloads whole document from memory.
1326
- * CAUTION! None further operations will be possible on this document.
1327
- * All objects refering to it will be useless.
1328
- *
1329
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1330
- */
1331
- public function unloadDocument() {
1332
- phpQuery::unloadDocuments($this->getDocumentID());
1333
- }
1334
- public function isHTML() {
1335
- return $this->documentWrapper->isHTML;
1336
- }
1337
- public function isXHTML() {
1338
- return $this->documentWrapper->isXHTML;
1339
- }
1340
- public function isXML() {
1341
- return $this->documentWrapper->isXML;
1342
- }
1343
- /**
1344
- * Enter description here...
1345
- *
1346
- * @link http://docs.jquery.com/Ajax/serialize
1347
- * @return string
1348
- */
1349
- public function serialize() {
1350
- return phpQuery::param($this->serializeArray());
1351
- }
1352
- /**
1353
- * Enter description here...
1354
- *
1355
- * @link http://docs.jquery.com/Ajax/serializeArray
1356
- * @return array
1357
- */
1358
- public function serializeArray($submit = null) {
1359
- $source = $this->filter('form, input, select, textarea')
1360
- ->find('input, select, textarea')
1361
- ->andSelf()
1362
- ->not('form');
1363
- $return = array();
1364
- // $source->dumpDie();
1365
- foreach($source as $input) {
1366
- $input = phpQuery::pq($input);
1367
- if ($input->is('[disabled]'))
1368
- continue;
1369
- if (!$input->is('[name]'))
1370
- continue;
1371
- if ($input->is('[type=checkbox]') && !$input->is('[checked]'))
1372
- continue;
1373
- // jquery diff
1374
- if ($submit && $input->is('[type=submit]')) {
1375
- if ($submit instanceof DOMELEMENT && ! $input->elements[0]->isSameNode($submit))
1376
- continue;
1377
- else if (is_string($submit) && $input->attr('name') != $submit)
1378
- continue;
1379
- }
1380
- $return[] = array(
1381
- 'name' => $input->attr('name'),
1382
- 'value' => $input->val(),
1383
- );
1384
- }
1385
- return $return;
1386
- }
1387
- /**
1388
- * @access private
1389
- */
1390
- protected function debug($in) {
1391
- if (! phpQuery::$debug )
1392
- return;
1393
- print('<pre>');
1394
- print_r($in);
1395
- // file debug
1396
- // file_put_contents(dirname(__FILE__).'/phpQuery.log', print_r($in, true)."\n", FILE_APPEND);
1397
- // quite handy debug trace
1398
- // if ( is_array($in))
1399
- // print_r(array_slice(debug_backtrace(), 3));
1400
- print("</pre>\n");
1401
- }
1402
- /**
1403
- * @access private
1404
- */
1405
- protected function isRegexp($pattern) {
1406
- return in_array(
1407
- $pattern[ mb_strlen($pattern)-1 ],
1408
- array('^','*','$')
1409
- );
1410
- }
1411
- /**
1412
- * Determines if $char is really a char.
1413
- *
1414
- * @param string $char
1415
- * @return bool
1416
- * @todo rewrite me to charcode range ! ;)
1417
- * @access private
1418
- */
1419
- protected function isChar($char) {
1420
- return extension_loaded('mbstring') && phpQuery::$mbstringSupport
1421
- ? mb_eregi('\w', $char)
1422
- : preg_match('@\w@', $char);
1423
- }
1424
- /**
1425
- * @access private
1426
- */
1427
- protected function parseSelector($query) {
1428
- // clean spaces
1429
- // TODO include this inside parsing ?
1430
- $query = trim(
1431
- preg_replace('@\s+@', ' ',
1432
- preg_replace('@\s*(>|\\+|~)\s*@', '\\1', $query)
1433
- )
1434
- );
1435
- $queries = array(array());
1436
- if (! $query)
1437
- return $queries;
1438
- $return =& $queries[0];
1439
- $specialChars = array('>',' ');
1440
- // $specialCharsMapping = array('/' => '>');
1441
- $specialCharsMapping = array();
1442
- $strlen = mb_strlen($query);
1443
- $classChars = array('.', '-');
1444
- $pseudoChars = array('-');
1445
- $tagChars = array('*', '|', '-');
1446
- // split multibyte string
1447
- // http://code.google.com/p/phpquery/issues/detail?id=76
1448
- $_query = array();
1449
- for ($i=0; $i<$strlen; $i++)
1450
- $_query[] = mb_substr($query, $i, 1);
1451
- $query = $_query;
1452
- // it works, but i dont like it...
1453
- $i = 0;
1454
- while( $i < $strlen) {
1455
- $c = $query[$i];
1456
- $tmp = '';
1457
- // TAG
1458
- if ($this->isChar($c) || in_array($c, $tagChars)) {
1459
- while(isset($query[$i])
1460
- && ($this->isChar($query[$i]) || in_array($query[$i], $tagChars))) {
1461
- $tmp .= $query[$i];
1462
- $i++;
1463
- }
1464
- $return[] = $tmp;
1465
- // IDs
1466
- } else if ( $c == '#') {
1467
- $i++;
1468
- while( isset($query[$i]) && ($this->isChar($query[$i]) || $query[$i] == '-')) {
1469
- $tmp .= $query[$i];
1470
- $i++;
1471
- }
1472
- $return[] = '#'.$tmp;
1473
- // SPECIAL CHARS
1474
- } else if (in_array($c, $specialChars)) {
1475
- $return[] = $c;
1476
- $i++;
1477
- // MAPPED SPECIAL MULTICHARS
1478
- // } else if ( $c.$query[$i+1] == '//') {
1479
- // $return[] = ' ';
1480
- // $i = $i+2;
1481
- // MAPPED SPECIAL CHARS
1482
- } else if ( isset($specialCharsMapping[$c])) {
1483
- $return[] = $specialCharsMapping[$c];
1484
- $i++;
1485
- // COMMA
1486
- } else if ( $c == ',') {
1487
- $queries[] = array();
1488
- $return =& $queries[ count($queries)-1 ];
1489
- $i++;
1490
- while( isset($query[$i]) && $query[$i] == ' ')
1491
- $i++;
1492
- // CLASSES
1493
- } else if ($c == '.') {
1494
- while( isset($query[$i]) && ($this->isChar($query[$i]) || in_array($query[$i], $classChars))) {
1495
- $tmp .= $query[$i];
1496
- $i++;
1497
- }
1498
- $return[] = $tmp;
1499
- // ~ General Sibling Selector
1500
- } else if ($c == '~') {
1501
- $spaceAllowed = true;
1502
- $tmp .= $query[$i++];
1503
- while( isset($query[$i])
1504
- && ($this->isChar($query[$i])
1505
- || in_array($query[$i], $classChars)
1506
- || $query[$i] == '*'
1507
- || ($query[$i] == ' ' && $spaceAllowed)
1508
- )) {
1509
- if ($query[$i] != ' ')
1510
- $spaceAllowed = false;
1511
- $tmp .= $query[$i];
1512
- $i++;
1513
- }
1514
- $return[] = $tmp;
1515
- // + Adjacent sibling selectors
1516
- } else if ($c == '+') {
1517
- $spaceAllowed = true;
1518
- $tmp .= $query[$i++];
1519
- while( isset($query[$i])
1520
- && ($this->isChar($query[$i])
1521
- || in_array($query[$i], $classChars)
1522
- || $query[$i] == '*'
1523
- || ($spaceAllowed && $query[$i] == ' ')
1524
- )) {
1525
- if ($query[$i] != ' ')
1526
- $spaceAllowed = false;
1527
- $tmp .= $query[$i];
1528
- $i++;
1529
- }
1530
- $return[] = $tmp;
1531
- // ATTRS
1532
- } else if ($c == '[') {
1533
- $stack = 1;
1534
- $tmp .= $c;
1535
- while( isset($query[++$i])) {
1536
- $tmp .= $query[$i];
1537
- if ( $query[$i] == '[') {
1538
- $stack++;
1539
- } else if ( $query[$i] == ']') {
1540
- $stack--;
1541
- if (! $stack )
1542
- break;
1543
- }
1544
- }
1545
- $return[] = $tmp;
1546
- $i++;
1547
- // PSEUDO CLASSES
1548
- } else if ($c == ':') {
1549
- $stack = 1;
1550
- $tmp .= $query[$i++];
1551
- while( isset($query[$i]) && ($this->isChar($query[$i]) || in_array($query[$i], $pseudoChars))) {
1552
- $tmp .= $query[$i];
1553
- $i++;
1554
- }
1555
- // with arguments ?
1556
- if ( isset($query[$i]) && $query[$i] == '(') {
1557
- $tmp .= $query[$i];
1558
- $stack = 1;
1559
- while( isset($query[++$i])) {
1560
- $tmp .= $query[$i];
1561
- if ( $query[$i] == '(') {
1562
- $stack++;
1563
- } else if ( $query[$i] == ')') {
1564
- $stack--;
1565
- if (! $stack )
1566
- break;
1567
- }
1568
- }
1569
- $return[] = $tmp;
1570
- $i++;
1571
- } else {
1572
- $return[] = $tmp;
1573
- }
1574
- } else {
1575
- $i++;
1576
- }
1577
- }
1578
- foreach($queries as $k => $q) {
1579
- if (isset($q[0])) {
1580
- if (isset($q[0][0]) && $q[0][0] == ':')
1581
- array_unshift($queries[$k], '*');
1582
- if ($q[0] != '>')
1583
- array_unshift($queries[$k], ' ');
1584
- }
1585
- }
1586
- return $queries;
1587
- }
1588
- /**
1589
- * Return matched DOM nodes.
1590
- *
1591
- * @param int $index
1592
- * @return array|DOMElement Single DOMElement or array of DOMElement.
1593
- */
1594
- public function get($index = null, $callback1 = null, $callback2 = null, $callback3 = null) {
1595
- $return = isset($index)
1596
- ? (isset($this->elements[$index]) ? $this->elements[$index] : null)
1597
- : $this->elements;
1598
- // pass thou callbacks
1599
- $args = func_get_args();
1600
- $args = array_slice($args, 1);
1601
- foreach($args as $callback) {
1602
- if (is_array($return))
1603
- foreach($return as $k => $v)
1604
- $return[$k] = phpQuery::callbackRun($callback, array($v));
1605
- else
1606
- $return = phpQuery::callbackRun($callback, array($return));
1607
- }
1608
- return $return;
1609
- }
1610
- /**
1611
- * Return matched DOM nodes.
1612
- * jQuery difference.
1613
- *
1614
- * @param int $index
1615
- * @return array|string Returns string if $index != null
1616
- * @todo implement callbacks
1617
- * @todo return only arrays ?
1618
- * @todo maybe other name...
1619
- */
1620
- public function getString($index = null, $callback1 = null, $callback2 = null, $callback3 = null) {
1621
- if ($index)
1622
- $return = $this->eq($index)->text();
1623
- else {
1624
- $return = array();
1625
- for($i = 0; $i < $this->size(); $i++) {
1626
- $return[] = $this->eq($i)->text();
1627
- }
1628
- }
1629
- // pass thou callbacks
1630
- $args = func_get_args();
1631
- $args = array_slice($args, 1);
1632
- foreach($args as $callback) {
1633
- $return = phpQuery::callbackRun($callback, array($return));
1634
- }
1635
- return $return;
1636
- }
1637
- /**
1638
- * Return matched DOM nodes.
1639
- * jQuery difference.
1640
- *
1641
- * @param int $index
1642
- * @return array|string Returns string if $index != null
1643
- * @todo implement callbacks
1644
- * @todo return only arrays ?
1645
- * @todo maybe other name...
1646
- */
1647
- public function getStrings($index = null, $callback1 = null, $callback2 = null, $callback3 = null) {
1648
- if ($index)
1649
- $return = $this->eq($index)->text();
1650
- else {
1651
- $return = array();
1652
- for($i = 0; $i < $this->size(); $i++) {
1653
- $return[] = $this->eq($i)->text();
1654
- }
1655
- // pass thou callbacks
1656
- $args = func_get_args();
1657
- $args = array_slice($args, 1);
1658
- }
1659
- foreach($args as $callback) {
1660
- if (is_array($return))
1661
- foreach($return as $k => $v)
1662
- $return[$k] = phpQuery::callbackRun($callback, array($v));
1663
- else
1664
- $return = phpQuery::callbackRun($callback, array($return));
1665
- }
1666
- return $return;
1667
- }
1668
- /**
1669
- * Returns new instance of actual class.
1670
- *
1671
- * @param array $newStack Optional. Will replace old stack with new and move old one to history.c
1672
- */
1673
- public function newInstance($newStack = null) {
1674
- $class = get_class($this);
1675
- // support inheritance by passing old object to overloaded constructor
1676
- $new = $class != 'phpQuery'
1677
- ? new $class($this, $this->getDocumentID())
1678
- : new phpQueryObject($this->getDocumentID());
1679
- $new->previous = $this;
1680
- if (is_null($newStack)) {
1681
- $new->elements = $this->elements;
1682
- if ($this->elementsBackup)
1683
- $this->elements = $this->elementsBackup;
1684
- } else if (is_string($newStack)) {
1685
- $new->elements = phpQuery::pq($newStack, $this->getDocumentID())->stack();
1686
- } else {
1687
- $new->elements = $newStack;
1688
- }
1689
- return $new;
1690
- }
1691
- /**
1692
- * Enter description here...
1693
- *
1694
- * In the future, when PHP will support XLS 2.0, then we would do that this way:
1695
- * contains(tokenize(@class, '\s'), "something")
1696
- * @param unknown_type $class
1697
- * @param unknown_type $node
1698
- * @return boolean
1699
- * @access private
1700
- */
1701
- protected function matchClasses($class, $node) {
1702
- // multi-class
1703
- if ( mb_strpos($class, '.', 1)) {
1704
- $classes = explode('.', substr($class, 1));
1705
- $classesCount = count( $classes );
1706
- $nodeClasses = explode(' ', $node->getAttribute('class') );
1707
- $nodeClassesCount = count( $nodeClasses );
1708
- if ( $classesCount > $nodeClassesCount )
1709
- return false;
1710
- $diff = count(
1711
- array_diff(
1712
- $classes,
1713
- $nodeClasses
1714
- )
1715
- );
1716
- if (! $diff )
1717
- return true;
1718
- // single-class
1719
- } else {
1720
- return in_array(
1721
- // strip leading dot from class name
1722
- substr($class, 1),
1723
- // get classes for element as array
1724
- explode(' ', $node->getAttribute('class') )
1725
- );
1726
- }
1727
- }
1728
- /**
1729
- * @access private
1730
- */
1731
- protected function runQuery($XQuery, $selector = null, $compare = null) {
1732
- if ($compare && ! method_exists($this, $compare))
1733
- return false;
1734
- $stack = array();
1735
- if (! $this->elements)
1736
- $this->debug('Stack empty, skipping...');
1737
- // var_dump($this->elements[0]->nodeType);
1738
- // element, document
1739
- foreach($this->stack(array(1, 9, 13)) as $k => $stackNode) {
1740
- $detachAfter = false;
1741
- // to work on detached nodes we need temporary place them somewhere
1742
- // thats because context xpath queries sucks ;]
1743
- $testNode = $stackNode;
1744
- while ($testNode) {
1745
- if (! $testNode->parentNode && ! $this->isRoot($testNode)) {
1746
- $this->root->appendChild($testNode);
1747
- $detachAfter = $testNode;
1748
- break;
1749
- }
1750
- $testNode = isset($testNode->parentNode)
1751
- ? $testNode->parentNode
1752
- : null;
1753
- }
1754
- // XXX tmp ?
1755
- $xpath = $this->documentWrapper->isXHTML
1756
- ? $this->getNodeXpath($stackNode, 'html')
1757
- : $this->getNodeXpath($stackNode);
1758
- // FIXME pseudoclasses-only query, support XML
1759
- $query = $XQuery == '//' && $xpath == '/html[1]'
1760
- ? '//*'
1761
- : $xpath.$XQuery;
1762
- $this->debug("XPATH: {$query}");
1763
- // run query, get elements
1764
- $nodes = $this->xpath->query($query);
1765
- $this->debug("QUERY FETCHED");
1766
- if (! $nodes->length )
1767
- $this->debug('Nothing found');
1768
- $debug = array();
1769
- foreach($nodes as $node) {
1770
- $matched = false;
1771
- if ( $compare) {
1772
- phpQuery::$debug ?
1773
- $this->debug("Found: ".$this->whois( $node ).", comparing with {$compare}()")
1774
- : null;
1775
- $phpQueryDebug = phpQuery::$debug;
1776
- phpQuery::$debug = false;
1777
- // TODO ??? use phpQuery::callbackRun()
1778
- if (call_user_func_array(array($this, $compare), array($selector, $node)))
1779
- $matched = true;
1780
- phpQuery::$debug = $phpQueryDebug;
1781
- } else {
1782
- $matched = true;
1783
- }
1784
- if ( $matched) {
1785
- if (phpQuery::$debug)
1786
- $debug[] = $this->whois( $node );
1787
- $stack[] = $node;
1788
- }
1789
- }
1790
- if (phpQuery::$debug) {
1791
- $this->debug("Matched ".count($debug).": ".implode(', ', $debug));
1792
- }
1793
- if ($detachAfter)
1794
- $this->root->removeChild($detachAfter);
1795
- }
1796
- $this->elements = $stack;
1797
- }
1798
- /**
1799
- * Enter description here...
1800
- *
1801
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1802
- */
1803
- public function find($selectors, $context = null, $noHistory = false) {
1804
- if (!$noHistory)
1805
- // backup last stack /for end()/
1806
- $this->elementsBackup = $this->elements;
1807
- // allow to define context
1808
- // TODO combine code below with phpQuery::pq() context guessing code
1809
- // as generic function
1810
- if ($context) {
1811
- if (! is_array($context) && $context instanceof DOMELEMENT)
1812
- $this->elements = array($context);
1813
- else if (is_array($context)) {
1814
- $this->elements = array();
1815
- foreach ($context as $c)
1816
- if ($c instanceof DOMELEMENT)
1817
- $this->elements[] = $c;
1818
- } else if ( $context instanceof self )
1819
- $this->elements = $context->elements;
1820
- }
1821
- $queries = $this->parseSelector($selectors);
1822
- $this->debug(array('FIND', $selectors, $queries));
1823
- $XQuery = '';
1824
- // remember stack state because of multi-queries
1825
- $oldStack = $this->elements;
1826
- // here we will be keeping found elements
1827
- $stack = array();
1828
- foreach($queries as $selector) {
1829
- $this->elements = $oldStack;
1830
- $delimiterBefore = false;
1831
- foreach($selector as $s) {
1832
- // TAG
1833
- $isTag = extension_loaded('mbstring') && phpQuery::$mbstringSupport
1834
- ? mb_ereg_match('^[\w|\||-]+$', $s) || $s == '*'
1835
- : preg_match('@^[\w|\||-]+$@', $s) || $s == '*';
1836
- if ($isTag) {
1837
- if ($this->isXML()) {
1838
- // namespace support
1839
- if (mb_strpos($s, '|') !== false) {
1840
- $ns = $tag = null;
1841
- list($ns, $tag) = explode('|', $s);
1842
- $XQuery .= "$ns:$tag";
1843
- } else if ($s == '*') {
1844
- $XQuery .= "*";
1845
- } else {
1846
- $XQuery .= "*[local-name()='$s']";
1847
- }
1848
- } else {
1849
- $XQuery .= $s;
1850
- }
1851
- // ID
1852
- } else if ($s[0] == '#') {
1853
- if ($delimiterBefore)
1854
- $XQuery .= '*';
1855
- $XQuery .= "[@id='".substr($s, 1)."']";
1856
- // ATTRIBUTES
1857
- } else if ($s[0] == '[') {
1858
- if ($delimiterBefore)
1859
- $XQuery .= '*';
1860
- // strip side brackets
1861
- $attr = trim($s, '][');
1862
- $execute = false;
1863
- // attr with specifed value
1864
- if (mb_strpos($s, '=')) {
1865
- $value = null;
1866
- list($attr, $value) = explode('=', $attr);
1867
- $value = trim($value, "'\"");
1868
- if ($this->isRegexp($attr)) {
1869
- // cut regexp character
1870
- $attr = substr($attr, 0, -1);
1871
- $execute = true;
1872
- $XQuery .= "[@{$attr}]";
1873
- } else {
1874
- $XQuery .= "[@{$attr}='{$value}']";
1875
- }
1876
- // attr without specified value
1877
- } else {
1878
- $XQuery .= "[@{$attr}]";
1879
- }
1880
- if ($execute) {
1881
- $this->runQuery($XQuery, $s, 'is');
1882
- $XQuery = '';
1883
- if (! $this->length())
1884
- break;
1885
- }
1886
- // CLASSES
1887
- } else if ($s[0] == '.') {
1888
- // TODO use return $this->find("./self::*[contains(concat(\" \",@class,\" \"), \" $class \")]");
1889
- // thx wizDom ;)
1890
- if ($delimiterBefore)
1891
- $XQuery .= '*';
1892
- $XQuery .= '[@class]';
1893
- $this->runQuery($XQuery, $s, 'matchClasses');
1894
- $XQuery = '';
1895
- if (! $this->length() )
1896
- break;
1897
- // ~ General Sibling Selector
1898
- } else if ($s[0] == '~') {
1899
- $this->runQuery($XQuery);
1900
- $XQuery = '';
1901
- $this->elements = $this
1902
- ->siblings(
1903
- substr($s, 1)
1904
- )->elements;
1905
- if (! $this->length() )
1906
- break;
1907
- // + Adjacent sibling selectors
1908
- } else if ($s[0] == '+') {
1909
- // TODO /following-sibling::
1910
- $this->runQuery($XQuery);
1911
- $XQuery = '';
1912
- $subSelector = substr($s, 1);
1913
- $subElements = $this->elements;
1914
- $this->elements = array();
1915
- foreach($subElements as $node) {
1916
- // search first DOMElement sibling
1917
- $test = $node->nextSibling;
1918
- while($test && ! ($test instanceof DOMELEMENT))
1919
- $test = $test->nextSibling;
1920
- if ($test && $this->is($subSelector, $test))
1921
- $this->elements[] = $test;
1922
- }
1923
- if (! $this->length() )
1924
- break;
1925
- // PSEUDO CLASSES
1926
- } else if ($s[0] == ':') {
1927
- // TODO optimization for :first :last
1928
- if ($XQuery) {
1929
- $this->runQuery($XQuery);
1930
- $XQuery = '';
1931
- }
1932
- if (! $this->length())
1933
- break;
1934
- $this->pseudoClasses($s);
1935
- if (! $this->length())
1936
- break;
1937
- // DIRECT DESCENDANDS
1938
- } else if ($s == '>') {
1939
- $XQuery .= '/';
1940
- $delimiterBefore = 2;
1941
- // ALL DESCENDANDS
1942
- } else if ($s == ' ') {
1943
- $XQuery .= '//';
1944
- $delimiterBefore = 2;
1945
- // ERRORS
1946
- } else {
1947
- phpQuery::debug("Unrecognized token '$s'");
1948
- }
1949
- $delimiterBefore = $delimiterBefore === 2;
1950
- }
1951
- // run query if any
1952
- if ($XQuery && $XQuery != '//') {
1953
- $this->runQuery($XQuery);
1954
- $XQuery = '';
1955
- }
1956
- foreach($this->elements as $node)
1957
- if (! $this->elementsContainsNode($node, $stack))
1958
- $stack[] = $node;
1959
- }
1960
- $this->elements = $stack;
1961
- return $this->newInstance();
1962
- }
1963
- /**
1964
- * @todo create API for classes with pseudoselectors
1965
- * @access private
1966
- */
1967
- protected function pseudoClasses($class) {
1968
- // TODO clean args parsing ?
1969
- $class = ltrim($class, ':');
1970
- $haveArgs = mb_strpos($class, '(');
1971
- if ($haveArgs !== false) {
1972
- $args = substr($class, $haveArgs+1, -1);
1973
- $class = substr($class, 0, $haveArgs);
1974
- }
1975
- switch($class) {
1976
- case 'even':
1977
- case 'odd':
1978
- $stack = array();
1979
- foreach($this->elements as $i => $node) {
1980
- if ($class == 'even' && ($i%2) == 0)
1981
- $stack[] = $node;
1982
- else if ( $class == 'odd' && $i % 2 )
1983
- $stack[] = $node;
1984
- }
1985
- $this->elements = $stack;
1986
- break;
1987
- case 'eq':
1988
- $k = intval($args);
1989
- $this->elements = isset( $this->elements[$k] )
1990
- ? array( $this->elements[$k] )
1991
- : array();
1992
- break;
1993
- case 'gt':
1994
- $this->elements = array_slice($this->elements, $args+1);
1995
- break;
1996
- case 'lt':
1997
- $this->elements = array_slice($this->elements, 0, $args+1);
1998
- break;
1999
- case 'first':
2000
- if (isset($this->elements[0]))
2001
- $this->elements = array($this->elements[0]);
2002
- break;
2003
- case 'last':
2004
- if ($this->elements)
2005
- $this->elements = array($this->elements[count($this->elements)-1]);
2006
- break;
2007
- /*case 'parent':
2008
- $stack = array();
2009
- foreach($this->elements as $node) {
2010
- if ( $node->childNodes->length )
2011
- $stack[] = $node;
2012
- }
2013
- $this->elements = $stack;
2014
- break;*/
2015
- case 'contains':
2016
- $text = trim($args, "\"'");
2017
- $stack = array();
2018
- foreach($this->elements as $node) {
2019
- if (mb_stripos($node->textContent, $text) === false)
2020
- continue;
2021
- $stack[] = $node;
2022
- }
2023
- $this->elements = $stack;
2024
- break;
2025
- case 'not':
2026
- $selector = self::unQuote($args);
2027
- $this->elements = $this->not($selector)->stack();
2028
- break;
2029
- case 'slice':
2030
- // TODO jQuery difference ?
2031
- $args = explode(',',
2032
- str_replace(', ', ',', trim($args, "\"'"))
2033
- );
2034
- $start = $args[0];
2035
- $end = isset($args[1])
2036
- ? $args[1]
2037
- : null;
2038
- if ($end > 0)
2039
- $end = $end-$start;
2040
- $this->elements = array_slice($this->elements, $start, $end);
2041
- break;
2042
- case 'has':
2043
- $selector = trim($args, "\"'");
2044
- $stack = array();
2045
- foreach($this->stack(1) as $el) {
2046
- if ($this->find($selector, $el, true)->length)
2047
- $stack[] = $el;
2048
- }
2049
- $this->elements = $stack;
2050
- break;
2051
- case 'submit':
2052
- case 'reset':
2053
- $this->elements = phpQuery::merge(
2054
- $this->map(array($this, 'is'),
2055
- "input[type=$class]", new CallbackParam()
2056
- ),
2057
- $this->map(array($this, 'is'),
2058
- "button[type=$class]", new CallbackParam()
2059
- )
2060
- );
2061
- break;
2062
- // $stack = array();
2063
- // foreach($this->elements as $node)
2064
- // if ($node->is('input[type=submit]') || $node->is('button[type=submit]'))
2065
- // $stack[] = $el;
2066
- // $this->elements = $stack;
2067
- case 'input':
2068
- $this->elements = $this->map(
2069
- array($this, 'is'),
2070
- 'input', new CallbackParam()
2071
- )->elements;
2072
- break;
2073
- case 'password':
2074
- case 'checkbox':
2075
- case 'radio':
2076
- case 'hidden':
2077
- case 'image':
2078
- case 'file':
2079
- $this->elements = $this->map(
2080
- array($this, 'is'),
2081
- "input[type=$class]", new CallbackParam()
2082
- )->elements;
2083
- break;
2084
- case 'parent':
2085
- $this->elements = $this->map(
2086
- create_function('$node', '
2087
- return $node instanceof DOMELEMENT && $node->childNodes->length
2088
- ? $node : null;')
2089
- )->elements;
2090
- break;
2091
- case 'empty':
2092
- $this->elements = $this->map(
2093
- create_function('$node', '
2094
- return $node instanceof DOMELEMENT && $node->childNodes->length
2095
- ? null : $node;')
2096
- )->elements;
2097
- break;
2098
- case 'disabled':
2099
- case 'selected':
2100
- case 'checked':
2101
- $this->elements = $this->map(
2102
- array($this, 'is'),
2103
- "[$class]", new CallbackParam()
2104
- )->elements;
2105
- break;
2106
- case 'enabled':
2107
- $this->elements = $this->map(
2108
- create_function('$node', '
2109
- return pq($node)->not(":disabled") ? $node : null;')
2110
- )->elements;
2111
- break;
2112
- case 'header':
2113
- $this->elements = $this->map(
2114
- create_function('$node',
2115
- '$isHeader = isset($node->tagName) && in_array($node->tagName, array(
2116
- "h1", "h2", "h3", "h4", "h5", "h6", "h7"
2117
- ));
2118
- return $isHeader
2119
- ? $node
2120
- : null;')
2121
- )->elements;
2122
- // $this->elements = $this->map(
2123
- // create_function('$node', '$node = pq($node);
2124
- // return $node->is("h1")
2125
- // || $node->is("h2")
2126
- // || $node->is("h3")
2127
- // || $node->is("h4")
2128
- // || $node->is("h5")
2129
- // || $node->is("h6")
2130
- // || $node->is("h7")
2131
- // ? $node
2132
- // : null;')
2133
- // )->elements;
2134
- break;
2135
- case 'only-child':
2136
- $this->elements = $this->map(
2137
- create_function('$node',
2138
- 'return pq($node)->siblings()->size() == 0 ? $node : null;')
2139
- )->elements;
2140
- break;
2141
- case 'first-child':
2142
- $this->elements = $this->map(
2143
- create_function('$node', 'return pq($node)->prevAll()->size() == 0 ? $node : null;')
2144
- )->elements;
2145
- break;
2146
- case 'last-child':
2147
- $this->elements = $this->map(
2148
- create_function('$node', 'return pq($node)->nextAll()->size() == 0 ? $node : null;')
2149
- )->elements;
2150
- break;
2151
- case 'nth-child':
2152
- $param = trim($args, "\"'");
2153
- if (! $param)
2154
- break;
2155
- // nth-child(n+b) to nth-child(1n+b)
2156
- if ($param{0} == 'n')
2157
- $param = '1'.$param;
2158
- // :nth-child(index/even/odd/equation)
2159
- if ($param == 'even' || $param == 'odd')
2160
- $mapped = $this->map(
2161
- create_function('$node, $param',
2162
- '$index = pq($node)->prevAll()->size()+1;
2163
- if ($param == "even" && ($index%2) == 0)
2164
- return $node;
2165
- else if ($param == "odd" && $index%2 == 1)
2166
- return $node;
2167
- else
2168
- return null;'),
2169
- new CallbackParam(), $param
2170
- );
2171
- else if (mb_strlen($param) > 1 && $param{1} == 'n')
2172
- // an+b
2173
- $mapped = $this->map(
2174
- create_function('$node, $param',
2175
- '$prevs = pq($node)->prevAll()->size();
2176
- $index = 1+$prevs;
2177
- $b = mb_strlen($param) > 3
2178
- ? $param{3}
2179
- : 0;
2180
- $a = $param{0};
2181
- if ($b && $param{2} == "-")
2182
- $b = -$b;
2183
- if ($a > 0) {
2184
- return ($index-$b)%$a == 0
2185
- ? $node
2186
- : null;
2187
- phpQuery::debug($a."*".floor($index/$a)."+$b-1 == ".($a*floor($index/$a)+$b-1)." ?= $prevs");
2188
- return $a*floor($index/$a)+$b-1 == $prevs
2189
- ? $node
2190
- : null;
2191
- } else if ($a == 0)
2192
- return $index == $b
2193
- ? $node
2194
- : null;
2195
- else
2196
- // negative value
2197
- return $index <= $b
2198
- ? $node
2199
- : null;
2200
- // if (! $b)
2201
- // return $index%$a == 0
2202
- // ? $node
2203
- // : null;
2204
- // else
2205
- // return ($index-$b)%$a == 0
2206
- // ? $node
2207
- // : null;
2208
- '),
2209
- new CallbackParam(), $param
2210
- );
2211
- else
2212
- // index
2213
- $mapped = $this->map(
2214
- create_function('$node, $index',
2215
- '$prevs = pq($node)->prevAll()->size();
2216
- if ($prevs && $prevs == $index-1)
2217
- return $node;
2218
- else if (! $prevs && $index == 1)
2219
- return $node;
2220
- else
2221
- return null;'),
2222
- new CallbackParam(), $param
2223
- );
2224
- $this->elements = $mapped->elements;
2225
- break;
2226
- default:
2227
- $this->debug("Unknown pseudoclass '{$class}', skipping...");
2228
- }
2229
- }
2230
- /**
2231
- * @access private
2232
- */
2233
- protected function __pseudoClassParam($paramsString) {
2234
- // TODO;
2235
- }
2236
- /**
2237
- * Enter description here...
2238
- *
2239
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2240
- */
2241
- public function is($selector, $nodes = null) {
2242
- phpQuery::debug(array("Is:", $selector));
2243
- if (! $selector)
2244
- return false;
2245
- $oldStack = $this->elements;
2246
- $returnArray = false;
2247
- if ($nodes && is_array($nodes)) {
2248
- $this->elements = $nodes;
2249
- } else if ($nodes)
2250
- $this->elements = array($nodes);
2251
- $this->filter($selector, true);
2252
- $stack = $this->elements;
2253
- $this->elements = $oldStack;
2254
- if ($nodes)
2255
- return $stack ? $stack : null;
2256
- return (bool)count($stack);
2257
- }
2258
- /**
2259
- * Enter description here...
2260
- * jQuery difference.
2261
- *
2262
- * Callback:
2263
- * - $index int
2264
- * - $node DOMNode
2265
- *
2266
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2267
- * @link http://docs.jquery.com/Traversing/filter
2268
- */
2269
- public function filterCallback($callback, $_skipHistory = false) {
2270
- if (! $_skipHistory) {
2271
- $this->elementsBackup = $this->elements;
2272
- $this->debug("Filtering by callback");
2273
- }
2274
- $newStack = array();
2275
- foreach($this->elements as $index => $node) {
2276
- $result = phpQuery::callbackRun($callback, array($index, $node));
2277
- if (is_null($result) || (! is_null($result) && $result))
2278
- $newStack[] = $node;
2279
- }
2280
- $this->elements = $newStack;
2281
- return $_skipHistory
2282
- ? $this
2283
- : $this->newInstance();
2284
- }
2285
- /**
2286
- * Enter description here...
2287
- *
2288
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2289
- * @link http://docs.jquery.com/Traversing/filter
2290
- */
2291
- public function filter($selectors, $_skipHistory = false) {
2292
- if ($selectors instanceof Callback OR $selectors instanceof Closure)
2293
- return $this->filterCallback($selectors, $_skipHistory);
2294
- if (! $_skipHistory)
2295
- $this->elementsBackup = $this->elements;
2296
- $notSimpleSelector = array(' ', '>', '~', '+', '/');
2297
- if (! is_array($selectors))
2298
- $selectors = $this->parseSelector($selectors);
2299
- if (! $_skipHistory)
2300
- $this->debug(array("Filtering:", $selectors));
2301
- $finalStack = array();
2302
- foreach($selectors as $selector) {
2303
- $stack = array();
2304
- if (! $selector)
2305
- break;
2306
- // avoid first space or /
2307
- if (in_array($selector[0], $notSimpleSelector))
2308
- $selector = array_slice($selector, 1);
2309
- // PER NODE selector chunks
2310
- foreach($this->stack() as $node) {
2311
- $break = false;
2312
- foreach($selector as $s) {
2313
- if (!($node instanceof DOMELEMENT)) {
2314
- // all besides DOMElement
2315
- if ( $s[0] == '[') {
2316
- $attr = trim($s, '[]');
2317
- if ( mb_strpos($attr, '=')) {
2318
- list( $attr, $val ) = explode('=', $attr);
2319
- if ($attr == 'nodeType' && $node->nodeType != $val)
2320
- $break = true;
2321
- }
2322
- } else
2323
- $break = true;
2324
- } else {
2325
- // DOMElement only
2326
- // ID
2327
- if ( $s[0] == '#') {
2328
- if ( $node->getAttribute('id') != substr($s, 1) )
2329
- $break = true;
2330
- // CLASSES
2331
- } else if ( $s[0] == '.') {
2332
- if (! $this->matchClasses( $s, $node ) )
2333
- $break = true;
2334
- // ATTRS
2335
- } else if ( $s[0] == '[') {
2336
- // strip side brackets
2337
- $attr = trim($s, '[]');
2338
- if (mb_strpos($attr, '=')) {
2339
- list($attr, $val) = explode('=', $attr);
2340
- $val = self::unQuote($val);
2341
- if ($attr == 'nodeType') {
2342
- if ($val != $node->nodeType)
2343
- $break = true;
2344
- } else if ($this->isRegexp($attr)) {
2345
- $val = extension_loaded('mbstring') && phpQuery::$mbstringSupport
2346
- ? quotemeta(trim($val, '"\''))
2347
- : preg_quote(trim($val, '"\''), '@');
2348
- // switch last character
2349
- switch( substr($attr, -1)) {
2350
- // quotemeta used insted of preg_quote
2351
- // http://code.google.com/p/phpquery/issues/detail?id=76
2352
- case '^':
2353
- $pattern = '^'.$val;
2354
- break;
2355
- case '*':
2356
- $pattern = '.*'.$val.'.*';
2357
- break;
2358
- case '$':
2359
- $pattern = '.*'.$val.'$';
2360
- break;
2361
- }
2362
- // cut last character
2363
- $attr = substr($attr, 0, -1);
2364
- $isMatch = extension_loaded('mbstring') && phpQuery::$mbstringSupport
2365
- ? mb_ereg_match($pattern, $node->getAttribute($attr))
2366
- : preg_match("@{$pattern}@", $node->getAttribute($attr));
2367
- if (! $isMatch)
2368
- $break = true;
2369
- } else if ($node->getAttribute($attr) != $val)
2370
- $break = true;
2371
- } else if (! $node->hasAttribute($attr))
2372
- $break = true;
2373
- // PSEUDO CLASSES
2374
- } else if ( $s[0] == ':') {
2375
- // skip
2376
- // TAG
2377
- } else if (trim($s)) {
2378
- if ($s != '*') {
2379
- // TODO namespaces
2380
- if (isset($node->tagName)) {
2381
- if ($node->tagName != $s)
2382
- $break = true;
2383
- } else if ($s == 'html' && ! $this->isRoot($node))
2384
- $break = true;
2385
- }
2386
- // AVOID NON-SIMPLE SELECTORS
2387
- } else if (in_array($s, $notSimpleSelector)) {
2388
- $break = true;
2389
- $this->debug(array('Skipping non simple selector', $selector));
2390
- }
2391
- }
2392
- if ($break)
2393
- break;
2394
- }
2395
- // if element passed all chunks of selector - add it to new stack
2396
- if (! $break )
2397
- $stack[] = $node;
2398
- }
2399
- $tmpStack = $this->elements;
2400
- $this->elements = $stack;
2401
- // PER ALL NODES selector chunks
2402
- foreach($selector as $s)
2403
- // PSEUDO CLASSES
2404
- if ($s[0] == ':')
2405
- $this->pseudoClasses($s);
2406
- foreach($this->elements as $node)
2407
- // XXX it should be merged without duplicates
2408
- // but jQuery doesnt do that
2409
- $finalStack[] = $node;
2410
- $this->elements = $tmpStack;
2411
- }
2412
- $this->elements = $finalStack;
2413
- if ($_skipHistory) {
2414
- return $this;
2415
- } else {
2416
- $this->debug("Stack length after filter(): ".count($finalStack));
2417
- return $this->newInstance();
2418
- }
2419
- }
2420
- /**
2421
- *
2422
- * @param $value
2423
- * @return unknown_type
2424
- * @TODO implement in all methods using passed parameters
2425
- */
2426
- protected static function unQuote($value) {
2427
- return $value[0] == '\'' || $value[0] == '"'
2428
- ? substr($value, 1, -1)
2429
- : $value;
2430
- }
2431
- /**
2432
- * Enter description here...
2433
- *
2434
- * @link http://docs.jquery.com/Ajax/load
2435
- * @return phpQuery|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2436
- * @todo Support $selector
2437
- */
2438
- public function load($url, $data = null, $callback = null) {
2439
- if ($data && ! is_array($data)) {
2440
- $callback = $data;
2441
- $data = null;
2442
- }
2443
- if (mb_strpos($url, ' ') !== false) {
2444
- $matches = null;
2445
- if (extension_loaded('mbstring') && phpQuery::$mbstringSupport)
2446
- mb_ereg('^([^ ]+) (.*)$', $url, $matches);
2447
- else
2448
- preg_match('^([^ ]+) (.*)$', $url, $matches);
2449
- $url = $matches[1];
2450
- $selector = $matches[2];
2451
- // FIXME this sucks, pass as callback param
2452
- $this->_loadSelector = $selector;
2453
- }
2454
- $ajax = array(
2455
- 'url' => $url,
2456
- 'type' => $data ? 'POST' : 'GET',
2457
- 'data' => $data,
2458
- 'complete' => $callback,
2459
- 'success' => array($this, '__loadSuccess')
2460
- );
2461
- phpQuery::ajax($ajax);
2462
- return $this;
2463
- }
2464
- /**
2465
- * @access private
2466
- * @param $html
2467
- * @return unknown_type
2468
- */
2469
- public function __loadSuccess($html) {
2470
- if ($this->_loadSelector) {
2471
- $html = phpQuery::newDocument($html)->find($this->_loadSelector);
2472
- unset($this->_loadSelector);
2473
- }
2474
- foreach($this->stack(1) as $node) {
2475
- phpQuery::pq($node, $this->getDocumentID())
2476
- ->markup($html);
2477
- }
2478
- }
2479
- /**
2480
- * Enter description here...
2481
- *
2482
- * @return phpQuery|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2483
- * @todo
2484
- */
2485
- public function css() {
2486
- // TODO
2487
- return $this;
2488
- }
2489
- /**
2490
- * @todo
2491
- *
2492
- */
2493
- public function show(){
2494
- // TODO
2495
- return $this;
2496
- }
2497
- /**
2498
- * @todo
2499
- *
2500
- */
2501
- public function hide(){
2502
- // TODO
2503
- return $this;
2504
- }
2505
- /**
2506
- * Trigger a type of event on every matched element.
2507
- *
2508
- * @param unknown_type $type
2509
- * @param unknown_type $data
2510
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2511
- * @TODO support more than event in $type (space-separated)
2512
- */
2513
- public function trigger($type, $data = array()) {
2514
- foreach($this->elements as $node)
2515
- phpQueryEvents::trigger($this->getDocumentID(), $type, $data, $node);
2516
- return $this;
2517
- }
2518
- /**
2519
- * This particular method triggers all bound event handlers on an element (for a specific event type) WITHOUT executing the browsers default actions.
2520
- *
2521
- * @param unknown_type $type
2522
- * @param unknown_type $data
2523
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2524
- * @TODO
2525
- */
2526
- public function triggerHandler($type, $data = array()) {
2527
- // TODO;
2528
- }
2529
- /**
2530
- * Binds a handler to one or more events (like click) for each matched element.
2531
- * Can also bind custom events.
2532
- *
2533
- * @param unknown_type $type
2534
- * @param unknown_type $data Optional
2535
- * @param unknown_type $callback
2536
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2537
- * @TODO support '!' (exclusive) events
2538
- * @TODO support more than event in $type (space-separated)
2539
- */
2540
- public function bind($type, $data, $callback = null) {
2541
- // TODO check if $data is callable, not using is_callable
2542
- if (! isset($callback)) {
2543
- $callback = $data;
2544
- $data = null;
2545
- }
2546
- foreach($this->elements as $node)
2547
- phpQueryEvents::add($this->getDocumentID(), $node, $type, $data, $callback);
2548
- return $this;
2549
- }
2550
- /**
2551
- * Enter description here...
2552
- *
2553
- * @param unknown_type $type
2554
- * @param unknown_type $callback
2555
- * @return unknown
2556
- * @TODO namespace events
2557
- * @TODO support more than event in $type (space-separated)
2558
- */
2559
- public function unbind($type = null, $callback = null) {
2560
- foreach($this->elements as $node)
2561
- phpQueryEvents::remove($this->getDocumentID(), $node, $type, $callback);
2562
- return $this;
2563
- }
2564
- /**
2565
- * Enter description here...
2566
- *
2567
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2568
- */
2569
- public function change($callback = null) {
2570
- if ($callback)
2571
- return $this->bind('change', $callback);
2572
- return $this->trigger('change');
2573
- }
2574
- /**
2575
- * Enter description here...
2576
- *
2577
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2578
- */
2579
- public function submit($callback = null) {
2580
- if ($callback)
2581
- return $this->bind('submit', $callback);
2582
- return $this->trigger('submit');
2583
- }
2584
- /**
2585
- * Enter description here...
2586
- *
2587
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2588
- */
2589
- public function click($callback = null) {
2590
- if ($callback)
2591
- return $this->bind('click', $callback);
2592
- return $this->trigger('click');
2593
- }
2594
- /**
2595
- * Enter description here...
2596
- *
2597
- * @param String|phpQuery
2598
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2599
- */
2600
- public function wrapAllOld($wrapper) {
2601
- $wrapper = pq($wrapper)->_clone();
2602
- if (! $wrapper->length() || ! $this->length() )
2603
- return $this;
2604
- $wrapper->insertBefore($this->elements[0]);
2605
- $deepest = $wrapper->elements[0];
2606
- while($deepest->firstChild && $deepest->firstChild instanceof DOMELEMENT)
2607
- $deepest = $deepest->firstChild;
2608
- pq($deepest)->append($this);
2609
- return $this;
2610
- }
2611
- /**
2612
- * Enter description here...
2613
- *
2614
- * TODO testme...
2615
- * @param String|phpQuery
2616
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2617
- */
2618
- public function wrapAll($wrapper) {
2619
- if (! $this->length())
2620
- return $this;
2621
- return phpQuery::pq($wrapper, $this->getDocumentID())
2622
- ->clone()
2623
- ->insertBefore($this->get(0))
2624
- ->map(array($this, '___wrapAllCallback'))
2625
- ->append($this);
2626
- }
2627
- /**
2628
- *
2629
- * @param $node
2630
- * @return unknown_type
2631
- * @access private
2632
- */
2633
- public function ___wrapAllCallback($node) {
2634
- $deepest = $node;
2635
- while($deepest->firstChild && $deepest->firstChild instanceof DOMELEMENT)
2636
- $deepest = $deepest->firstChild;
2637
- return $deepest;
2638
- }
2639
- /**
2640
- * Enter description here...
2641
- * NON JQUERY METHOD
2642
- *
2643
- * @param String|phpQuery
2644
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2645
- */
2646
- public function wrapAllPHP($codeBefore, $codeAfter) {
2647
- return $this
2648
- ->slice(0, 1)
2649
- ->beforePHP($codeBefore)
2650
- ->end()
2651
- ->slice(-1)
2652
- ->afterPHP($codeAfter)
2653
- ->end();
2654
- }
2655
- /**
2656
- * Enter description here...
2657
- *
2658
- * @param String|phpQuery
2659
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2660
- */
2661
- public function wrap($wrapper) {
2662
- foreach($this->stack() as $node)
2663
- phpQuery::pq($node, $this->getDocumentID())->wrapAll($wrapper);
2664
- return $this;
2665
- }
2666
- /**
2667
- * Enter description here...
2668
- *
2669
- * @param String|phpQuery
2670
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2671
- */
2672
- public function wrapPHP($codeBefore, $codeAfter) {
2673
- foreach($this->stack() as $node)
2674
- phpQuery::pq($node, $this->getDocumentID())->wrapAllPHP($codeBefore, $codeAfter);
2675
- return $this;
2676
- }
2677
- /**
2678
- * Enter description here...
2679
- *
2680
- * @param String|phpQuery
2681
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2682
- */
2683
- public function wrapInner($wrapper) {
2684
- foreach($this->stack() as $node)
2685
- phpQuery::pq($node, $this->getDocumentID())->contents()->wrapAll($wrapper);
2686
- return $this;
2687
- }
2688
- /**
2689
- * Enter description here...
2690
- *
2691
- * @param String|phpQuery
2692
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2693
- */
2694
- public function wrapInnerPHP($codeBefore, $codeAfter) {
2695
- foreach($this->stack(1) as $node)
2696
- phpQuery::pq($node, $this->getDocumentID())->contents()
2697
- ->wrapAllPHP($codeBefore, $codeAfter);
2698
- return $this;
2699
- }
2700
- /**
2701
- * Enter description here...
2702
- *
2703
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2704
- * @testme Support for text nodes
2705
- */
2706
- public function contents() {
2707
- $stack = array();
2708
- foreach($this->stack(1) as $el) {
2709
- // FIXME (fixed) http://code.google.com/p/phpquery/issues/detail?id=56
2710
- // if (! isset($el->childNodes))
2711
- // continue;
2712
- foreach($el->childNodes as $node) {
2713
- $stack[] = $node;
2714
- }
2715
- }
2716
- return $this->newInstance($stack);
2717
- }
2718
- /**
2719
- * Enter description here...
2720
- *
2721
- * jQuery difference.
2722
- *
2723
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2724
- */
2725
- public function contentsUnwrap() {
2726
- foreach($this->stack(1) as $node) {
2727
- if (! $node->parentNode )
2728
- continue;
2729
- $childNodes = array();
2730
- // any modification in DOM tree breaks childNodes iteration, so cache them first
2731
- foreach($node->childNodes as $chNode )
2732
- $childNodes[] = $chNode;
2733
- foreach($childNodes as $chNode )
2734
- // $node->parentNode->appendChild($chNode);
2735
- $node->parentNode->insertBefore($chNode, $node);
2736
- $node->parentNode->removeChild($node);
2737
- }
2738
- return $this;
2739
- }
2740
- /**
2741
- * Enter description here...
2742
- *
2743
- * jQuery difference.
2744
- */
2745
- public function switchWith($markup) {
2746
- $markup = pq($markup, $this->getDocumentID());
2747
- $content = null;
2748
- foreach($this->stack(1) as $node) {
2749
- pq($node)
2750
- ->contents()->toReference($content)->end()
2751
- ->replaceWith($markup->clone()->append($content));
2752
- }
2753
- return $this;
2754
- }
2755
- /**
2756
- * Enter description here...
2757
- *
2758
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2759
- */
2760
- public function eq($num) {
2761
- $oldStack = $this->elements;
2762
- $this->elementsBackup = $this->elements;
2763
- $this->elements = array();
2764
- if ( isset($oldStack[$num]) )
2765
- $this->elements[] = $oldStack[$num];
2766
- return $this->newInstance();
2767
- }
2768
- /**
2769
- * Enter description here...
2770
- *
2771
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2772
- */
2773
- public function size() {
2774
- return count($this->elements);
2775
- }
2776
- /**
2777
- * Enter description here...
2778
- *
2779
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2780
- * @deprecated Use length as attribute
2781
- */
2782
- public function length() {
2783
- return $this->size();
2784
- }
2785
- public function count() {
2786
- return $this->size();
2787
- }
2788
- /**
2789
- * Enter description here...
2790
- *
2791
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2792
- * @todo $level
2793
- */
2794
- public function end($level = 1) {
2795
- // $this->elements = array_pop( $this->history );
2796
- // return $this;
2797
- // $this->previous->DOM = $this->DOM;
2798
- // $this->previous->XPath = $this->XPath;
2799
- return $this->previous
2800
- ? $this->previous
2801
- : $this;
2802
- }
2803
- /**
2804
- * Enter description here...
2805
- * Normal use ->clone() .
2806
- *
2807
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2808
- * @access private
2809
- */
2810
- public function _clone() {
2811
- $newStack = array();
2812
- //pr(array('copy... ', $this->whois()));
2813
- //$this->dumpHistory('copy');
2814
- $this->elementsBackup = $this->elements;
2815
- foreach($this->elements as $node) {
2816
- $newStack[] = $node->cloneNode(true);
2817
- }
2818
- $this->elements = $newStack;
2819
- return $this->newInstance();
2820
- }
2821
- /**
2822
- * Enter description here...
2823
- *
2824
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2825
- */
2826
- public function replaceWithPHP($code) {
2827
- return $this->replaceWith(phpQuery::php($code));
2828
- }
2829
- /**
2830
- * Enter description here...
2831
- *
2832
- * @param String|phpQuery $content
2833
- * @link http://docs.jquery.com/Manipulation/replaceWith#content
2834
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2835
- */
2836
- public function replaceWith($content) {
2837
- return $this->after($content)->remove();
2838
- }
2839
- /**
2840
- * Enter description here...
2841
- *
2842
- * @param String $selector
2843
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2844
- * @todo this works ?
2845
- */
2846
- public function replaceAll($selector) {
2847
- foreach(phpQuery::pq($selector, $this->getDocumentID()) as $node)
2848
- phpQuery::pq($node, $this->getDocumentID())
2849
- ->after($this->_clone())
2850
- ->remove();
2851
- return $this;
2852
- }
2853
- /**
2854
- * Enter description here...
2855
- *
2856
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2857
- */
2858
- public function remove($selector = null) {
2859
- $loop = $selector
2860
- ? $this->filter($selector)->elements
2861
- : $this->elements;
2862
- foreach($loop as $node) {
2863
- if (! $node->parentNode )
2864
- continue;
2865
- if (isset($node->tagName))
2866
- $this->debug("Removing '{$node->tagName}'");
2867
- $node->parentNode->removeChild($node);
2868
- // Mutation event
2869
- $event = new DOMEvent(array(
2870
- 'target' => $node,
2871
- 'type' => 'DOMNodeRemoved'
2872
- ));
2873
- phpQueryEvents::trigger($this->getDocumentID(),
2874
- $event->type, array($event), $node
2875
- );
2876
- }
2877
- return $this;
2878
- }
2879
- protected function markupEvents($newMarkup, $oldMarkup, $node) {
2880
- if ($node->tagName == 'textarea' && $newMarkup != $oldMarkup) {
2881
- $event = new DOMEvent(array(
2882
- 'target' => $node,
2883
- 'type' => 'change'
2884
- ));
2885
- phpQueryEvents::trigger($this->getDocumentID(),
2886
- $event->type, array($event), $node
2887
- );
2888
- }
2889
- }
2890
- /**
2891
- * jQuey difference
2892
- *
2893
- * @param $markup
2894
- * @return unknown_type
2895
- * @TODO trigger change event for textarea
2896
- */
2897
- public function markup($markup = null, $callback1 = null, $callback2 = null, $callback3 = null) {
2898
- $args = func_get_args();
2899
- if ($this->documentWrapper->isXML)
2900
- return call_user_func_array(array($this, 'xml'), $args);
2901
- else
2902
- return call_user_func_array(array($this, 'html'), $args);
2903
- }
2904
- /**
2905
- * jQuey difference
2906
- *
2907
- * @param $markup
2908
- * @return unknown_type
2909
- */
2910
- public function markupOuter($callback1 = null, $callback2 = null, $callback3 = null) {
2911
- $args = func_get_args();
2912
- if ($this->documentWrapper->isXML)
2913
- return call_user_func_array(array($this, 'xmlOuter'), $args);
2914
- else
2915
- return call_user_func_array(array($this, 'htmlOuter'), $args);
2916
- }
2917
- /**
2918
- * Enter description here...
2919
- *
2920
- * @param unknown_type $html
2921
- * @return string|phpQuery|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2922
- * @TODO force html result
2923
- */
2924
- public function html($html = null, $callback1 = null, $callback2 = null, $callback3 = null) {
2925
- if (isset($html)) {
2926
- // INSERT
2927
- $nodes = $this->documentWrapper->import($html);
2928
- $this->empty();
2929
- foreach($this->stack(1) as $alreadyAdded => $node) {
2930
- // for now, limit events for textarea
2931
- if (($this->isXHTML() || $this->isHTML()) && $node->tagName == 'textarea')
2932
- $oldHtml = pq($node, $this->getDocumentID())->markup();
2933
- foreach($nodes as $newNode) {
2934
- $node->appendChild($alreadyAdded
2935
- ? $newNode->cloneNode(true)
2936
- : $newNode
2937
- );
2938
- }
2939
- // for now, limit events for textarea
2940
- if (($this->isXHTML() || $this->isHTML()) && $node->tagName == 'textarea')
2941
- $this->markupEvents($html, $oldHtml, $node);
2942
- }
2943
- return $this;
2944
- } else {
2945
- // FETCH
2946
- $return = $this->documentWrapper->markup($this->elements, true);
2947
- $args = func_get_args();
2948
- foreach(array_slice($args, 1) as $callback) {
2949
- $return = phpQuery::callbackRun($callback, array($return));
2950
- }
2951
- return $return;
2952
- }
2953
- }
2954
- /**
2955
- * @TODO force xml result
2956
- */
2957
- public function xml($xml = null, $callback1 = null, $callback2 = null, $callback3 = null) {
2958
- $args = func_get_args();
2959
- return call_user_func_array(array($this, 'html'), $args);
2960
- }
2961
- /**
2962
- * Enter description here...
2963
- * @TODO force html result
2964
- *
2965
- * @return String
2966
- */
2967
- public function htmlOuter($callback1 = null, $callback2 = null, $callback3 = null) {
2968
- $markup = $this->documentWrapper->markup($this->elements);
2969
- // pass thou callbacks
2970
- $args = func_get_args();
2971
- foreach($args as $callback) {
2972
- $markup = phpQuery::callbackRun($callback, array($markup));
2973
- }
2974
- return $markup;
2975
- }
2976
- /**
2977
- * @TODO force xml result
2978
- */
2979
- public function xmlOuter($callback1 = null, $callback2 = null, $callback3 = null) {
2980
- $args = func_get_args();
2981
- return call_user_func_array(array($this, 'htmlOuter'), $args);
2982
- }
2983
- public function __toString() {
2984
- return $this->markupOuter();
2985
- }
2986
- /**
2987
- * Just like html(), but returns markup with VALID (dangerous) PHP tags.
2988
- *
2989
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2990
- * @todo support returning markup with PHP tags when called without param
2991
- */
2992
- public function php($code = null) {
2993
- return $this->markupPHP($code);
2994
- }
2995
- /**
2996
- * Enter description here...
2997
- *
2998
- * @param $code
2999
- * @return unknown_type
3000
- */
3001
- public function markupPHP($code = null) {
3002
- return isset($code)
3003
- ? $this->markup(phpQuery::php($code))
3004
- : phpQuery::markupToPHP($this->markup());
3005
- }
3006
- /**
3007
- * Enter description here...
3008
- *
3009
- * @param $code
3010
- * @return unknown_type
3011
- */
3012
- public function markupOuterPHP() {
3013
- return phpQuery::markupToPHP($this->markupOuter());
3014
- }
3015
- /**
3016
- * Enter description here...
3017
- *
3018
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3019
- */
3020
- public function children($selector = null) {
3021
- $stack = array();
3022
- foreach($this->stack(1) as $node) {
3023
- // foreach($node->getElementsByTagName('*') as $newNode) {
3024
- foreach($node->childNodes as $newNode) {
3025
- if ($newNode->nodeType != 1)
3026
- continue;
3027
- if ($selector && ! $this->is($selector, $newNode))
3028
- continue;
3029
- if ($this->elementsContainsNode($newNode, $stack))
3030
- continue;
3031
- $stack[] = $newNode;
3032
- }
3033
- }
3034
- $this->elementsBackup = $this->elements;
3035
- $this->elements = $stack;
3036
- return $this->newInstance();
3037
- }
3038
- /**
3039
- * Enter description here...
3040
- *
3041
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3042
- */
3043
- public function ancestors($selector = null) {
3044
- return $this->children( $selector );
3045
- }
3046
- /**
3047
- * Enter description here...
3048
- *
3049
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3050
- */
3051
- public function append( $content) {
3052
- return $this->insert($content, __FUNCTION__);
3053
- }
3054
- /**
3055
- * Enter description here...
3056
- *
3057
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3058
- */
3059
- public function appendPHP( $content) {
3060
- return $this->insert("<php><!-- {$content} --></php>", 'append');
3061
- }
3062
- /**
3063
- * Enter description here...
3064
- *
3065
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3066
- */
3067
- public function appendTo( $seletor) {
3068
- return $this->insert($seletor, __FUNCTION__);
3069
- }
3070
- /**
3071
- * Enter description here...
3072
- *
3073
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3074
- */
3075
- public function prepend( $content) {
3076
- return $this->insert($content, __FUNCTION__);
3077
- }
3078
- /**
3079
- * Enter description here...
3080
- *
3081
- * @todo accept many arguments, which are joined, arrays maybe also
3082
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3083
- */
3084
- public function prependPHP( $content) {
3085
- return $this->insert("<php><!-- {$content} --></php>", 'prepend');
3086
- }
3087
- /**
3088
- * Enter description here...
3089
- *
3090
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3091
- */
3092
- public function prependTo( $seletor) {
3093
- return $this->insert($seletor, __FUNCTION__);
3094
- }
3095
- /**
3096
- * Enter description here...
3097
- *
3098
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3099
- */
3100
- public function before($content) {
3101
- return $this->insert($content, __FUNCTION__);
3102
- }
3103
- /**
3104
- * Enter description here...
3105
- *
3106
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3107
- */
3108
- public function beforePHP( $content) {
3109
- return $this->insert("<php><!-- {$content} --></php>", 'before');
3110
- }
3111
- /**
3112
- * Enter description here...
3113
- *
3114
- * @param String|phpQuery
3115
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3116
- */
3117
- public function insertBefore( $seletor) {
3118
- return $this->insert($seletor, __FUNCTION__);
3119
- }
3120
- /**
3121
- * Enter description here...
3122
- *
3123
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3124
- */
3125
- public function after( $content) {
3126
- return $this->insert($content, __FUNCTION__);
3127
- }
3128
- /**
3129
- * Enter description here...
3130
- *
3131
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3132
- */
3133
- public function afterPHP( $content) {
3134
- return $this->insert("<php><!-- {$content} --></php>", 'after');
3135
- }
3136
- /**
3137
- * Enter description here...
3138
- *
3139
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3140
- */
3141
- public function insertAfter( $seletor) {
3142
- return $this->insert($seletor, __FUNCTION__);
3143
- }
3144
- /**
3145
- * Internal insert method. Don't use it.
3146
- *
3147
- * @param unknown_type $target
3148
- * @param unknown_type $type
3149
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3150
- * @access private
3151
- */
3152
- public function insert($target, $type) {
3153
- $this->debug("Inserting data with '{$type}'");
3154
- $to = false;
3155
- switch( $type) {
3156
- case 'appendTo':
3157
- case 'prependTo':
3158
- case 'insertBefore':
3159
- case 'insertAfter':
3160
- $to = true;
3161
- }
3162
- switch(gettype($target)) {
3163
- case 'string':
3164
- $insertFrom = $insertTo = array();
3165
- if ($to) {
3166
- // INSERT TO
3167
- $insertFrom = $this->elements;
3168
- if (phpQuery::isMarkup($target)) {
3169
- // $target is new markup, import it
3170
- $insertTo = $this->documentWrapper->import($target);
3171
- // insert into selected element
3172
- } else {
3173
- // $tagret is a selector
3174
- $thisStack = $this->elements;
3175
- $this->toRoot();
3176
- $insertTo = $this->find($target)->elements;
3177
- $this->elements = $thisStack;
3178
- }
3179
- } else {
3180
- // INSERT FROM
3181
- $insertTo = $this->elements;
3182
- $insertFrom = $this->documentWrapper->import($target);
3183
- }
3184
- break;
3185
- case 'object':
3186
- $insertFrom = $insertTo = array();
3187
- // phpQuery
3188
- if ($target instanceof self) {
3189
- if ($to) {
3190
- $insertTo = $target->elements;
3191
- if ($this->documentFragment && $this->stackIsRoot())
3192
- // get all body children
3193
- // $loop = $this->find('body > *')->elements;
3194
- // TODO test it, test it hard...
3195
- // $loop = $this->newInstance($this->root)->find('> *')->elements;
3196
- $loop = $this->root->childNodes;
3197
- else
3198
- $loop = $this->elements;
3199
- // import nodes if needed
3200
- $insertFrom = $this->getDocumentID() == $target->getDocumentID()
3201
- ? $loop
3202
- : $target->documentWrapper->import($loop);
3203
- } else {
3204
- $insertTo = $this->elements;
3205
- if ( $target->documentFragment && $target->stackIsRoot() )
3206
- // get all body children
3207
- // $loop = $target->find('body > *')->elements;
3208
- $loop = $target->root->childNodes;
3209
- else
3210
- $loop = $target->elements;
3211
- // import nodes if needed
3212
- $insertFrom = $this->getDocumentID() == $target->getDocumentID()
3213
- ? $loop
3214
- : $this->documentWrapper->import($loop);
3215
- }
3216
- // DOMNODE
3217
- } elseif ($target instanceof DOMNODE) {
3218
- // import node if needed
3219
- // if ( $target->ownerDocument != $this->DOM )
3220
- // $target = $this->DOM->importNode($target, true);
3221
- if ( $to) {
3222
- $insertTo = array($target);
3223
- if ($this->documentFragment && $this->stackIsRoot())
3224
- // get all body children
3225
- $loop = $this->root->childNodes;
3226
- // $loop = $this->find('body > *')->elements;
3227
- else
3228
- $loop = $this->elements;
3229
- foreach($loop as $fromNode)
3230
- // import nodes if needed
3231
- $insertFrom[] = ! $fromNode->ownerDocument->isSameNode($target->ownerDocument)
3232
- ? $target->ownerDocument->importNode($fromNode, true)
3233
- : $fromNode;
3234
- } else {
3235
- // import node if needed
3236
- if (! $target->ownerDocument->isSameNode($this->document))
3237
- $target = $this->document->importNode($target, true);
3238
- $insertTo = $this->elements;
3239
- $insertFrom[] = $target;
3240
- }
3241
- }
3242
- break;
3243
- }
3244
- phpQuery::debug("From ".count($insertFrom)."; To ".count($insertTo)." nodes");
3245
- foreach($insertTo as $insertNumber => $toNode) {
3246
- // we need static relative elements in some cases
3247
- switch( $type) {
3248
- case 'prependTo':
3249
- case 'prepend':
3250
- $firstChild = $toNode->firstChild;
3251
- break;
3252
- case 'insertAfter':
3253
- case 'after':
3254
- $nextSibling = $toNode->nextSibling;
3255
- break;
3256
- }
3257
- foreach($insertFrom as $fromNode) {
3258
- // clone if inserted already before
3259
- $insert = $insertNumber
3260
- ? $fromNode->cloneNode(true)
3261
- : $fromNode;
3262
- switch($type) {
3263
- case 'appendTo':
3264
- case 'append':
3265
- // $toNode->insertBefore(
3266
- // $fromNode,
3267
- // $toNode->lastChild->nextSibling
3268
- // );
3269
- $toNode->appendChild($insert);
3270
- $eventTarget = $insert;
3271
- break;
3272
- case 'prependTo':
3273
- case 'prepend':
3274
- $toNode->insertBefore(
3275
- $insert,
3276
- $firstChild
3277
- );
3278
- break;
3279
- case 'insertBefore':
3280
- case 'before':
3281
- if (! $toNode->parentNode)
3282
- throw new Exception("No parentNode, can't do {$type}()");
3283
- else
3284
- $toNode->parentNode->insertBefore(
3285
- $insert,
3286
- $toNode
3287
- );
3288
- break;
3289
- case 'insertAfter':
3290
- case 'after':
3291
- if (! $toNode->parentNode)
3292
- throw new Exception("No parentNode, can't do {$type}()");
3293
- else
3294
- $toNode->parentNode->insertBefore(
3295
- $insert,
3296
- $nextSibling
3297
- );
3298
- break;
3299
- }
3300
- // Mutation event
3301
- $event = new DOMEvent(array(
3302
- 'target' => $insert,
3303
- 'type' => 'DOMNodeInserted'
3304
- ));
3305
- phpQueryEvents::trigger($this->getDocumentID(),
3306
- $event->type, array($event), $insert
3307
- );
3308
- }
3309
- }
3310
- return $this;
3311
- }
3312
- /**
3313
- * Enter description here...
3314
- *
3315
- * @return Int
3316
- */
3317
- public function index($subject) {
3318
- $index = -1;
3319
- $subject = $subject instanceof phpQueryObject
3320
- ? $subject->elements[0]
3321
- : $subject;
3322
- foreach($this->newInstance() as $k => $node) {
3323
- if ($node->isSameNode($subject))
3324
- $index = $k;
3325
- }
3326
- return $index;
3327
- }
3328
- /**
3329
- * Enter description here...
3330
- *
3331
- * @param unknown_type $start
3332
- * @param unknown_type $end
3333
- *
3334
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3335
- * @testme
3336
- */
3337
- public function slice($start, $end = null) {
3338
- // $last = count($this->elements)-1;
3339
- // $end = $end
3340
- // ? min($end, $last)
3341
- // : $last;
3342
- // if ($start < 0)
3343
- // $start = $last+$start;
3344
- // if ($start > $last)
3345
- // return array();
3346
- if ($end > 0)
3347
- $end = $end-$start;
3348
- return $this->newInstance(
3349
- array_slice($this->elements, $start, $end)
3350
- );
3351
- }
3352
- /**
3353
- * Enter description here...
3354
- *
3355
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3356
- */
3357
- public function reverse() {
3358
- $this->elementsBackup = $this->elements;
3359
- $this->elements = array_reverse($this->elements);
3360
- return $this->newInstance();
3361
- }
3362
- /**
3363
- * Return joined text content.
3364
- * @return String
3365
- */
3366
- public function text($text = null, $callback1 = null, $callback2 = null, $callback3 = null) {
3367
- if (isset($text))
3368
- return $this->html(htmlspecialchars($text));
3369
- $args = func_get_args();
3370
- $args = array_slice($args, 1);
3371
- $return = '';
3372
- foreach($this->elements as $node) {
3373
- $text = $node->textContent;
3374
- if (count($this->elements) > 1 && $text)
3375
- $text .= "\n";
3376
- foreach($args as $callback) {
3377
- $text = phpQuery::callbackRun($callback, array($text));
3378
- }
3379
- $return .= $text;
3380
- }
3381
- return $return;
3382
- }
3383
- /**
3384
- * Enter description here...
3385
- *
3386
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3387
- */
3388
- public function plugin($class, $file = null) {
3389
- phpQuery::plugin($class, $file);
3390
- return $this;
3391
- }
3392
- /**
3393
- * Deprecated, use $pq->plugin() instead.
3394
- *
3395
- * @deprecated
3396
- * @param $class
3397
- * @param $file
3398
- * @return unknown_type
3399
- */
3400
- public static function extend($class, $file = null) {
3401
- return $this->plugin($class, $file);
3402
- }
3403
- /**
3404
- *
3405
- * @access private
3406
- * @param $method
3407
- * @param $args
3408
- * @return unknown_type
3409
- */
3410
- public function __call($method, $args) {
3411
- $aliasMethods = array('clone', 'empty');
3412
- if (isset(phpQuery::$extendMethods[$method])) {
3413
- array_unshift($args, $this);
3414
- return phpQuery::callbackRun(
3415
- phpQuery::$extendMethods[$method], $args
3416
- );
3417
- } else if (isset(phpQuery::$pluginsMethods[$method])) {
3418
- array_unshift($args, $this);
3419
- $class = phpQuery::$pluginsMethods[$method];
3420
- $realClass = "phpQueryObjectPlugin_$class";
3421
- $return = call_user_func_array(
3422
- array($realClass, $method),
3423
- $args
3424
- );
3425
- // XXX deprecate ?
3426
- return is_null($return)
3427
- ? $this
3428
- : $return;
3429
- } else if (in_array($method, $aliasMethods)) {
3430
- return call_user_func_array(array($this, '_'.$method), $args);
3431
- } else
3432
- throw new Exception("Method '{$method}' doesnt exist");
3433
- }
3434
- /**
3435
- * Safe rename of next().
3436
- *
3437
- * Use it ONLY when need to call next() on an iterated object (in same time).
3438
- * Normaly there is no need to do such thing ;)
3439
- *
3440
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3441
- * @access private
3442
- */
3443
- public function _next($selector = null) {
3444
- return $this->newInstance(
3445
- $this->getElementSiblings('nextSibling', $selector, true)
3446
- );
3447
- }
3448
- /**
3449
- * Use prev() and next().
3450
- *
3451
- * @deprecated
3452
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3453
- * @access private
3454
- */
3455
- public function _prev($selector = null) {
3456
- return $this->prev($selector);
3457
- }
3458
- /**
3459
- * Enter description here...
3460
- *
3461
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3462
- */
3463
- public function prev($selector = null) {
3464
- return $this->newInstance(
3465
- $this->getElementSiblings('previousSibling', $selector, true)
3466
- );
3467
- }
3468
- /**
3469
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3470
- * @todo
3471
- */
3472
- public function prevAll($selector = null) {
3473
- return $this->newInstance(
3474
- $this->getElementSiblings('previousSibling', $selector)
3475
- );
3476
- }
3477
- /**
3478
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3479
- * @todo FIXME: returns source elements insted of next siblings
3480
- */
3481
- public function nextAll($selector = null) {
3482
- return $this->newInstance(
3483
- $this->getElementSiblings('nextSibling', $selector)
3484
- );
3485
- }
3486
- /**
3487
- * @access private
3488
- */
3489
- protected function getElementSiblings($direction, $selector = null, $limitToOne = false) {
3490
- $stack = array();
3491
- $count = 0;
3492
- foreach($this->stack() as $node) {
3493
- $test = $node;
3494
- while( isset($test->{$direction}) && $test->{$direction}) {
3495
- $test = $test->{$direction};
3496
- if (! $test instanceof DOMELEMENT)
3497
- continue;
3498
- $stack[] = $test;
3499
- if ($limitToOne)
3500
- break;
3501
- }
3502
- }
3503
- if ($selector) {
3504
- $stackOld = $this->elements;
3505
- $this->elements = $stack;
3506
- $stack = $this->filter($selector, true)->stack();
3507
- $this->elements = $stackOld;
3508
- }
3509
- return $stack;
3510
- }
3511
- /**
3512
- * Enter description here...
3513
- *
3514
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3515
- */
3516
- public function siblings($selector = null) {
3517
- $stack = array();
3518
- $siblings = array_merge(
3519
- $this->getElementSiblings('previousSibling', $selector),
3520
- $this->getElementSiblings('nextSibling', $selector)
3521
- );
3522
- foreach($siblings as $node) {
3523
- if (! $this->elementsContainsNode($node, $stack))
3524
- $stack[] = $node;
3525
- }
3526
- return $this->newInstance($stack);
3527
- }
3528
- /**
3529
- * Enter description here...
3530
- *
3531
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3532
- */
3533
- public function not($selector = null) {
3534
- if (is_string($selector))
3535
- phpQuery::debug(array('not', $selector));
3536
- else
3537
- phpQuery::debug('not');
3538
- $stack = array();
3539
- if ($selector instanceof self || $selector instanceof DOMNODE) {
3540
- foreach($this->stack() as $node) {
3541
- if ($selector instanceof self) {
3542
- $matchFound = false;
3543
- foreach($selector->stack() as $notNode) {
3544
- if ($notNode->isSameNode($node))
3545
- $matchFound = true;
3546
- }
3547
- if (! $matchFound)
3548
- $stack[] = $node;
3549
- } else if ($selector instanceof DOMNODE) {
3550
- if (! $selector->isSameNode($node))
3551
- $stack[] = $node;
3552
- } else {
3553
- if (! $this->is($selector))
3554
- $stack[] = $node;
3555
- }
3556
- }
3557
- } else {
3558
- $orgStack = $this->stack();
3559
- $matched = $this->filter($selector, true)->stack();
3560
- // $matched = array();
3561
- // // simulate OR in filter() instead of AND 5y
3562
- // foreach($this->parseSelector($selector) as $s) {
3563
- // $matched = array_merge($matched,
3564
- // $this->filter(array($s))->stack()
3565
- // );
3566
- // }
3567
- foreach($orgStack as $node)
3568
- if (! $this->elementsContainsNode($node, $matched))
3569
- $stack[] = $node;
3570
- }
3571
- return $this->newInstance($stack);
3572
- }
3573
- /**
3574
- * Enter description here...
3575
- *
3576
- * @param string|phpQueryObject
3577
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3578
- */
3579
- public function add($selector = null) {
3580
- if (! $selector)
3581
- return $this;
3582
- $stack = array();
3583
- $this->elementsBackup = $this->elements;
3584
- $found = phpQuery::pq($selector, $this->getDocumentID());
3585
- $this->merge($found->elements);
3586
- return $this->newInstance();
3587
- }
3588
- /**
3589
- * @access private
3590
- */
3591
- protected function merge() {
3592
- foreach(func_get_args() as $nodes)
3593
- foreach($nodes as $newNode )
3594
- if (! $this->elementsContainsNode($newNode) )
3595
- $this->elements[] = $newNode;
3596
- }
3597
- /**
3598
- * @access private
3599
- * TODO refactor to stackContainsNode
3600
- */
3601
- protected function elementsContainsNode($nodeToCheck, $elementsStack = null) {
3602
- $loop = ! is_null($elementsStack)
3603
- ? $elementsStack
3604
- : $this->elements;
3605
- foreach($loop as $node) {
3606
- if ( $node->isSameNode( $nodeToCheck ) )
3607
- return true;
3608
- }
3609
- return false;
3610
- }
3611
- /**
3612
- * Enter description here...
3613
- *
3614
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3615
- */
3616
- public function parent($selector = null) {
3617
- $stack = array();
3618
- foreach($this->elements as $node )
3619
- if ( $node->parentNode && ! $this->elementsContainsNode($node->parentNode, $stack) )
3620
- $stack[] = $node->parentNode;
3621
- $this->elementsBackup = $this->elements;
3622
- $this->elements = $stack;
3623
- if ( $selector )
3624
- $this->filter($selector, true);
3625
- return $this->newInstance();
3626
- }
3627
- /**
3628
- * Enter description here...
3629
- *
3630
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3631
- */
3632
- public function parents($selector = null) {
3633
- $stack = array();
3634
- if (! $this->elements )
3635
- $this->debug('parents() - stack empty');
3636
- foreach($this->elements as $node) {
3637
- $test = $node;
3638
- while( $test->parentNode) {
3639
- $test = $test->parentNode;
3640
- if ($this->isRoot($test))
3641
- break;
3642
- if (! $this->elementsContainsNode($test, $stack)) {
3643
- $stack[] = $test;
3644
- continue;
3645
- }
3646
- }
3647
- }
3648
- $this->elementsBackup = $this->elements;
3649
- $this->elements = $stack;
3650
- if ( $selector )
3651
- $this->filter($selector, true);
3652
- return $this->newInstance();
3653
- }
3654
- /**
3655
- * Internal stack iterator.
3656
- *
3657
- * @access private
3658
- */
3659
- public function stack($nodeTypes = null) {
3660
- if (!isset($nodeTypes))
3661
- return $this->elements;
3662
- if (!is_array($nodeTypes))
3663
- $nodeTypes = array($nodeTypes);
3664
- $return = array();
3665
- foreach($this->elements as $node) {
3666
- if (in_array($node->nodeType, $nodeTypes))
3667
- $return[] = $node;
3668
- }
3669
- return $return;
3670
- }
3671
- // TODO phpdoc; $oldAttr is result of hasAttribute, before any changes
3672
- protected function attrEvents($attr, $oldAttr, $oldValue, $node) {
3673
- // skip events for XML documents
3674
- if (! $this->isXHTML() && ! $this->isHTML())
3675
- return;
3676
- $event = null;
3677
- // identify
3678
- $isInputValue = $node->tagName == 'input'
3679
- && (
3680
- in_array($node->getAttribute('type'),
3681
- array('text', 'password', 'hidden'))
3682
- || !$node->getAttribute('type')
3683
- );
3684
- $isRadio = $node->tagName == 'input'
3685
- && $node->getAttribute('type') == 'radio';
3686
- $isCheckbox = $node->tagName == 'input'
3687
- && $node->getAttribute('type') == 'checkbox';
3688
- $isOption = $node->tagName == 'option';
3689
- if ($isInputValue && $attr == 'value' && $oldValue != $node->getAttribute($attr)) {
3690
- $event = new DOMEvent(array(
3691
- 'target' => $node,
3692
- 'type' => 'change'
3693
- ));
3694
- } else if (($isRadio || $isCheckbox) && $attr == 'checked' && (
3695
- // check
3696
- (! $oldAttr && $node->hasAttribute($attr))
3697
- // un-check
3698
- || (! $node->hasAttribute($attr) && $oldAttr)
3699
- )) {
3700
- $event = new DOMEvent(array(
3701
- 'target' => $node,
3702
- 'type' => 'change'
3703
- ));
3704
- } else if ($isOption && $node->parentNode && $attr == 'selected' && (
3705
- // select
3706
- (! $oldAttr && $node->hasAttribute($attr))
3707
- // un-select
3708
- || (! $node->hasAttribute($attr) && $oldAttr)
3709
- )) {
3710
- $event = new DOMEvent(array(
3711
- 'target' => $node->parentNode,
3712
- 'type' => 'change'
3713
- ));
3714
- }
3715
- if ($event) {
3716
- phpQueryEvents::trigger($this->getDocumentID(),
3717
- $event->type, array($event), $node
3718
- );
3719
- }
3720
- }
3721
- public function attr($attr = null, $value = null) {
3722
- foreach($this->stack(1) as $node) {
3723
- if (! is_null($value)) {
3724
- $loop = $attr == '*'
3725
- ? $this->getNodeAttrs($node)
3726
- : array($attr);
3727
- foreach($loop as $a) {
3728
- $oldValue = $node->getAttribute($a);
3729
- $oldAttr = $node->hasAttribute($a);
3730
- // TODO raises an error when charset other than UTF-8
3731
- // while document's charset is also not UTF-8
3732
- @$node->setAttribute($a, $value);
3733
- $this->attrEvents($a, $oldAttr, $oldValue, $node);
3734
- }
3735
- } else if ($attr == '*') {
3736
- // jQuery difference
3737
- $return = array();
3738
- foreach($node->attributes as $n => $v)
3739
- $return[$n] = $v->value;
3740
- return $return;
3741
- } else
3742
- return $node->hasAttribute($attr)
3743
- ? $node->getAttribute($attr)
3744
- : null;
3745
- }
3746
- return is_null($value)
3747
- ? '' : $this;
3748
- }
3749
- /**
3750
- * @access private
3751
- */
3752
- protected function getNodeAttrs($node) {
3753
- $return = array();
3754
- foreach($node->attributes as $n => $o)
3755
- $return[] = $n;
3756
- return $return;
3757
- }
3758
- /**
3759
- * Enter description here...
3760
- *
3761
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3762
- * @todo check CDATA ???
3763
- */
3764
- public function attrPHP($attr, $code) {
3765
- if (! is_null($code)) {
3766
- $value = '<'.'?php '.$code.' ?'.'>';
3767
- // TODO tempolary solution
3768
- // http://code.google.com/p/phpquery/issues/detail?id=17
3769
- // if (function_exists('mb_detect_encoding') && mb_detect_encoding($value) == 'ASCII')
3770
- // $value = mb_convert_encoding($value, 'UTF-8', 'HTML-ENTITIES');
3771
- }
3772
- foreach($this->stack(1) as $node) {
3773
- if (! is_null($code)) {
3774
- // $attrNode = $this->DOM->createAttribute($attr);
3775
- $node->setAttribute($attr, $value);
3776
- // $attrNode->value = $value;
3777
- // $node->appendChild($attrNode);
3778
- } else if ( $attr == '*') {
3779
- // jQuery diff
3780
- $return = array();
3781
- foreach($node->attributes as $n => $v)
3782
- $return[$n] = $v->value;
3783
- return $return;
3784
- } else
3785
- return $node->getAttribute($attr);
3786
- }
3787
- return $this;
3788
- }
3789
- /**
3790
- * Enter description here...
3791
- *
3792
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3793
- */
3794
- public function removeAttr($attr) {
3795
- foreach($this->stack(1) as $node) {
3796
- $loop = $attr == '*'
3797
- ? $this->getNodeAttrs($node)
3798
- : array($attr);
3799
- foreach($loop as $a) {
3800
- $oldValue = $node->getAttribute($a);
3801
- $node->removeAttribute($a);
3802
- $this->attrEvents($a, $oldValue, null, $node);
3803
- }
3804
- }
3805
- return $this;
3806
- }
3807
- /**
3808
- * Return form element value.
3809
- *
3810
- * @return String Fields value.
3811
- */
3812
- public function val($val = null) {
3813
- if (! isset($val)) {
3814
- if ($this->eq(0)->is('select')) {
3815
- $selected = $this->eq(0)->find('option[selected=selected]');
3816
- if ($selected->is('[value]'))
3817
- return $selected->attr('value');
3818
- else
3819
- return $selected->text();
3820
- } else if ($this->eq(0)->is('textarea'))
3821
- return $this->eq(0)->markup();
3822
- else
3823
- return $this->eq(0)->attr('value');
3824
- } else {
3825
- $_val = null;
3826
- foreach($this->stack(1) as $node) {
3827
- $node = pq($node, $this->getDocumentID());
3828
- if (is_array($val) && in_array($node->attr('type'), array('checkbox', 'radio'))) {
3829
- $isChecked = in_array($node->attr('value'), $val)
3830
- || in_array($node->attr('name'), $val);
3831
- if ($isChecked)
3832
- $node->attr('checked', 'checked');
3833
- else
3834
- $node->removeAttr('checked');
3835
- } else if ($node->get(0)->tagName == 'select') {
3836
- if (! isset($_val)) {
3837
- $_val = array();
3838
- if (! is_array($val))
3839
- $_val = array((string)$val);
3840
- else
3841
- foreach($val as $v)
3842
- $_val[] = $v;
3843
- }
3844
- foreach($node['option']->stack(1) as $option) {
3845
- $option = pq($option, $this->getDocumentID());
3846
- $selected = false;
3847
- // XXX: workaround for string comparsion, see issue #96
3848
- // http://code.google.com/p/phpquery/issues/detail?id=96
3849
- $selected = is_null($option->attr('value'))
3850
- ? in_array($option->markup(), $_val)
3851
- : in_array($option->attr('value'), $_val);
3852
- // $optionValue = $option->attr('value');
3853
- // $optionText = $option->text();
3854
- // $optionTextLenght = mb_strlen($optionText);
3855
- // foreach($_val as $v)
3856
- // if ($optionValue == $v)
3857
- // $selected = true;
3858
- // else if ($optionText == $v && $optionTextLenght == mb_strlen($v))
3859
- // $selected = true;
3860
- if ($selected)
3861
- $option->attr('selected', 'selected');
3862
- else
3863
- $option->removeAttr('selected');
3864
- }
3865
- } else if ($node->get(0)->tagName == 'textarea')
3866
- $node->markup($val);
3867
- else
3868
- $node->attr('value', $val);
3869
- }
3870
- }
3871
- return $this;
3872
- }
3873
- /**
3874
- * Enter description here...
3875
- *
3876
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3877
- */
3878
- public function andSelf() {
3879
- if ( $this->previous )
3880
- $this->elements = array_merge($this->elements, $this->previous->elements);
3881
- return $this;
3882
- }
3883
- /**
3884
- * Enter description here...
3885
- *
3886
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3887
- */
3888
- public function addClass( $className) {
3889
- if (! $className)
3890
- return $this;
3891
- foreach($this->stack(1) as $node) {
3892
- if (! $this->is(".$className", $node))
3893
- $node->setAttribute(
3894
- 'class',
3895
- trim($node->getAttribute('class').' '.$className)
3896
- );
3897
- }
3898
- return $this;
3899
- }
3900
- /**
3901
- * Enter description here...
3902
- *
3903
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3904
- */
3905
- public function addClassPHP( $className) {
3906
- foreach($this->stack(1) as $node) {
3907
- $classes = $node->getAttribute('class');
3908
- $newValue = $classes
3909
- ? $classes.' <'.'?php '.$className.' ?'.'>'
3910
- : '<'.'?php '.$className.' ?'.'>';
3911
- $node->setAttribute('class', $newValue);
3912
- }
3913
- return $this;
3914
- }
3915
- /**
3916
- * Enter description here...
3917
- *
3918
- * @param string $className
3919
- * @return bool
3920
- */
3921
- public function hasClass($className) {
3922
- foreach($this->stack(1) as $node) {
3923
- if ( $this->is(".$className", $node))
3924
- return true;
3925
- }
3926
- return false;
3927
- }
3928
- /**
3929
- * Enter description here...
3930
- *
3931
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3932
- */
3933
- public function removeClass($className) {
3934
- foreach($this->stack(1) as $node) {
3935
- $classes = explode( ' ', $node->getAttribute('class'));
3936
- if ( in_array($className, $classes)) {
3937
- $classes = array_diff($classes, array($className));
3938
- if ( $classes )
3939
- $node->setAttribute('class', implode(' ', $classes));
3940
- else
3941
- $node->removeAttribute('class');
3942
- }
3943
- }
3944
- return $this;
3945
- }
3946
- /**
3947
- * Enter description here...
3948
- *
3949
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3950
- */
3951
- public function toggleClass($className) {
3952
- foreach($this->stack(1) as $node) {
3953
- if ( $this->is( $node, '.'.$className ))
3954
- $this->removeClass($className);
3955
- else
3956
- $this->addClass($className);
3957
- }
3958
- return $this;
3959
- }
3960
- /**
3961
- * Proper name without underscore (just ->empty()) also works.
3962
- *
3963
- * Removes all child nodes from the set of matched elements.
3964
- *
3965
- * Example:
3966
- * pq("p")._empty()
3967
- *
3968
- * HTML:
3969
- * <p>Hello, <span>Person</span> <a href="#">and person</a></p>
3970
- *
3971
- * Result:
3972
- * [ <p></p> ]
3973
- *
3974
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3975
- * @access private
3976
- */
3977
- public function _empty() {
3978
- foreach($this->stack(1) as $node) {
3979
- // thx to 'dave at dgx dot cz'
3980
- $node->nodeValue = '';
3981
- }
3982
- return $this;
3983
- }
3984
- /**
3985
- * Enter description here...
3986
- *
3987
- * @param array|string $callback Expects $node as first param, $index as second
3988
- * @param array $scope External variables passed to callback. Use compact('varName1', 'varName2'...) and extract($scope)
3989
- * @param array $arg1 Will ba passed as third and futher args to callback.
3990
- * @param array $arg2 Will ba passed as fourth and futher args to callback, and so on...
3991
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3992
- */
3993
- public function each($callback, $param1 = null, $param2 = null, $param3 = null) {
3994
- $paramStructure = null;
3995
- if (func_num_args() > 1) {
3996
- $paramStructure = func_get_args();
3997
- $paramStructure = array_slice($paramStructure, 1);
3998
- }
3999
- foreach($this->elements as $v)
4000
- phpQuery::callbackRun($callback, array($v), $paramStructure);
4001
- return $this;
4002
- }
4003
- /**
4004
- * Run callback on actual object.
4005
- *
4006
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4007
- */
4008
- public function callback($callback, $param1 = null, $param2 = null, $param3 = null) {
4009
- $params = func_get_args();
4010
- $params[0] = $this;
4011
- phpQuery::callbackRun($callback, $params);
4012
- return $this;
4013
- }
4014
- /**
4015
- * Enter description here...
4016
- *
4017
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4018
- * @todo add $scope and $args as in each() ???
4019
- */
4020
- public function map($callback, $param1 = null, $param2 = null, $param3 = null) {
4021
- // $stack = array();
4022
- //// foreach($this->newInstance() as $node) {
4023
- // foreach($this->newInstance() as $node) {
4024
- // $result = call_user_func($callback, $node);
4025
- // if ($result)
4026
- // $stack[] = $result;
4027
- // }
4028
- $params = func_get_args();
4029
- array_unshift($params, $this->elements);
4030
- return $this->newInstance(
4031
- call_user_func_array(array('phpQuery', 'map'), $params)
4032
- // phpQuery::map($this->elements, $callback)
4033
- );
4034
- }
4035
- /**
4036
- * Enter description here...
4037
- *
4038
- * @param <type> $key
4039
- * @param <type> $value
4040
- */
4041
- public function data($key, $value = null) {
4042
- if (! isset($value)) {
4043
- // TODO? implement specific jQuery behavior od returning parent values
4044
- // is child which we look up doesn't exist
4045
- return phpQuery::data($this->get(0), $key, $value, $this->getDocumentID());
4046
- } else {
4047
- foreach($this as $node)
4048
- phpQuery::data($node, $key, $value, $this->getDocumentID());
4049
- return $this;
4050
- }
4051
- }
4052
- /**
4053
- * Enter description here...
4054
- *
4055
- * @param <type> $key
4056
- */
4057
- public function removeData($key) {
4058
- foreach($this as $node)
4059
- phpQuery::removeData($node, $key, $this->getDocumentID());
4060
- return $this;
4061
- }
4062
- // INTERFACE IMPLEMENTATIONS
4063
-
4064
- // ITERATOR INTERFACE
4065
- /**
4066
- * @access private
4067
- */
4068
- public function rewind(){
4069
- $this->debug('iterating foreach');
4070
- // phpQuery::selectDocument($this->getDocumentID());
4071
- $this->elementsBackup = $this->elements;
4072
- $this->elementsInterator = $this->elements;
4073
- $this->valid = isset( $this->elements[0] )
4074
- ? 1 : 0;
4075
- // $this->elements = $this->valid
4076
- // ? array($this->elements[0])
4077
- // : array();
4078
- $this->current = 0;
4079
- }
4080
- /**
4081
- * @access private
4082
- */
4083
- public function current(){
4084
- return $this->elementsInterator[ $this->current ];
4085
- }
4086
- /**
4087
- * @access private
4088
- */
4089
- public function key(){
4090
- return $this->current;
4091
- }
4092
- /**
4093
- * Double-function method.
4094
- *
4095
- * First: main iterator interface method.
4096
- * Second: Returning next sibling, alias for _next().
4097
- *
4098
- * Proper functionality is choosed automagicaly.
4099
- *
4100
- * @see phpQueryObject::_next()
4101
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4102
- */
4103
- public function next($cssSelector = null){
4104
- // if ($cssSelector || $this->valid)
4105
- // return $this->_next($cssSelector);
4106
- $this->valid = isset( $this->elementsInterator[ $this->current+1 ] )
4107
- ? true
4108
- : false;
4109
- if (! $this->valid && $this->elementsInterator) {
4110
- $this->elementsInterator = null;
4111
- } else if ($this->valid) {
4112
- $this->current++;
4113
- } else {
4114
- return $this->_next($cssSelector);
4115
- }
4116
- }
4117
- /**
4118
- * @access private
4119
- */
4120
- public function valid(){
4121
- return $this->valid;
4122
- }
4123
- // ITERATOR INTERFACE END
4124
- // ARRAYACCESS INTERFACE
4125
- /**
4126
- * @access private
4127
- */
4128
- public function offsetExists($offset) {
4129
- return $this->find($offset)->size() > 0;
4130
- }
4131
- /**
4132
- * @access private
4133
- */
4134
- public function offsetGet($offset) {
4135
- return $this->find($offset);
4136
- }
4137
- /**
4138
- * @access private
4139
- */
4140
- public function offsetSet($offset, $value) {
4141
- // $this->find($offset)->replaceWith($value);
4142
- $this->find($offset)->html($value);
4143
- }
4144
- /**
4145
- * @access private
4146
- */
4147
- public function offsetUnset($offset) {
4148
- // empty
4149
- throw new Exception("Can't do unset, use array interface only for calling queries and replacing HTML.");
4150
- }
4151
- // ARRAYACCESS INTERFACE END
4152
- /**
4153
- * Returns node's XPath.
4154
- *
4155
- * @param unknown_type $oneNode
4156
- * @return string
4157
- * @TODO use native getNodePath is avaible
4158
- * @access private
4159
- */
4160
- protected function getNodeXpath($oneNode = null, $namespace = null) {
4161
- $return = array();
4162
- $loop = $oneNode
4163
- ? array($oneNode)
4164
- : $this->elements;
4165
- // if ($namespace)
4166
- // $namespace .= ':';
4167
- foreach($loop as $node) {
4168
- if ($node instanceof DOMDOCUMENT) {
4169
- $return[] = '';
4170
- continue;
4171
- }
4172
- $xpath = array();
4173
- while(! ($node instanceof DOMDOCUMENT)) {
4174
- $i = 1;
4175
- $sibling = $node;
4176
- while($sibling->previousSibling) {
4177
- $sibling = $sibling->previousSibling;
4178
- $isElement = $sibling instanceof DOMELEMENT;
4179
- if ($isElement && $sibling->tagName == $node->tagName)
4180
- $i++;
4181
- }
4182
- $xpath[] = $this->isXML()
4183
- ? "*[local-name()='{$node->tagName}'][{$i}]"
4184
- : "{$node->tagName}[{$i}]";
4185
- $node = $node->parentNode;
4186
- }
4187
- $xpath = join('/', array_reverse($xpath));
4188
- $return[] = '/'.$xpath;
4189
- }
4190
- return $oneNode
4191
- ? $return[0]
4192
- : $return;
4193
- }
4194
- // HELPERS
4195
- public function whois($oneNode = null) {
4196
- $return = array();
4197
- $loop = $oneNode
4198
- ? array( $oneNode )
4199
- : $this->elements;
4200
- foreach($loop as $node) {
4201
- if (isset($node->tagName)) {
4202
- $tag = in_array($node->tagName, array('php', 'js'))
4203
- ? strtoupper($node->tagName)
4204
- : $node->tagName;
4205
- $return[] = $tag
4206
- .($node->getAttribute('id')
4207
- ? '#'.$node->getAttribute('id'):'')
4208
- .($node->getAttribute('class')
4209
- ? '.'.join('.', split(' ', $node->getAttribute('class'))):'')
4210
- .($node->getAttribute('name')
4211
- ? '[name="'.$node->getAttribute('name').'"]':'')
4212
- .($node->getAttribute('value') && strpos($node->getAttribute('value'), '<'.'?php') === false
4213
- ? '[value="'.substr(str_replace("\n", '', $node->getAttribute('value')), 0, 15).'"]':'')
4214
- .($node->getAttribute('value') && strpos($node->getAttribute('value'), '<'.'?php') !== false
4215
- ? '[value=PHP]':'')
4216
- .($node->getAttribute('selected')
4217
- ? '[selected]':'')
4218
- .($node->getAttribute('checked')
4219
- ? '[checked]':'')
4220
- ;
4221
- } else if ($node instanceof DOMTEXT) {
4222
- if (trim($node->textContent))
4223
- $return[] = 'Text:'.substr(str_replace("\n", ' ', $node->textContent), 0, 15);
4224
- } else {
4225
-
4226
- }
4227
- }
4228
- return $oneNode && isset($return[0])
4229
- ? $return[0]
4230
- : $return;
4231
- }
4232
- /**
4233
- * Dump htmlOuter and preserve chain. Usefull for debugging.
4234
- *
4235
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4236
- *
4237
- */
4238
- public function dump() {
4239
- print 'DUMP #'.(phpQuery::$dumpCount++).' ';
4240
- $debug = phpQuery::$debug;
4241
- phpQuery::$debug = false;
4242
- // print __FILE__.':'.__LINE__."\n";
4243
- var_dump($this->htmlOuter());
4244
- return $this;
4245
- }
4246
- public function dumpWhois() {
4247
- print 'DUMP #'.(phpQuery::$dumpCount++).' ';
4248
- $debug = phpQuery::$debug;
4249
- phpQuery::$debug = false;
4250
- // print __FILE__.':'.__LINE__."\n";
4251
- var_dump('whois', $this->whois());
4252
- phpQuery::$debug = $debug;
4253
- return $this;
4254
- }
4255
- public function dumpLength() {
4256
- print 'DUMP #'.(phpQuery::$dumpCount++).' ';
4257
- $debug = phpQuery::$debug;
4258
- phpQuery::$debug = false;
4259
- // print __FILE__.':'.__LINE__."\n";
4260
- var_dump('length', $this->length());
4261
- phpQuery::$debug = $debug;
4262
- return $this;
4263
- }
4264
- public function dumpTree($html = true, $title = true) {
4265
- $output = $title
4266
- ? 'DUMP #'.(phpQuery::$dumpCount++)." \n" : '';
4267
- $debug = phpQuery::$debug;
4268
- phpQuery::$debug = false;
4269
- foreach($this->stack() as $node)
4270
- $output .= $this->__dumpTree($node);
4271
- phpQuery::$debug = $debug;
4272
- print $html
4273
- ? nl2br(str_replace(' ', '&nbsp;', $output))
4274
- : $output;
4275
- return $this;
4276
- }
4277
- private function __dumpTree($node, $intend = 0) {
4278
- $whois = $this->whois($node);
4279
- $return = '';
4280
- if ($whois)
4281
- $return .= str_repeat(' - ', $intend).$whois."\n";
4282
- if (isset($node->childNodes))
4283
- foreach($node->childNodes as $chNode)
4284
- $return .= $this->__dumpTree($chNode, $intend+1);
4285
- return $return;
4286
- }
4287
- /**
4288
- * Dump htmlOuter and stop script execution. Usefull for debugging.
4289
- *
4290
- */
4291
- public function dumpDie() {
4292
- print __FILE__.':'.__LINE__;
4293
- var_dump($this->htmlOuter());
4294
- die();
4295
- }
4296
- }
4297
-
4298
-
4299
- // -- Multibyte Compatibility functions ---------------------------------------
4300
- // http://svn.iphonewebdev.com/lace/lib/mb_compat.php
4301
-
4302
- /**
4303
- * mb_internal_encoding()
4304
- *
4305
- * Included for mbstring pseudo-compatability.
4306
- */
4307
- if (!function_exists('mb_internal_encoding'))
4308
- {
4309
- function mb_internal_encoding($enc) {return true; }
4310
- }
4311
-
4312
- /**
4313
- * mb_regex_encoding()
4314
- *
4315
- * Included for mbstring pseudo-compatability.
4316
- */
4317
- if (!function_exists('mb_regex_encoding'))
4318
- {
4319
- function mb_regex_encoding($enc) {return true; }
4320
- }
4321
-
4322
- /**
4323
- * mb_strlen()
4324
- *
4325
- * Included for mbstring pseudo-compatability.
4326
- */
4327
- if (!function_exists('mb_strlen'))
4328
- {
4329
- function mb_strlen($str)
4330
- {
4331
- return strlen($str);
4332
- }
4333
- }
4334
-
4335
- /**
4336
- * mb_strpos()
4337
- *
4338
- * Included for mbstring pseudo-compatability.
4339
- */
4340
- if (!function_exists('mb_strpos'))
4341
- {
4342
- function mb_strpos($haystack, $needle, $offset=0)
4343
- {
4344
- return strpos($haystack, $needle, $offset);
4345
- }
4346
- }
4347
- /**
4348
- * mb_stripos()
4349
- *
4350
- * Included for mbstring pseudo-compatability.
4351
- */
4352
- if (!function_exists('mb_stripos'))
4353
- {
4354
- function mb_stripos($haystack, $needle, $offset=0)
4355
- {
4356
- return stripos($haystack, $needle, $offset);
4357
- }
4358
- }
4359
-
4360
- /**
4361
- * mb_substr()
4362
- *
4363
- * Included for mbstring pseudo-compatability.
4364
- */
4365
- if (!function_exists('mb_substr'))
4366
- {
4367
- function mb_substr($str, $start, $length=0)
4368
- {
4369
- return substr($str, $start, $length);
4370
- }
4371
- }
4372
-
4373
- /**
4374
- * mb_substr_count()
4375
- *
4376
- * Included for mbstring pseudo-compatability.
4377
- */
4378
- if (!function_exists('mb_substr_count'))
4379
- {
4380
- function mb_substr_count($haystack, $needle)
4381
- {
4382
- return substr_count($haystack, $needle);
4383
- }
4384
- }
4385
-
4386
-
4387
- /**
4388
- * Static namespace for phpQuery functions.
4389
- *
4390
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
4391
- * @package phpQuery
4392
- */
4393
- abstract class phpQuery {
4394
- /**
4395
- * XXX: Workaround for mbstring problems
4396
- *
4397
- * @var bool
4398
- */
4399
- public static $mbstringSupport = true;
4400
- public static $debug = false;
4401
- public static $documents = array();
4402
- public static $defaultDocumentID = null;
4403
- // public static $defaultDoctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"';
4404
- /**
4405
- * Applies only to HTML.
4406
- *
4407
- * @var unknown_type
4408
- */
4409
- public static $defaultDoctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
4410
- "http://www.w3.org/TR/html4/loose.dtd">';
4411
- public static $defaultCharset = 'UTF-8';
4412
- /**
4413
- * Static namespace for plugins.
4414
- *
4415
- * @var object
4416
- */
4417
- public static $plugins = array();
4418
- /**
4419
- * List of loaded plugins.
4420
- *
4421
- * @var unknown_type
4422
- */
4423
- public static $pluginsLoaded = array();
4424
- public static $pluginsMethods = array();
4425
- public static $pluginsStaticMethods = array();
4426
- public static $extendMethods = array();
4427
- /**
4428
- * @TODO implement
4429
- */
4430
- public static $extendStaticMethods = array();
4431
- /**
4432
- * Hosts allowed for AJAX connections.
4433
- * Dot '.' means $_SERVER['HTTP_HOST'] (if any).
4434
- *
4435
- * @var array
4436
- */
4437
- public static $ajaxAllowedHosts = array(
4438
- '.'
4439
- );
4440
- /**
4441
- * AJAX settings.
4442
- *
4443
- * @var array
4444
- * XXX should it be static or not ?
4445
- */
4446
- public static $ajaxSettings = array(
4447
- 'url' => '',//TODO
4448
- 'global' => true,
4449
- 'type' => "GET",
4450
- 'timeout' => null,
4451
- 'contentType' => "application/x-www-form-urlencoded",
4452
- 'processData' => true,
4453
- // 'async' => true,
4454
- 'data' => null,
4455
- 'username' => null,
4456
- 'password' => null,
4457
- 'accepts' => array(
4458
- 'xml' => "application/xml, text/xml",
4459
- 'html' => "text/html",
4460
- 'script' => "text/javascript, application/javascript",
4461
- 'json' => "application/json, text/javascript",
4462
- 'text' => "text/plain",
4463
- '_default' => "*/*"
4464
- )
4465
- );
4466
- public static $lastModified = null;
4467
- public static $active = 0;
4468
- public static $dumpCount = 0;
4469
- /**
4470
- * Multi-purpose function.
4471
- * Use pq() as shortcut.
4472
- *
4473
- * In below examples, $pq is any result of pq(); function.
4474
- *
4475
- * 1. Import markup into existing document (without any attaching):
4476
- * - Import into selected document:
4477
- * pq('<div/>') // DOESNT accept text nodes at beginning of input string !
4478
- * - Import into document with ID from $pq->getDocumentID():
4479
- * pq('<div/>', $pq->getDocumentID())
4480
- * - Import into same document as DOMNode belongs to:
4481
- * pq('<div/>', DOMNode)
4482
- * - Import into document from phpQuery object:
4483
- * pq('<div/>', $pq)
4484
- *
4485
- * 2. Run query:
4486
- * - Run query on last selected document:
4487
- * pq('div.myClass')
4488
- * - Run query on document with ID from $pq->getDocumentID():
4489
- * pq('div.myClass', $pq->getDocumentID())
4490
- * - Run query on same document as DOMNode belongs to and use node(s)as root for query:
4491
- * pq('div.myClass', DOMNode)
4492
- * - Run query on document from phpQuery object
4493
- * and use object's stack as root node(s) for query:
4494
- * pq('div.myClass', $pq)
4495
- *
4496
- * @param string|DOMNode|DOMNodeList|array $arg1 HTML markup, CSS Selector, DOMNode or array of DOMNodes
4497
- * @param string|phpQueryObject|DOMNode $context DOM ID from $pq->getDocumentID(), phpQuery object (determines also query root) or DOMNode (determines also query root)
4498
- *
4499
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery|QueryTemplatesPhpQuery|false
4500
- * phpQuery object or false in case of error.
4501
- */
4502
- public static function pq($arg1, $context = null) {
4503
- if ($arg1 instanceof DOMNODE && ! isset($context)) {
4504
- foreach(phpQuery::$documents as $documentWrapper) {
4505
- $compare = $arg1 instanceof DOMDocument
4506
- ? $arg1 : $arg1->ownerDocument;
4507
- if ($documentWrapper->document->isSameNode($compare))
4508
- $context = $documentWrapper->id;
4509
- }
4510
- }
4511
- if (! $context) {
4512
- $domId = self::$defaultDocumentID;
4513
- if (! $domId)
4514
- throw new Exception("Can't use last created DOM, because there isn't any. Use phpQuery::newDocument() first.");
4515
- // } else if (is_object($context) && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
4516
- } else if (is_object($context) && $context instanceof phpQueryObject)
4517
- $domId = $context->getDocumentID();
4518
- else if ($context instanceof DOMDOCUMENT) {
4519
- $domId = self::getDocumentID($context);
4520
- if (! $domId) {
4521
- //throw new Exception('Orphaned DOMDocument');
4522
- $domId = self::newDocument($context)->getDocumentID();
4523
- }
4524
- } else if ($context instanceof DOMNODE) {
4525
- $domId = self::getDocumentID($context);
4526
- if (! $domId) {
4527
- throw new Exception('Orphaned DOMNode');
4528
- // $domId = self::newDocument($context->ownerDocument);
4529
- }
4530
- } else
4531
- $domId = $context;
4532
- if ($arg1 instanceof phpQueryObject) {
4533
- // if (is_object($arg1) && (get_class($arg1) == 'phpQueryObject' || $arg1 instanceof PHPQUERY || is_subclass_of($arg1, 'phpQueryObject'))) {
4534
- /**
4535
- * Return $arg1 or import $arg1 stack if document differs:
4536
- * pq(pq('<div/>'))
4537
- */
4538
- if ($arg1->getDocumentID() == $domId)
4539
- return $arg1;
4540
- $class = get_class($arg1);
4541
- // support inheritance by passing old object to overloaded constructor
4542
- $phpQuery = $class != 'phpQuery'
4543
- ? new $class($arg1, $domId)
4544
- : new phpQueryObject($domId);
4545
- $phpQuery->elements = array();
4546
- foreach($arg1->elements as $node)
4547
- $phpQuery->elements[] = $phpQuery->document->importNode($node, true);
4548
- return $phpQuery;
4549
- } else if ($arg1 instanceof DOMNODE || (is_array($arg1) && isset($arg1[0]) && $arg1[0] instanceof DOMNODE)) {
4550
- /*
4551
- * Wrap DOM nodes with phpQuery object, import into document when needed:
4552
- * pq(array($domNode1, $domNode2))
4553
- */
4554
- $phpQuery = new phpQueryObject($domId);
4555
- if (!($arg1 instanceof DOMNODELIST) && ! is_array($arg1))
4556
- $arg1 = array($arg1);
4557
- $phpQuery->elements = array();
4558
- foreach($arg1 as $node) {
4559
- $sameDocument = $node->ownerDocument instanceof DOMDOCUMENT
4560
- && ! $node->ownerDocument->isSameNode($phpQuery->document);
4561
- $phpQuery->elements[] = $sameDocument
4562
- ? $phpQuery->document->importNode($node, true)
4563
- : $node;
4564
- }
4565
- return $phpQuery;
4566
- } else if (self::isMarkup($arg1)) {
4567
- /**
4568
- * Import HTML:
4569
- * pq('<div/>')
4570
- */
4571
- $phpQuery = new phpQueryObject($domId);
4572
- return $phpQuery->newInstance(
4573
- $phpQuery->documentWrapper->import($arg1)
4574
- );
4575
- } else {
4576
- /**
4577
- * Run CSS query:
4578
- * pq('div.myClass')
4579
- */
4580
- $phpQuery = new phpQueryObject($domId);
4581
- // if ($context && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
4582
- if ($context && $context instanceof phpQueryObject)
4583
- $phpQuery->elements = $context->elements;
4584
- else if ($context && $context instanceof DOMNODELIST) {
4585
- $phpQuery->elements = array();
4586
- foreach($context as $node)
4587
- $phpQuery->elements[] = $node;
4588
- } else if ($context && $context instanceof DOMNODE)
4589
- $phpQuery->elements = array($context);
4590
- return $phpQuery->find($arg1);
4591
- }
4592
- }
4593
- /**
4594
- * Sets default document to $id. Document has to be loaded prior
4595
- * to using this method.
4596
- * $id can be retrived via getDocumentID() or getDocumentIDRef().
4597
- *
4598
- * @param unknown_type $id
4599
- */
4600
- public static function selectDocument($id) {
4601
- $id = self::getDocumentID($id);
4602
- self::debug("Selecting document '$id' as default one");
4603
- self::$defaultDocumentID = self::getDocumentID($id);
4604
- }
4605
- /**
4606
- * Returns document with id $id or last used as phpQueryObject.
4607
- * $id can be retrived via getDocumentID() or getDocumentIDRef().
4608
- * Chainable.
4609
- *
4610
- * @see phpQuery::selectDocument()
4611
- * @param unknown_type $id
4612
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4613
- */
4614
- public static function getDocument($id = null) {
4615
- if ($id)
4616
- phpQuery::selectDocument($id);
4617
- else
4618
- $id = phpQuery::$defaultDocumentID;
4619
- return new phpQueryObject($id);
4620
- }
4621
- /**
4622
- * Creates new document from markup.
4623
- * Chainable.
4624
- *
4625
- * @param unknown_type $markup
4626
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4627
- */
4628
- public static function newDocument($markup = null, $contentType = null) {
4629
- if (! $markup)
4630
- $markup = '';
4631
- $documentID = phpQuery::createDocumentWrapper($markup, $contentType);
4632
- return new phpQueryObject($documentID);
4633
- }
4634
- /**
4635
- * Creates new document from markup.
4636
- * Chainable.
4637
- *
4638
- * @param unknown_type $markup
4639
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4640
- */
4641
- public static function newDocumentHTML($markup = null, $charset = null) {
4642
- $contentType = $charset
4643
- ? ";charset=$charset"
4644
- : '';
4645
- return self::newDocument($markup, "text/html{$contentType}");
4646
- }
4647
- /**
4648
- * Creates new document from markup.
4649
- * Chainable.
4650
- *
4651
- * @param unknown_type $markup
4652
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4653
- */
4654
- public static function newDocumentXML($markup = null, $charset = null) {
4655
- $contentType = $charset
4656
- ? ";charset=$charset"
4657
- : '';
4658
- return self::newDocument($markup, "text/xml{$contentType}");
4659
- }
4660
- /**
4661
- * Creates new document from markup.
4662
- * Chainable.
4663
- *
4664
- * @param unknown_type $markup
4665
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4666
- */
4667
- public static function newDocumentXHTML($markup = null, $charset = null) {
4668
- $contentType = $charset
4669
- ? ";charset=$charset"
4670
- : '';
4671
- return self::newDocument($markup, "application/xhtml+xml{$contentType}");
4672
- }
4673
- /**
4674
- * Creates new document from markup.
4675
- * Chainable.
4676
- *
4677
- * @param unknown_type $markup
4678
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4679
- */
4680
- public static function newDocumentPHP($markup = null, $contentType = "text/html") {
4681
- // TODO pass charset to phpToMarkup if possible (use DOMDocumentWrapper function)
4682
- $markup = phpQuery::phpToMarkup($markup, self::$defaultCharset);
4683
- return self::newDocument($markup, $contentType);
4684
- }
4685
- public static function phpToMarkup($php, $charset = 'utf-8') {
4686
- $regexes = array(
4687
- '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)<'.'?php?(.*?)(?:\\?>)([^\']*)\'@s',
4688
- '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)<'.'?php?(.*?)(?:\\?>)([^"]*)"@s',
4689
- );
4690
- foreach($regexes as $regex)
4691
- while (preg_match($regex, $php, $matches)) {
4692
- $php = preg_replace_callback(
4693
- $regex,
4694
- // create_function('$m, $charset = "'.$charset.'"',
4695
- // 'return $m[1].$m[2]
4696
- // .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
4697
- // .$m[5].$m[2];'
4698
- // ),
4699
- array('phpQuery', '_phpToMarkupCallback'),
4700
- $php
4701
- );
4702
- }
4703
- $regex = '@(^|>[^<]*)+?(<\?php(.*?)(\?>))@s';
4704
- //preg_match_all($regex, $php, $matches);
4705
- //var_dump($matches);
4706
- $php = preg_replace($regex, '\\1<php><!-- \\3 --></php>', $php);
4707
- return $php;
4708
- }
4709
- public static function _phpToMarkupCallback($php, $charset = 'utf-8') {
4710
- return $m[1].$m[2]
4711
- .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
4712
- .$m[5].$m[2];
4713
- }
4714
- public static function _markupToPHPCallback($m) {
4715
- return "<"."?php ".htmlspecialchars_decode($m[1])." ?".">";
4716
- }
4717
- /**
4718
- * Converts document markup containing PHP code generated by phpQuery::php()
4719
- * into valid (executable) PHP code syntax.
4720
- *
4721
- * @param string|phpQueryObject $content
4722
- * @return string PHP code.
4723
- */
4724
- public static function markupToPHP($content) {
4725
- if ($content instanceof phpQueryObject)
4726
- $content = $content->markupOuter();
4727
- /* <php>...</php> to <?php...? > */
4728
- $content = preg_replace_callback(
4729
- '@<php>\s*<!--(.*?)-->\s*</php>@s',
4730
- // create_function('$m',
4731
- // 'return "<'.'?php ".htmlspecialchars_decode($m[1])." ?'.'>";'
4732
- // ),
4733
- array('phpQuery', '_markupToPHPCallback'),
4734
- $content
4735
- );
4736
- /* <node attr='< ?php ? >'> extra space added to save highlighters */
4737
- $regexes = array(
4738
- '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^\']*)\'@s',
4739
- '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)(?:&lt;|%3C)\\?(?:php)?(.*?)(?:\\?(?:&gt;|%3E))([^"]*)"@s',
4740
- );
4741
- foreach($regexes as $regex)
4742
- while (preg_match($regex, $content))
4743
- $content = preg_replace_callback(
4744
- $regex,
4745
- create_function('$m',
4746
- 'return $m[1].$m[2].$m[3]."<?php "
4747
- .str_replace(
4748
- array("%20", "%3E", "%09", "&#10;", "&#9;", "%7B", "%24", "%7D", "%22", "%5B", "%5D"),
4749
- array(" ", ">", " ", "\n", " ", "{", "$", "}", \'"\', "[", "]"),
4750
- htmlspecialchars_decode($m[4])
4751
- )
4752
- ." ?>".$m[5].$m[2];'
4753
- ),
4754
- $content
4755
- );
4756
- return $content;
4757
- }
4758
- /**
4759
- * Creates new document from file $file.
4760
- * Chainable.
4761
- *
4762
- * @param string $file URLs allowed. See File wrapper page at php.net for more supported sources.
4763
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4764
- */
4765
- public static function newDocumentFile($file, $contentType = null) {
4766
- $documentID = self::createDocumentWrapper(
4767
- file_get_contents($file), $contentType
4768
- );
4769
- return new phpQueryObject($documentID);
4770
- }
4771
- /**
4772
- * Creates new document from markup.
4773
- * Chainable.
4774
- *
4775
- * @param unknown_type $markup
4776
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4777
- */
4778
- public static function newDocumentFileHTML($file, $charset = null) {
4779
- $contentType = $charset
4780
- ? ";charset=$charset"
4781
- : '';
4782
- return self::newDocumentFile($file, "text/html{$contentType}");
4783
- }
4784
- /**
4785
- * Creates new document from markup.
4786
- * Chainable.
4787
- *
4788
- * @param unknown_type $markup
4789
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4790
- */
4791
- public static function newDocumentFileXML($file, $charset = null) {
4792
- $contentType = $charset
4793
- ? ";charset=$charset"
4794
- : '';
4795
- return self::newDocumentFile($file, "text/xml{$contentType}");
4796
- }
4797
- /**
4798
- * Creates new document from markup.
4799
- * Chainable.
4800
- *
4801
- * @param unknown_type $markup
4802
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4803
- */
4804
- public static function newDocumentFileXHTML($file, $charset = null) {
4805
- $contentType = $charset
4806
- ? ";charset=$charset"
4807
- : '';
4808
- return self::newDocumentFile($file, "application/xhtml+xml{$contentType}");
4809
- }
4810
- /**
4811
- * Creates new document from markup.
4812
- * Chainable.
4813
- *
4814
- * @param unknown_type $markup
4815
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4816
- */
4817
- public static function newDocumentFilePHP($file, $contentType = null) {
4818
- return self::newDocumentPHP(file_get_contents($file), $contentType);
4819
- }
4820
- /**
4821
- * Reuses existing DOMDocument object.
4822
- * Chainable.
4823
- *
4824
- * @param $document DOMDocument
4825
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4826
- * @TODO support DOMDocument
4827
- */
4828
- public static function loadDocument($document) {
4829
- // TODO
4830
- die('TODO loadDocument');
4831
- }
4832
- /**
4833
- * Enter description here...
4834
- *
4835
- * @param unknown_type $html
4836
- * @param unknown_type $domId
4837
- * @return unknown New DOM ID
4838
- * @todo support PHP tags in input
4839
- * @todo support passing DOMDocument object from self::loadDocument
4840
- */
4841
- protected static function createDocumentWrapper($html, $contentType = null, $documentID = null) {
4842
- if (function_exists('domxml_open_mem'))
4843
- throw new Exception("Old PHP4 DOM XML extension detected. phpQuery won't work until this extension is enabled.");
4844
- // $id = $documentID
4845
- // ? $documentID
4846
- // : md5(microtime());
4847
- $document = null;
4848
- if ($html instanceof DOMDOCUMENT) {
4849
- if (self::getDocumentID($html)) {
4850
- // document already exists in phpQuery::$documents, make a copy
4851
- $document = clone $html;
4852
- } else {
4853
- // new document, add it to phpQuery::$documents
4854
- $wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
4855
- }
4856
- } else {
4857
- $wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
4858
- }
4859
- // $wrapper->id = $id;
4860
- // bind document
4861
- phpQuery::$documents[$wrapper->id] = $wrapper;
4862
- // remember last loaded document
4863
- phpQuery::selectDocument($wrapper->id);
4864
- return $wrapper->id;
4865
- }
4866
- /**
4867
- * Extend class namespace.
4868
- *
4869
- * @param string|array $target
4870
- * @param array $source
4871
- * @TODO support string $source
4872
- * @return unknown_type
4873
- */
4874
- public static function extend($target, $source) {
4875
- switch($target) {
4876
- case 'phpQueryObject':
4877
- $targetRef = &self::$extendMethods;
4878
- $targetRef2 = &self::$pluginsMethods;
4879
- break;
4880
- case 'phpQuery':
4881
- $targetRef = &self::$extendStaticMethods;
4882
- $targetRef2 = &self::$pluginsStaticMethods;
4883
- break;
4884
- default:
4885
- throw new Exception("Unsupported \$target type");
4886
- }
4887
- if (is_string($source))
4888
- $source = array($source => $source);
4889
- foreach($source as $method => $callback) {
4890
- if (isset($targetRef[$method])) {
4891
- // throw new Exception
4892
- self::debug("Duplicate method '{$method}', can\'t extend '{$target}'");
4893
- continue;
4894
- }
4895
- if (isset($targetRef2[$method])) {
4896
- // throw new Exception
4897
- self::debug("Duplicate method '{$method}' from plugin '{$targetRef2[$method]}',"
4898
- ." can\'t extend '{$target}'");
4899
- continue;
4900
- }
4901
- $targetRef[$method] = $callback;
4902
- }
4903
- return true;
4904
- }
4905
- /**
4906
- * Extend phpQuery with $class from $file.
4907
- *
4908
- * @param string $class Extending class name. Real class name can be prepended phpQuery_.
4909
- * @param string $file Filename to include. Defaults to "{$class}.php".
4910
- */
4911
- public static function plugin($class, $file = null) {
4912
- // TODO $class checked agains phpQuery_$class
4913
- // if (strpos($class, 'phpQuery') === 0)
4914
- // $class = substr($class, 8);
4915
- if (in_array($class, self::$pluginsLoaded))
4916
- return true;
4917
- if (! $file)
4918
- $file = $class.'.php';
4919
- $objectClassExists = class_exists('phpQueryObjectPlugin_'.$class);
4920
- $staticClassExists = class_exists('phpQueryPlugin_'.$class);
4921
- if (! $objectClassExists && ! $staticClassExists)
4922
- require_once($file);
4923
- self::$pluginsLoaded[] = $class;
4924
- // static methods
4925
- if (class_exists('phpQueryPlugin_'.$class)) {
4926
- $realClass = 'phpQueryPlugin_'.$class;
4927
- $vars = get_class_vars($realClass);
4928
- $loop = isset($vars['phpQueryMethods'])
4929
- && ! is_null($vars['phpQueryMethods'])
4930
- ? $vars['phpQueryMethods']
4931
- : get_class_methods($realClass);
4932
- foreach($loop as $method) {
4933
- if ($method == '__initialize')
4934
- continue;
4935
- if (! is_callable(array($realClass, $method)))
4936
- continue;
4937
- if (isset(self::$pluginsStaticMethods[$method])) {
4938
- throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsStaticMethods[$method]."'");
4939
- return;
4940
- }
4941
- self::$pluginsStaticMethods[$method] = $class;
4942
- }
4943
- if (method_exists($realClass, '__initialize'))
4944
- call_user_func_array(array($realClass, '__initialize'), array());
4945
- }
4946
- // object methods
4947
- if (class_exists('phpQueryObjectPlugin_'.$class)) {
4948
- $realClass = 'phpQueryObjectPlugin_'.$class;
4949
- $vars = get_class_vars($realClass);
4950
- $loop = isset($vars['phpQueryMethods'])
4951
- && ! is_null($vars['phpQueryMethods'])
4952
- ? $vars['phpQueryMethods']
4953
- : get_class_methods($realClass);
4954
- foreach($loop as $method) {
4955
- if (! is_callable(array($realClass, $method)))
4956
- continue;
4957
- if (isset(self::$pluginsMethods[$method])) {
4958
- throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsMethods[$method]."'");
4959
- continue;
4960
- }
4961
- self::$pluginsMethods[$method] = $class;
4962
- }
4963
- }
4964
- return true;
4965
- }
4966
- /**
4967
- * Unloades all or specified document from memory.
4968
- *
4969
- * @param mixed $documentID @see phpQuery::getDocumentID() for supported types.
4970
- */
4971
- public static function unloadDocuments($id = null) {
4972
- if (isset($id)) {
4973
- if ($id = self::getDocumentID($id))
4974
- unset(phpQuery::$documents[$id]);
4975
- } else {
4976
- foreach(phpQuery::$documents as $k => $v) {
4977
- unset(phpQuery::$documents[$k]);
4978
- }
4979
- }
4980
- }
4981
- /**
4982
- * Parses phpQuery object or HTML result against PHP tags and makes them active.
4983
- *
4984
- * @param phpQuery|string $content
4985
- * @deprecated
4986
- * @return string
4987
- */
4988
- public static function unsafePHPTags($content) {
4989
- return self::markupToPHP($content);
4990
- }
4991
- public static function DOMNodeListToArray($DOMNodeList) {
4992
- $array = array();
4993
- if (! $DOMNodeList)
4994
- return $array;
4995
- foreach($DOMNodeList as $node)
4996
- $array[] = $node;
4997
- return $array;
4998
- }
4999
- /**
5000
- * Checks if $input is HTML string, which has to start with '<'.
5001
- *
5002
- * @deprecated
5003
- * @param String $input
5004
- * @return Bool
5005
- * @todo still used ?
5006
- */
5007
- public static function isMarkup($input) {
5008
- return ! is_array($input) && substr(trim($input), 0, 1) == '<';
5009
- }
5010
- public static function debug($text) {
5011
- if (self::$debug)
5012
- print var_dump($text);
5013
- }
5014
- /**
5015
- * Make an AJAX request.
5016
- *
5017
- * @param array See $options http://docs.jquery.com/Ajax/jQuery.ajax#toptions
5018
- * Additional options are:
5019
- * 'document' - document for global events, @see phpQuery::getDocumentID()
5020
- * 'referer' - implemented
5021
- * 'requested_with' - TODO; not implemented (X-Requested-With)
5022
- * @return Zend_Http_Client
5023
- * @link http://docs.jquery.com/Ajax/jQuery.ajax
5024
- *
5025
- * @TODO $options['cache']
5026
- * @TODO $options['processData']
5027
- * @TODO $options['xhr']
5028
- * @TODO $options['data'] as string
5029
- * @TODO XHR interface
5030
- */
5031
- public static function ajax($options = array(), $xhr = null) {
5032
- $options = array_merge(
5033
- self::$ajaxSettings, $options
5034
- );
5035
- $documentID = isset($options['document'])
5036
- ? self::getDocumentID($options['document'])
5037
- : null;
5038
- if ($xhr) {
5039
- // reuse existing XHR object, but clean it up
5040
- $client = $xhr;
5041
- // $client->setParameterPost(null);
5042
- // $client->setParameterGet(null);
5043
- $client->setAuth(false);
5044
- $client->setHeaders("If-Modified-Since", null);
5045
- $client->setHeaders("Referer", null);
5046
- $client->resetParameters();
5047
- } else {
5048
- // create new XHR object
5049
- require_once('Zend/Http/Client.php');
5050
- $client = new Zend_Http_Client();
5051
- $client->setCookieJar();
5052
- }
5053
- if (isset($options['timeout']))
5054
- $client->setConfig(array(
5055
- 'timeout' => $options['timeout'],
5056
- ));
5057
- // 'maxredirects' => 0,
5058
- foreach(self::$ajaxAllowedHosts as $k => $host)
5059
- if ($host == '.' && isset($_SERVER['HTTP_HOST']))
5060
- self::$ajaxAllowedHosts[$k] = $_SERVER['HTTP_HOST'];
5061
- $host = parse_url($options['url'], PHP_URL_HOST);
5062
- if (! in_array($host, self::$ajaxAllowedHosts)) {
5063
- throw new Exception("Request not permitted, host '$host' not present in "
5064
- ."phpQuery::\$ajaxAllowedHosts");
5065
- }
5066
- // JSONP
5067
- $jsre = "/=\\?(&|$)/";
5068
- if (isset($options['dataType']) && $options['dataType'] == 'jsonp') {
5069
- $jsonpCallbackParam = $options['jsonp']
5070
- ? $options['jsonp'] : 'callback';
5071
- if (strtolower($options['type']) == 'get') {
5072
- if (! preg_match($jsre, $options['url'])) {
5073
- $sep = strpos($options['url'], '?')
5074
- ? '&' : '?';
5075
- $options['url'] .= "$sep$jsonpCallbackParam=?";
5076
- }
5077
- } else if ($options['data']) {
5078
- $jsonp = false;
5079
- foreach($options['data'] as $n => $v) {
5080
- if ($v == '?')
5081
- $jsonp = true;
5082
- }
5083
- if (! $jsonp) {
5084
- $options['data'][$jsonpCallbackParam] = '?';
5085
- }
5086
- }
5087
- $options['dataType'] = 'json';
5088
- }
5089
- if (isset($options['dataType']) && $options['dataType'] == 'json') {
5090
- $jsonpCallback = 'json_'.md5(microtime());
5091
- $jsonpData = $jsonpUrl = false;
5092
- if ($options['data']) {
5093
- foreach($options['data'] as $n => $v) {
5094
- if ($v == '?')
5095
- $jsonpData = $n;
5096
- }
5097
- }
5098
- if (preg_match($jsre, $options['url']))
5099
- $jsonpUrl = true;
5100
- if ($jsonpData !== false || $jsonpUrl) {
5101
- // remember callback name for httpData()
5102
- $options['_jsonp'] = $jsonpCallback;
5103
- if ($jsonpData !== false)
5104
- $options['data'][$jsonpData] = $jsonpCallback;
5105
- if ($jsonpUrl)
5106
- $options['url'] = preg_replace($jsre, "=$jsonpCallback\\1", $options['url']);
5107
- }
5108
- }
5109
- $client->setUri($options['url']);
5110
- $client->setMethod(strtoupper($options['type']));
5111
- if (isset($options['referer']) && $options['referer'])
5112
- $client->setHeaders('Referer', $options['referer']);
5113
- $client->setHeaders(array(
5114
- // 'content-type' => $options['contentType'],
5115
- 'User-Agent' => 'Mozilla/5.0 (X11; U; Linux x86; en-US; rv:1.9.0.5) Gecko'
5116
- .'/2008122010 Firefox/3.0.5',
5117
- // TODO custom charset
5118
- 'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
5119
- // 'Connection' => 'keep-alive',
5120
- // 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
5121
- 'Accept-Language' => 'en-us,en;q=0.5',
5122
- ));
5123
- if ($options['username'])
5124
- $client->setAuth($options['username'], $options['password']);
5125
- if (isset($options['ifModified']) && $options['ifModified'])
5126
- $client->setHeaders("If-Modified-Since",
5127
- self::$lastModified
5128
- ? self::$lastModified
5129
- : "Thu, 01 Jan 1970 00:00:00 GMT"
5130
- );
5131
- $client->setHeaders("Accept",
5132
- isset($options['dataType'])
5133
- && isset(self::$ajaxSettings['accepts'][ $options['dataType'] ])
5134
- ? self::$ajaxSettings['accepts'][ $options['dataType'] ].", */*"
5135
- : self::$ajaxSettings['accepts']['_default']
5136
- );
5137
- // TODO $options['processData']
5138
- if ($options['data'] instanceof phpQueryObject) {
5139
- $serialized = $options['data']->serializeArray($options['data']);
5140
- $options['data'] = array();
5141
- foreach($serialized as $r)
5142
- $options['data'][ $r['name'] ] = $r['value'];
5143
- }
5144
- if (strtolower($options['type']) == 'get') {
5145
- $client->setParameterGet($options['data']);
5146
- } else if (strtolower($options['type']) == 'post') {
5147
- $client->setEncType($options['contentType']);
5148
- $client->setParameterPost($options['data']);
5149
- }
5150
- if (self::$active == 0 && $options['global'])
5151
- phpQueryEvents::trigger($documentID, 'ajaxStart');
5152
- self::$active++;
5153
- // beforeSend callback
5154
- if (isset($options['beforeSend']) && $options['beforeSend'])
5155
- phpQuery::callbackRun($options['beforeSend'], array($client));
5156
- // ajaxSend event
5157
- if ($options['global'])
5158
- phpQueryEvents::trigger($documentID, 'ajaxSend', array($client, $options));
5159
- if (phpQuery::$debug) {
5160
- self::debug("{$options['type']}: {$options['url']}\n");
5161
- self::debug("Options: <pre>".var_export($options, true)."</pre>\n");
5162
- // if ($client->getCookieJar())
5163
- // self::debug("Cookies: <pre>".var_export($client->getCookieJar()->getMatchingCookies($options['url']), true)."</pre>\n");
5164
- }
5165
- // request
5166
- $response = $client->request();
5167
- if (phpQuery::$debug) {
5168
- self::debug('Status: '.$response->getStatus().' / '.$response->getMessage());
5169
- self::debug($client->getLastRequest());
5170
- self::debug($response->getHeaders());
5171
- }
5172
- if ($response->isSuccessful()) {
5173
- // XXX tempolary
5174
- self::$lastModified = $response->getHeader('Last-Modified');
5175
- $data = self::httpData($response->getBody(), $options['dataType'], $options);
5176
- if (isset($options['success']) && $options['success'])
5177
- phpQuery::callbackRun($options['success'], array($data, $response->getStatus(), $options));
5178
- if ($options['global'])
5179
- phpQueryEvents::trigger($documentID, 'ajaxSuccess', array($client, $options));
5180
- } else {
5181
- if (isset($options['error']) && $options['error'])
5182
- phpQuery::callbackRun($options['error'], array($client, $response->getStatus(), $response->getMessage()));
5183
- if ($options['global'])
5184
- phpQueryEvents::trigger($documentID, 'ajaxError', array($client, /*$response->getStatus(),*/$response->getMessage(), $options));
5185
- }
5186
- if (isset($options['complete']) && $options['complete'])
5187
- phpQuery::callbackRun($options['complete'], array($client, $response->getStatus()));
5188
- if ($options['global'])
5189
- phpQueryEvents::trigger($documentID, 'ajaxComplete', array($client, $options));
5190
- if ($options['global'] && ! --self::$active)
5191
- phpQueryEvents::trigger($documentID, 'ajaxStop');
5192
- return $client;
5193
- // if (is_null($domId))
5194
- // $domId = self::$defaultDocumentID ? self::$defaultDocumentID : false;
5195
- // return new phpQueryAjaxResponse($response, $domId);
5196
- }
5197
- protected static function httpData($data, $type, $options) {
5198
- if (isset($options['dataFilter']) && $options['dataFilter'])
5199
- $data = self::callbackRun($options['dataFilter'], array($data, $type));
5200
- if (is_string($data)) {
5201
- if ($type == "json") {
5202
- if (isset($options['_jsonp']) && $options['_jsonp']) {
5203
- $data = preg_replace('/^\s*\w+\((.*)\)\s*$/s', '$1', $data);
5204
- }
5205
- $data = self::parseJSON($data);
5206
- }
5207
- }
5208
- return $data;
5209
- }
5210
- /**
5211
- * Enter description here...
5212
- *
5213
- * @param array|phpQuery $data
5214
- *
5215
- */
5216
- public static function param($data) {
5217
- return http_build_query($data, null, '&');
5218
- }
5219
- public static function get($url, $data = null, $callback = null, $type = null) {
5220
- if (!is_array($data)) {
5221
- $callback = $data;
5222
- $data = null;
5223
- }
5224
- // TODO some array_values on this shit
5225
- return phpQuery::ajax(array(
5226
- 'type' => 'GET',
5227
- 'url' => $url,
5228
- 'data' => $data,
5229
- 'success' => $callback,
5230
- 'dataType' => $type,
5231
- ));
5232
- }
5233
- public static function post($url, $data = null, $callback = null, $type = null) {
5234
- if (!is_array($data)) {
5235
- $callback = $data;
5236
- $data = null;
5237
- }
5238
- return phpQuery::ajax(array(
5239
- 'type' => 'POST',
5240
- 'url' => $url,
5241
- 'data' => $data,
5242
- 'success' => $callback,
5243
- 'dataType' => $type,
5244
- ));
5245
- }
5246
- public static function getJSON($url, $data = null, $callback = null) {
5247
- if (!is_array($data)) {
5248
- $callback = $data;
5249
- $data = null;
5250
- }
5251
- // TODO some array_values on this shit
5252
- return phpQuery::ajax(array(
5253
- 'type' => 'GET',
5254
- 'url' => $url,
5255
- 'data' => $data,
5256
- 'success' => $callback,
5257
- 'dataType' => 'json',
5258
- ));
5259
- }
5260
- public static function ajaxSetup($options) {
5261
- self::$ajaxSettings = array_merge(
5262
- self::$ajaxSettings,
5263
- $options
5264
- );
5265
- }
5266
- public static function ajaxAllowHost($host1, $host2 = null, $host3 = null) {
5267
- $loop = is_array($host1)
5268
- ? $host1
5269
- : func_get_args();
5270
- foreach($loop as $host) {
5271
- if ($host && ! in_array($host, phpQuery::$ajaxAllowedHosts)) {
5272
- phpQuery::$ajaxAllowedHosts[] = $host;
5273
- }
5274
- }
5275
- }
5276
- public static function ajaxAllowURL($url1, $url2 = null, $url3 = null) {
5277
- $loop = is_array($url1)
5278
- ? $url1
5279
- : func_get_args();
5280
- foreach($loop as $url)
5281
- phpQuery::ajaxAllowHost(parse_url($url, PHP_URL_HOST));
5282
- }
5283
- /**
5284
- * Returns JSON representation of $data.
5285
- *
5286
- * @static
5287
- * @param mixed $data
5288
- * @return string
5289
- */
5290
- public static function toJSON($data) {
5291
- if (function_exists('json_encode'))
5292
- return json_encode($data);
5293
- require_once('Zend/Json/Encoder.php');
5294
- return Zend_Json_Encoder::encode($data);
5295
- }
5296
- /**
5297
- * Parses JSON into proper PHP type.
5298
- *
5299
- * @static
5300
- * @param string $json
5301
- * @return mixed
5302
- */
5303
- public static function parseJSON($json) {
5304
- if (function_exists('json_decode')) {
5305
- $return = json_decode(trim($json), true);
5306
- // json_decode and UTF8 issues
5307
- if (isset($return))
5308
- return $return;
5309
- }
5310
- require_once('Zend/Json/Decoder.php');
5311
- return Zend_Json_Decoder::decode($json);
5312
- }
5313
- /**
5314
- * Returns source's document ID.
5315
- *
5316
- * @param $source DOMNode|phpQueryObject
5317
- * @return string
5318
- */
5319
- public static function getDocumentID($source) {
5320
- if ($source instanceof DOMDOCUMENT) {
5321
- foreach(phpQuery::$documents as $id => $document) {
5322
- if ($source->isSameNode($document->document))
5323
- return $id;
5324
- }
5325
- } else if ($source instanceof DOMNODE) {
5326
- foreach(phpQuery::$documents as $id => $document) {
5327
- if ($source->ownerDocument->isSameNode($document->document))
5328
- return $id;
5329
- }
5330
- } else if ($source instanceof phpQueryObject)
5331
- return $source->getDocumentID();
5332
- else if (is_string($source) && isset(phpQuery::$documents[$source]))
5333
- return $source;
5334
- }
5335
- /**
5336
- * Get DOMDocument object related to $source.
5337
- * Returns null if such document doesn't exist.
5338
- *
5339
- * @param $source DOMNode|phpQueryObject|string
5340
- * @return string
5341
- */
5342
- public static function getDOMDocument($source) {
5343
- if ($source instanceof DOMDOCUMENT)
5344
- return $source;
5345
- $source = self::getDocumentID($source);
5346
- return $source
5347
- ? self::$documents[$id]['document']
5348
- : null;
5349
- }
5350
-
5351
- // UTILITIES
5352
- // http://docs.jquery.com/Utilities
5353
-
5354
- /**
5355
- *
5356
- * @return unknown_type
5357
- * @link http://docs.jquery.com/Utilities/jQuery.makeArray
5358
- */
5359
- public static function makeArray($obj) {
5360
- $array = array();
5361
- if (is_object($object) && $object instanceof DOMNODELIST) {
5362
- foreach($object as $value)
5363
- $array[] = $value;
5364
- } else if (is_object($object) && ! ($object instanceof Iterator)) {
5365
- foreach(get_object_vars($object) as $name => $value)
5366
- $array[0][$name] = $value;
5367
- } else {
5368
- foreach($object as $name => $value)
5369
- $array[0][$name] = $value;
5370
- }
5371
- return $array;
5372
- }
5373
- public static function inArray($value, $array) {
5374
- return in_array($value, $array);
5375
- }
5376
- /**
5377
- *
5378
- * @param $object
5379
- * @param $callback
5380
- * @return unknown_type
5381
- * @link http://docs.jquery.com/Utilities/jQuery.each
5382
- */
5383
- public static function each($object, $callback, $param1 = null, $param2 = null, $param3 = null) {
5384
- $paramStructure = null;
5385
- if (func_num_args() > 2) {
5386
- $paramStructure = func_get_args();
5387
- $paramStructure = array_slice($paramStructure, 2);
5388
- }
5389
- if (is_object($object) && ! ($object instanceof Iterator)) {
5390
- foreach(get_object_vars($object) as $name => $value)
5391
- phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
5392
- } else {
5393
- foreach($object as $name => $value)
5394
- phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
5395
- }
5396
- }
5397
- /**
5398
- *
5399
- * @link http://docs.jquery.com/Utilities/jQuery.map
5400
- */
5401
- public static function map($array, $callback, $param1 = null, $param2 = null, $param3 = null) {
5402
- $result = array();
5403
- $paramStructure = null;
5404
- if (func_num_args() > 2) {
5405
- $paramStructure = func_get_args();
5406
- $paramStructure = array_slice($paramStructure, 2);
5407
- }
5408
- foreach($array as $v) {
5409
- $vv = phpQuery::callbackRun($callback, array($v), $paramStructure);
5410
- // $callbackArgs = $args;
5411
- // foreach($args as $i => $arg) {
5412
- // $callbackArgs[$i] = $arg instanceof CallbackParam
5413
- // ? $v
5414
- // : $arg;
5415
- // }
5416
- // $vv = call_user_func_array($callback, $callbackArgs);
5417
- if (is_array($vv)) {
5418
- foreach($vv as $vvv)
5419
- $result[] = $vvv;
5420
- } else if ($vv !== null) {
5421
- $result[] = $vv;
5422
- }
5423
- }
5424
- return $result;
5425
- }
5426
- /**
5427
- *
5428
- * @param $callback Callback
5429
- * @param $params
5430
- * @param $paramStructure
5431
- * @return unknown_type
5432
- */
5433
- public static function callbackRun($callback, $params = array(), $paramStructure = null) {
5434
- if (! $callback)
5435
- return;
5436
- if ($callback instanceof CallbackParameterToReference) {
5437
- // TODO support ParamStructure to select which $param push to reference
5438
- if (isset($params[0]))
5439
- $callback->callback = $params[0];
5440
- return true;
5441
- }
5442
- if ($callback instanceof Callback) {
5443
- $paramStructure = $callback->params;
5444
- $callback = $callback->callback;
5445
- }
5446
- if (! $paramStructure)
5447
- return call_user_func_array($callback, $params);
5448
- $p = 0;
5449
- foreach($paramStructure as $i => $v) {
5450
- $paramStructure[$i] = $v instanceof CallbackParam
5451
- ? $params[$p++]
5452
- : $v;
5453
- }
5454
- return call_user_func_array($callback, $paramStructure);
5455
- }
5456
- /**
5457
- * Merge 2 phpQuery objects.
5458
- * @param array $one
5459
- * @param array $two
5460
- * @protected
5461
- * @todo node lists, phpQueryObject
5462
- */
5463
- public static function merge($one, $two) {
5464
- $elements = $one->elements;
5465
- foreach($two->elements as $node) {
5466
- $exists = false;
5467
- foreach($elements as $node2) {
5468
- if ($node2->isSameNode($node))
5469
- $exists = true;
5470
- }
5471
- if (! $exists)
5472
- $elements[] = $node;
5473
- }
5474
- return $elements;
5475
- // $one = $one->newInstance();
5476
- // $one->elements = $elements;
5477
- // return $one;
5478
- }
5479
- /**
5480
- *
5481
- * @param $array
5482
- * @param $callback
5483
- * @param $invert
5484
- * @return unknown_type
5485
- * @link http://docs.jquery.com/Utilities/jQuery.grep
5486
- */
5487
- public static function grep($array, $callback, $invert = false) {
5488
- $result = array();
5489
- foreach($array as $k => $v) {
5490
- $r = call_user_func_array($callback, array($v, $k));
5491
- if ($r === !(bool)$invert)
5492
- $result[] = $v;
5493
- }
5494
- return $result;
5495
- }
5496
- public static function unique($array) {
5497
- return array_unique($array);
5498
- }
5499
- /**
5500
- *
5501
- * @param $function
5502
- * @return unknown_type
5503
- * @TODO there are problems with non-static methods, second parameter pass it
5504
- * but doesnt verify is method is really callable
5505
- */
5506
- public static function isFunction($function) {
5507
- return is_callable($function);
5508
- }
5509
- public static function trim($str) {
5510
- return trim($str);
5511
- }
5512
- /* PLUGINS NAMESPACE */
5513
- /**
5514
- *
5515
- * @param $url
5516
- * @param $callback
5517
- * @param $param1
5518
- * @param $param2
5519
- * @param $param3
5520
- * @return phpQueryObject
5521
- */
5522
- public static function browserGet($url, $callback, $param1 = null, $param2 = null, $param3 = null) {
5523
- if (self::plugin('WebBrowser')) {
5524
- $params = func_get_args();
5525
- return self::callbackRun(array(self::$plugins, 'browserGet'), $params);
5526
- } else {
5527
- self::debug('WebBrowser plugin not available...');
5528
- }
5529
- }
5530
- /**
5531
- *
5532
- * @param $url
5533
- * @param $data
5534
- * @param $callback
5535
- * @param $param1
5536
- * @param $param2
5537
- * @param $param3
5538
- * @return phpQueryObject
5539
- */
5540
- public static function browserPost($url, $data, $callback, $param1 = null, $param2 = null, $param3 = null) {
5541
- if (self::plugin('WebBrowser')) {
5542
- $params = func_get_args();
5543
- return self::callbackRun(array(self::$plugins, 'browserPost'), $params);
5544
- } else {
5545
- self::debug('WebBrowser plugin not available...');
5546
- }
5547
- }
5548
- /**
5549
- *
5550
- * @param $ajaxSettings
5551
- * @param $callback
5552
- * @param $param1
5553
- * @param $param2
5554
- * @param $param3
5555
- * @return phpQueryObject
5556
- */
5557
- public static function browser($ajaxSettings, $callback, $param1 = null, $param2 = null, $param3 = null) {
5558
- if (self::plugin('WebBrowser')) {
5559
- $params = func_get_args();
5560
- return self::callbackRun(array(self::$plugins, 'browser'), $params);
5561
- } else {
5562
- self::debug('WebBrowser plugin not available...');
5563
- }
5564
- }
5565
- /**
5566
- *
5567
- * @param $code
5568
- * @return string
5569
- */
5570
- public static function php($code) {
5571
- return self::code('php', $code);
5572
- }
5573
- /**
5574
- *
5575
- * @param $type
5576
- * @param $code
5577
- * @return string
5578
- */
5579
- public static function code($type, $code) {
5580
- return "<$type><!-- ".trim($code)." --></$type>";
5581
- }
5582
-
5583
- public static function __callStatic($method, $params) {
5584
- return call_user_func_array(
5585
- array(phpQuery::$plugins, $method),
5586
- $params
5587
- );
5588
- }
5589
- protected static function dataSetupNode($node, $documentID) {
5590
- // search are return if alredy exists
5591
- foreach(phpQuery::$documents[$documentID]->dataNodes as $dataNode) {
5592
- if ($node->isSameNode($dataNode))
5593
- return $dataNode;
5594
- }
5595
- // if doesn't, add it
5596
- phpQuery::$documents[$documentID]->dataNodes[] = $node;
5597
- return $node;
5598
- }
5599
- protected static function dataRemoveNode($node, $documentID) {
5600
- // search are return if alredy exists
5601
- foreach(phpQuery::$documents[$documentID]->dataNodes as $k => $dataNode) {
5602
- if ($node->isSameNode($dataNode)) {
5603
- unset(self::$documents[$documentID]->dataNodes[$k]);
5604
- unset(self::$documents[$documentID]->data[ $dataNode->dataID ]);
5605
- }
5606
- }
5607
- }
5608
- public static function data($node, $name, $data, $documentID = null) {
5609
- if (! $documentID)
5610
- // TODO check if this works
5611
- $documentID = self::getDocumentID($node);
5612
- $document = phpQuery::$documents[$documentID];
5613
- $node = self::dataSetupNode($node, $documentID);
5614
- if (! isset($node->dataID))
5615
- $node->dataID = ++phpQuery::$documents[$documentID]->uuid;
5616
- $id = $node->dataID;
5617
- if (! isset($document->data[$id]))
5618
- $document->data[$id] = array();
5619
- if (! is_null($data))
5620
- $document->data[$id][$name] = $data;
5621
- if ($name) {
5622
- if (isset($document->data[$id][$name]))
5623
- return $document->data[$id][$name];
5624
- } else
5625
- return $id;
5626
- }
5627
- public static function removeData($node, $name, $documentID) {
5628
- if (! $documentID)
5629
- // TODO check if this works
5630
- $documentID = self::getDocumentID($node);
5631
- $document = phpQuery::$documents[$documentID];
5632
- $node = self::dataSetupNode($node, $documentID);
5633
- $id = $node->dataID;
5634
- if ($name) {
5635
- if (isset($document->data[$id][$name]))
5636
- unset($document->data[$id][$name]);
5637
- $name = null;
5638
- foreach($document->data[$id] as $name)
5639
- break;
5640
- if (! $name)
5641
- self::removeData($node, $name, $documentID);
5642
- } else {
5643
- self::dataRemoveNode($node, $documentID);
5644
- }
5645
- }
5646
- }
5647
- /**
5648
- * Plugins static namespace class.
5649
- *
5650
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
5651
- * @package phpQuery
5652
- * @todo move plugin methods here (as statics)
5653
- */
5654
- class phpQueryPlugins {
5655
- public function __call($method, $args) {
5656
- if (isset(phpQuery::$extendStaticMethods[$method])) {
5657
- $return = call_user_func_array(
5658
- phpQuery::$extendStaticMethods[$method],
5659
- $args
5660
- );
5661
- } else if (isset(phpQuery::$pluginsStaticMethods[$method])) {
5662
- $class = phpQuery::$pluginsStaticMethods[$method];
5663
- $realClass = "phpQueryPlugin_$class";
5664
- $return = call_user_func_array(
5665
- array($realClass, $method),
5666
- $args
5667
- );
5668
- return isset($return)
5669
- ? $return
5670
- : $this;
5671
- } else
5672
- throw new Exception("Method '{$method}' doesnt exist");
5673
- }
5674
- }
5675
- /**
5676
- * Shortcut to phpQuery::pq($arg1, $context)
5677
- * Chainable.
5678
- *
5679
- * @see phpQuery::pq()
5680
- * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
5681
- * @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
5682
- * @package phpQuery
5683
- */
5684
- function pq($arg1, $context = null) {
5685
- $args = func_get_args();
5686
- return call_user_func_array(
5687
- array('phpQuery', 'pq'),
5688
- $args
5689
- );
5690
- }
5691
- // add plugins dir and Zend framework to include path
5692
- set_include_path(
5693
- get_include_path()
5694
- .PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/'
5695
- .PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/plugins/'
5696
- );
5697
- // why ? no __call nor __get for statics in php...
5698
- // XXX __callStatic will be available in PHP 5.3
5699
- phpQuery::$plugins = new phpQueryPlugins();
5700
- // include bootstrap file (personal library config)
5701
- if (file_exists(dirname(__FILE__).'/phpQuery/bootstrap.php'))
5702
- require_once dirname(__FILE__).'/phpQuery/bootstrap.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/wp-plugin-dev-classes/class-wp-meta-box-page.php DELETED
@@ -1,664 +0,0 @@
1
- <?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
2
- if ( ! class_exists( 'WP_Meta_Box_Page_01' ) ):
3
- /**
4
- * WP_Meta_Box_Page_01 Class
5
- *
6
- * Creating a dashboard-like admin page with meta boxes
7
- *
8
- * Requires WordPress 3.0+ and PHP 5.2+
9
- *
10
- * Custom Actions:
11
- * - load action params: (1) this object
12
- * - meta_box_load
13
- * - meta_box_load-[page_slug]
14
- *
15
- * Custom Filters:
16
- * - page_title filter params: (1) default content, (2) this object
17
- * - meta_box_page_title
18
- * - meta_box_page_title-[page_slug]
19
- *
20
- * - page_content filter params: (1) default content, (2) this object
21
- * - meta_box_page_content
22
- * - meta_box_page_content-[page_slug]
23
- *
24
- * - screen_settings filter params: (1) default content, (2) current screen object, (3) this object
25
- * - meta_box_screen_settings
26
- * - meta_box_screen_settings-[page_slug]
27
- *
28
- * - contextual_help filter params: (1) default content, (2) current screen id, (3) current screen object, (4) this object
29
- * - meta_box_contextual_help
30
- * - meta_box_contextual_help-[page_slug]
31
- *
32
- * @version 0.1
33
- * @author Victor Villaverde Laan
34
- * @link http://www.freelancephp.net/
35
- * @license Dual licensed under the MIT and GPL licenses
36
- */
37
- class WP_Meta_Box_Page_01 {
38
-
39
- /**
40
- * Default settings
41
- * @var array
42
- */
43
- private static $default_settings = array(
44
- // Page title
45
- 'page_title' => NULL, // Default will be set equal to $menu_title
46
-
47
- // Menu title
48
- 'menu_title' => NULL, // Default will be set equal to $page_title
49
-
50
- // Page slug
51
- 'page_slug' => NULL, // Default will be set to: sanitize_title_with_dashes( $menu_title )
52
-
53
- // Default number of columns
54
- 'default_columns' => 2,
55
-
56
- // Column widths
57
- 'column_widths' => array(
58
- 1 => array( 99 ),
59
- 2 => array( 49, 49 ),
60
- 3 => array( 32.33, 32.33, 32.33 ),
61
- 4 => array( 24, 24, 24, 24 ),
62
- ),
63
-
64
- // Add page method
65
- 'add_page_method' => 'add_options_page', // OR: add_menu_page, add_object_page, add_submenu_page
66
-
67
- // Extra params for the add_page_method
68
-
69
- // Capability
70
- 'capability' => 'manage_options', // Optional for all methods
71
-
72
- // Parent slug
73
- 'parent_slug' => NULL, // Nescessary when using "add_submenu_page"
74
-
75
- // Url to the icon to be used for the menu ( around 20 x 20 pixels )
76
- 'icon_url' => NULL, // Only for "add_menu_page" or "add_object_page"
77
-
78
- // The position in the menu order this menu should appear
79
- 'position' => NULL, // Only for "add_menu_page", default on bottom of the menu
80
- );
81
-
82
- /**
83
- * Settings
84
- * @var array
85
- */
86
- private $settings = array();
87
-
88
- /**
89
- * Meta boxes
90
- * @var array
91
- */
92
- private $meta_boxes = array();
93
-
94
- /**
95
- * Pagehook ( will be set when page is created )
96
- * @var string
97
- */
98
- protected $pagehook = NULL;
99
-
100
-
101
- /**
102
- * Initialize
103
- * @param array $settings Optional
104
- */
105
- public function init( $settings = NULL, $load_callback = NULL ) {
106
- // get settings of child class
107
- $child_settings = $this->settings;
108
-
109
- // set settings in 3 steps...
110
- // (1) first set the default options
111
- $this->set_setting( self::$default_settings );
112
-
113
- // (2) set child settings
114
- if ( ! empty( $child_settings ) )
115
- $this->set_setting( $child_settings );
116
-
117
- // (3) set param settings
118
- if ( $settings !== NULL )
119
- $this->set_setting( $settings );
120
-
121
- // actions
122
- add_action( 'admin_menu', array( $this, 'call_admin_menu' ) );
123
-
124
- // add load action
125
- if ( $load_callback !== NULL )
126
- add_action( 'meta_box_load-'. $this->get_setting( 'page_slug' ), $load_callback );
127
- }
128
-
129
- /**
130
- * Set default setting value
131
- * @param mixed $key Also possible to give an array of key/value pairs
132
- * @param mixed $value Optional
133
- * @static
134
- */
135
- public static function set_default_setting( $key, $value = NULL ) {
136
- if ( is_array( $key ) ) {
137
- foreach ( $key AS $k => $v )
138
- self::set_default_setting( $k, $v );
139
- } else {
140
- self::$default_settings[ $key ] = $value;
141
- }
142
- }
143
-
144
- /**
145
- * Set setting value
146
- * @param mixed $key Also possible to give an array of key/value pairs
147
- * @param mixed $value Optional
148
- * @return $this For chaining
149
- */
150
- public function set_setting( $key, $value = NULL ) {
151
- if ( is_array( $key ) ) {
152
- foreach ( $key AS $k => $v )
153
- $this->set_setting( $k, $v );
154
- } else {
155
- $this->settings[ $key ] = $value;
156
- }
157
-
158
- // auto-set related prop values
159
- if ( $value !== NULL ) {
160
- if ( $key == 'menu_title' AND $this->get_setting( 'page_title' ) === NULL )
161
- $this->set_setting( 'page_title', $value );
162
-
163
- if ( $key == 'page_title' AND $this->get_setting( 'menu_title' ) === NULL )
164
- $this->set_setting( 'menu_title', $value );
165
-
166
- if ( ( $key == 'menu_title' OR $key == 'page_title' ) AND $this->get_setting( 'page_slug' ) === NULL ) {
167
- $new_val = $value;
168
- $new_val = sanitize_title_with_dashes( $new_val );
169
- $new_val = strtolower( $new_val );
170
-
171
- // check for valid page_slug
172
- if ( ! preg_match( '/^[a-z_-]+$/', $new_val ) ) {
173
- $new_val = str_replace(
174
- array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ),
175
- array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' ),
176
- $new_val
177
- );
178
-
179
- $new_val = ereg_replace( '[^a-z_-]', '', $new_val );
180
- }
181
-
182
- $this->set_setting( 'page_slug', $new_val );
183
- }
184
- }
185
-
186
- return $this;
187
- }
188
-
189
- /**
190
- * Get setting value
191
- * @param string $key Optional, when NULL will return array of all options
192
- * @param mixed $default Optional, return default when key cannot be found OR is NULL
193
- * @return mixed
194
- */
195
- public function get_setting( $key = NULL, $default = NULL ) {
196
- if ( $key === NULL )
197
- return $this->settings;
198
-
199
- if ( key_exists( $key, $this->settings ) )
200
- return ( $this->settings[ $key ] === NULL ) ? $default : $this->settings[ $key ];
201
-
202
- return $default;
203
- }
204
-
205
- /**
206
- * Helper for adding a meta box
207
- * @param string $title Title of the meta box
208
- * @param string $callback Callback for the meta box content
209
- * @param mixed $context Optional, add meta box to this column (normal = 1, side = 2, column3 = 3, column4 = 4)
210
- * @param string $priority Optional, the priority within the context where the boxes should show ( 'high', 'core', 'default' or 'low' )
211
- * @return $this For chaining
212
- */
213
- public function add_meta_box( $title, $callback, $context = 'normal', $id = NULL, $priority = 'default' ) {
214
- $this->meta_boxes[] = array(
215
- 'id' => $id,
216
- 'title' => $title,
217
- 'callback' => $callback,
218
- 'context' => $context,
219
- 'priority' => $priority,
220
- );
221
-
222
- return $this;
223
- }
224
-
225
- /**
226
- * Add callback to "meta_box_load-[page]" action, only applied for this page/object
227
- * @param mixed $callback Callback function
228
- * @return $this
229
- */
230
- public function add_load_action( $callback ) {
231
- add_filter( 'meta_box_load-' . $this->get_setting( 'page_slug' ), $callback );
232
- return $this;
233
- }
234
-
235
- /**
236
- * Add callback to "meta_box_load" action, applied to all instances of this class
237
- * @param mixed $callback Callback function
238
- * @static
239
- */
240
- public static function add_global_load_action( $callback ) {
241
- add_filter( 'meta_box_load', $callback );
242
- }
243
-
244
- /**
245
- * Add callback to "meta_box_screen_settings-[page]" filter, only applied for this page/object
246
- * @param mixed $callback Callback function
247
- * @return $this
248
- */
249
- public function add_screen_settings_filter( $callback ) {
250
- add_filter( 'meta_box_screen_settings-' . $this->get_setting( 'page_slug' ), $callback );
251
- return $this;
252
- }
253
-
254
- /**
255
- * Add callback to "meta_box_contextual_help-[page]" filter, only applied for this page/object
256
- * @param mixed $callback Callback function
257
- * @return $this
258
- */
259
- public function add_contextual_help_filter( $callback ) {
260
- add_filter( 'meta_box_contextual_help-' . $this->get_setting( 'page_slug' ), $callback );
261
- return $this;
262
- }
263
-
264
- /**
265
- * Add callback to "meta_box_page_title-[page]" filter, only applied for this page/object
266
- * @param mixed $callback Callback function
267
- * @return $this
268
- */
269
- public function add_title_filter( $callback ) {
270
- add_filter( 'meta_box_page_title-' . $this->get_setting( 'page_slug' ), $callback );
271
- return $this;
272
- }
273
-
274
- /**
275
- * Add callback to "meta_box_page_content" filter, applied to all instances of this class
276
- * @param mixed $callback Callback function
277
- * @static
278
- */
279
- public static function add_global_title_filter( $callback ) {
280
- add_filter( 'meta_box_page_title', $callback );
281
- }
282
-
283
- /**
284
- * Add callback to "meta_box_page_content-[page]" filter, only applied for this page/object
285
- * @param mixed $callback Callback function
286
- * @return $this
287
- */
288
- public function add_content_filter( $callback ) {
289
- add_filter( 'meta_box_page_title-' . $this->get_setting( 'page_slug' ), $callback );
290
- return $this;
291
- }
292
-
293
- /**
294
- * Add callback to "meta_box_page_content" filter, applied to all instances of this class
295
- * @param mixed $callback Callback function
296
- * @static
297
- */
298
- public static function add_global_content_filter( $callback ) {
299
- add_filter( 'meta_box_page_title', $callback );
300
- }
301
-
302
- /**
303
- * Admin menu callback
304
- */
305
- public function call_admin_menu() {
306
- // add page
307
- switch ( $this->get_setting( 'add_page_method' ) ) {
308
- case 'add_menu_page':
309
- $this->pagehook = add_menu_page(
310
- $this->get_setting( 'page_title' ),
311
- $this->get_setting( 'menu_title' ),
312
- $this->get_setting( 'capability' ),
313
- $this->get_setting( 'page_slug' ),
314
- array( $this, 'call_page_content' ),
315
- $this->get_setting( 'icon_url' ),
316
- $this->get_setting( 'position' )
317
- );
318
- break;
319
-
320
- case 'add_object_page':
321
- $this->pagehook = add_object_page(
322
- $this->get_setting( 'page_title' ),
323
- $this->get_setting( 'menu_title' ),
324
- $this->get_setting( 'capability' ),
325
- $this->get_setting( 'page_slug' ),
326
- array( $this, 'call_page_content' ),
327
- $this->get_setting( 'icon_url' )
328
- );
329
- break;
330
-
331
- case 'add_submenu_page':
332
- $this->pagehook = add_submenu_page(
333
- $this->get_setting( 'parent_slug' ),
334
- $this->get_setting( 'page_title' ),
335
- $this->get_setting( 'menu_title' ),
336
- $this->get_setting( 'capability' ),
337
- $this->get_setting( 'page_slug' ),
338
- array( $this, 'call_page_content' )
339
- );
340
- break;
341
-
342
- case 'add_options_page':
343
- default:
344
- $this->pagehook = add_options_page(
345
- $this->get_setting( 'page_title' ),
346
- $this->get_setting( 'menu_title' ),
347
- $this->get_setting( 'capability' ),
348
- $this->get_setting( 'page_slug' ),
349
- array( $this, 'call_page_content' )
350
- );
351
- break;
352
-
353
- }
354
-
355
- // execute action
356
- do_action( 'meta_box_load', $this );
357
-
358
- // load page
359
- add_action( 'load-' . $this->pagehook, array( $this, 'call_load_page' ) );
360
- }
361
-
362
- /**
363
- * Admin head callback
364
- */
365
- public function call_admin_head() {
366
- ?>
367
- <style type="text/css">
368
- .postbox-container { padding-right:1%; float:left ; /* for WP < 3.1 */ }
369
- .postbox-container .meta-box-sortables { min-height:200px; }
370
- .postbox-container .postbox { min-width:0; }
371
- .postbox-container .postbox .inside { margin:10px 0 ; padding:0 10px; } /* for WP < 3.2 */
372
- </style>
373
- <?php
374
- }
375
-
376
- /**
377
- * Load page callback
378
- */
379
- public function call_load_page() {
380
- // execute action
381
- do_action( 'meta_box_load-' . $this->get_setting( 'page_slug' ), $this );
382
-
383
- // add script for meta boxes
384
- wp_enqueue_script( 'postbox' );
385
-
386
- // add to admin head
387
- add_action( 'admin_head', array( $this, 'call_admin_head' ) );
388
-
389
- // add screen settings filter
390
- add_filter( 'screen_settings', array( $this, 'call_screen_settings') );
391
-
392
- // add help text
393
- add_filter( 'contextual_help', array( $this, 'call_contextual_help' ) );
394
-
395
- // columns
396
- if ( function_exists( 'add_screen_option' ) ) {
397
- $count = count( $this->get_setting( 'column_widths' ) );
398
-
399
- add_screen_option( 'layout_columns', array(
400
- 'max' => $count,
401
- 'default' => min( $count, $this->get_setting( 'default_columns' ) )
402
- ));
403
- }
404
-
405
- // add meta boxes
406
- $nr = 0;
407
- foreach ( $this->meta_boxes AS $box ) {
408
- $title = $box[ 'title' ];
409
- $id = ( isset( $box[ 'id' ] ) ) ? $box[ 'id' ] : sanitize_title_with_dashes( $title .'-'. ++$nr, 'meta-box-' . $nr );
410
- $callback = ( is_string( $box[ 'callback' ] ) && method_exists( $this, $box[ 'callback' ] ) ) ? array( $this, $box[ 'callback' ] ) : $box[ 'callback' ];
411
- $context = $box[ 'context' ];
412
- $priority = $box[ 'priority' ];
413
-
414
- // set context
415
- if ( $context == 2 OR strtolower( $context ) == 'side' ) {
416
- $context = 'side';
417
- } elseif ( $context == 3 OR strtolower( $context ) == 'column3' ) {
418
- $context = 'column3';
419
- } elseif ( $context == 4 OR strtolower( $context ) == 'column4' ) {
420
- $context = 'column4';
421
- } else { // default
422
- $context = 'normal';
423
- }
424
-
425
- // add meta box
426
- add_meta_box( $id, $title, $callback, $this->pagehook, $context, $priority ); // $callback_args, doesn't seem to work
427
- }
428
- }
429
-
430
- /**
431
- * Screen settings (callback)
432
- * @param string $content
433
- * @return string
434
- */
435
- public function call_screen_settings( $content ) {
436
- if ( self::get_current_screen()->id == convert_to_screen( $this->pagehook )->id ) {
437
- // apply filters for this meta box page
438
- $content = apply_filters( 'meta_box_screen_settings-' . $this->get_setting( 'page_slug' ), $content, $this->get_current_screen(), $this );
439
- }
440
-
441
- return $content;
442
- }
443
-
444
- /**
445
- * Contextual help (callback)
446
- * @param string $content
447
- * @return string
448
- */
449
- public function call_contextual_help( $content ) {
450
- $current_screen = $this->get_current_screen();
451
-
452
- if ( $current_screen->id == convert_to_screen( $this->pagehook )->id ) {
453
- // apply filters for this meta box page
454
- $content = apply_filters( 'meta_box_contextual_help-' . $this->get_setting( 'page_slug' ), $content, $current_screen->id, $current_screen, $this );
455
- }
456
-
457
- return $content;
458
- }
459
-
460
- /**
461
- * Display admin page content (callback)
462
- */
463
- public function call_page_content() {
464
- echo '<div class="wrap">';
465
-
466
- // page title
467
- $meta_boxes_page_title = apply_filters( 'meta_box_page_title', $this->get_page_title(), $this );
468
- echo apply_filters( 'meta_box_page_title-' . $this->get_setting( 'page_slug' ), $meta_boxes_page_title, $this );
469
-
470
- // page content
471
- $meta_boxes_content = apply_filters( 'meta_box_page_content', $this->get_page_content(), $this );
472
- echo apply_filters( 'meta_box_page_content-' . $this->get_setting( 'page_slug' ), $meta_boxes_content, $this );
473
-
474
- echo '</div>';
475
- }
476
-
477
- /**
478
- * Get page title
479
- * Can be changed by using adding filters, see add_title_filter()
480
- * @return string
481
- */
482
- private function get_page_title() {
483
- return '<h2>' . get_admin_page_title() . '</h2>';
484
- }
485
-
486
- /**
487
- * Get meta boxes content. Can be changed by adding filters, see add_content_filter()
488
- * @return string
489
- */
490
- private function get_page_content() {
491
- return self::get_ob_callback( array( $this, 'show_meta_boxes' ) );
492
- }
493
-
494
- /**
495
- * Display meta boxes content
496
- */
497
- private function show_meta_boxes() {
498
- $opt_column_widths = $this->get_setting( 'column_widths' );
499
- $hide2 = $hide3 = $hide4 = '';
500
- switch ( self::get_screen_layout_columns( $this->get_setting( 'default_columns' ) ) ) {
501
- case 4:
502
- $column_widths = $opt_column_widths[ 4 ];
503
- break;
504
- case 3:
505
- $column_widths = $opt_column_widths[ 3 ];
506
- $hide4 = 'display:none;';
507
- break;
508
- case 2:
509
- $column_widths = $opt_column_widths[ 2 ];
510
- $hide3 = $hide4 = 'display:none;';
511
- break;
512
- default:
513
- $column_widths = $opt_column_widths[ 1 ];
514
- $hide2 = $hide3 = $hide4 = 'display:none;';
515
- }
516
-
517
- $column_widths = array_pad( $column_widths, 4, 0 );
518
- ?>
519
- <div id='<?php echo $this->pagehook ?>-widgets' class='metabox-holder'>
520
- <div class='postbox-container' style='width:<?php echo $column_widths[0] ?>%'>
521
- <?php do_meta_boxes( $this->pagehook, 'normal', '' ); ?>
522
- </div>
523
-
524
- <div class='postbox-container' style='<?php echo $hide2 ?>width:<?php echo $column_widths[1] ?>%'>
525
- <?php do_meta_boxes( $this->pagehook, 'side', '' ); ?>
526
- </div>
527
-
528
- <div class='postbox-container' style='<?php echo $hide3 ?>width:<?php echo $column_widths[2] ?>%'>
529
- <?php do_meta_boxes( $this->pagehook, 'column3', '' ); ?>
530
- </div>
531
-
532
- <div class='postbox-container' style='<?php echo $hide4 ?>width:<?php echo $column_widths[3] ?>%'>
533
- <?php do_meta_boxes( $this->pagehook, 'column4', '' ); ?>
534
- </div>
535
- </div>
536
-
537
- <form style="display:none" method="get" action="">
538
- <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
539
- <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
540
- </form>
541
-
542
- <script type="text/javascript">
543
- //<![CDATA[
544
- jQuery( document ).ready( function( $ ){
545
- var columnWidths = <?php echo json_encode( $opt_column_widths ) ?>,
546
- $boxes = $( '.postbox-container' ),
547
- setColumnWidths = function () {
548
- var c = $( 'input[name="screen_columns"]:checked' ).val();
549
-
550
- // first hide all boxes
551
- $boxes.hide();
552
-
553
- // set width and show boxes
554
- for ( var x = 0; x < columnWidths[ c ].length; x++ ) {
555
- $boxes.eq( x )
556
- .css( 'width', columnWidths[ c ][ x ]+ '%' )
557
- .show();
558
- }
559
- };
560
-
561
- // radio screen columns
562
- $( 'input[name="screen_columns"]' )
563
- .click(function(){
564
- if ( $( 'input[name="screen_columns"]:checked' ).val() == $( this ).val() ) {
565
- setTimeout(function(){
566
- setColumnWidths();
567
- }, 1 );
568
- }
569
- })
570
- .change(function( e ){
571
- setColumnWidths();
572
-
573
- // prevent
574
- e.stopImmediatePropagation();
575
- });
576
-
577
- // trigger change event of selected column
578
- $( 'input[name="screen_columns"]:checked' ).change();
579
-
580
- <?php if ( self::wp_version( '3.2', '<' ) ): ?>
581
- // for WP < 3.2
582
-
583
- // close postboxes that should be closed
584
- $( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' );
585
-
586
- // Loading saved screen settings
587
- postboxes.add_postbox_toggles( '<?php echo $this->pagehook ?>' );
588
- <?php endif; ?>
589
- });
590
- //]]>
591
- </script>
592
- <?php
593
- }
594
-
595
- /**
596
- * Static helpers
597
- */
598
-
599
- /**
600
- * Get content displayed by given callback
601
- * @param mixed $callback
602
- * @return string
603
- * @static
604
- */
605
- public static function get_ob_callback( $callback ) {
606
- // start output buffer
607
- ob_start();
608
-
609
- // call callback
610
- call_user_func( $callback );
611
-
612
- // get the view content
613
- $content = ob_get_contents();
614
-
615
- // clean output buffer
616
- ob_end_clean();
617
-
618
- return $content;
619
- }
620
-
621
- /**
622
- * Get current version of WP or compare versions
623
- * @global string $wp_version
624
- * @return mixed
625
- * @static
626
- */
627
- public static function wp_version( $compare_version = NULL, $operator = NULL ) {
628
- global $wp_version;
629
- $cur_wp_version = preg_replace( '/-.*$/', '', $wp_version );
630
-
631
- if ( $compare_version === NULL )
632
- return $cur_wp_version;
633
-
634
- // check comparison
635
- return version_compare( $cur_wp_version, $compare_version, $operator );
636
- }
637
-
638
- /**
639
- * Return global WP value of $screen_layout_columns
640
- * @global integer $screen_layout_columns
641
- * @return integer
642
- * @static
643
- */
644
- public static function get_screen_layout_columns( $default = NULL ) {
645
- global $screen_layout_columns;
646
- return ( empty( $screen_layout_columns ) ) ? $default : $screen_layout_columns;
647
- }
648
-
649
- /**
650
- * Return global WP value of $current_screen
651
- * @global string $current_screen
652
- * @return string
653
- * @static
654
- */
655
- public static function get_current_screen( $default = NULL ) {
656
- global $current_screen;
657
- return $current_screen;
658
- }
659
-
660
- } // End WP_Meta_Box_Page_01 Class
661
-
662
- endif;
663
-
664
- /* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/wp-plugin-dev-classes/class-wp-option-forms.php DELETED
@@ -1,374 +0,0 @@
1
- <?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
2
- if ( ! class_exists( 'WP_Option_Forms_01' ) ):
3
- /**
4
- * WP_Option_Forms_01 Class
5
- *
6
- * Simple class for creating option forms of a the same option_group.
7
- * Also with Ajax save support.
8
- *
9
- * Requires WordPress 3.0+ and PHP 5.2+
10
- *
11
- * @version 0.1
12
- * @author Victor Villaverde Laan
13
- * @link http://www.freelancephp.net/
14
- * @license Dual licensed under the MIT and GPL licenses
15
- */
16
- class WP_Option_Forms_01 {
17
-
18
- /**
19
- * Name used as prefix for saving option names
20
- * @var string
21
- */
22
- protected $name = NULL;
23
-
24
- /**
25
- * Option names and values
26
- * @var string
27
- */
28
- protected $options = array();
29
-
30
- /**
31
- * Current option name
32
- * @var string
33
- */
34
- protected $current_option = NULL;
35
-
36
-
37
- /**
38
- * Constructor
39
- * @param array $name
40
- * @param array $options Optional
41
- */
42
- public function __construct( $name, $options = array() ) {
43
- $this->name = sanitize_title_with_dashes( $name );
44
-
45
- // set option names
46
- foreach ( $options AS $option_name => $values ) {
47
- $this->add_option( $option_name, $values );
48
- }
49
-
50
- // actions
51
- add_action( 'wp_ajax_wpof_update_options', array( $this, 'call_wp_ajax' ) );
52
- add_action( 'admin_menu', array( $this, 'call_admin_menu' ) );
53
- }
54
-
55
- /**
56
- * Admin menu callback
57
- */
58
- public function call_admin_menu() {
59
- // Register settings
60
- foreach ( $this->options AS $option_name => $values ) {
61
- register_setting( $option_name, $option_name );
62
- }
63
-
64
- // script
65
- // wp_enqueue_script( 'option-forms', plugins_url( 'js/wp-option-forms.js', WP_EXTERNAL_LINKS_FILE ), array( 'jquery' ), '1.0' );
66
- }
67
-
68
- /**
69
- * Ajax call for saving option values
70
- */
71
- public function call_wp_ajax() {
72
- check_ajax_referer( 'wpof_update_options', 'wpof-nonce' );
73
-
74
- if ( ! current_user_can( 'manage_options' ) ) {
75
- die( '0' );
76
- }
77
-
78
- $option_name = $_POST[ 'ajax_option_name' ];
79
- $values = NULL;
80
-
81
- if ( isset( $_POST[ $option_name ] ) )
82
- $values = $_POST[ $option_name ];
83
-
84
- if ( ! is_array( $values ) ) {
85
- die( '0' );
86
- }
87
-
88
- $values = stripslashes_deep( $values );
89
-
90
- foreach ( $values as $key => $val ) {
91
- if ( $key === 'ignore' ) {
92
- // text area (don't remove line breaks)
93
- $values[ $key ] = filter_var( $val, FILTER_SANITIZE_STRING );
94
- } else {
95
- $values[ $key ] = sanitize_text_field( $val );
96
- }
97
- }
98
-
99
- update_option( $option_name, $values );
100
-
101
- die( '1' );
102
- }
103
-
104
- /**
105
- * Add option (or reset option when already exists)
106
- * @param string $option_name
107
- * @param array $default_values Optional
108
- * @return this
109
- */
110
- public function add_option( $option_name, $default_values = array() ) {
111
- // set values
112
- $saved_values = get_option( $this->name .'-'. $option_name );
113
-
114
- if ( empty( $saved_values ) ) {
115
- foreach ( $default_values AS $key => $value )
116
- $values[ $key ] = $value;
117
- } else {
118
- foreach ( $default_values AS $key => $value )
119
- $values[ $key ] = '';
120
-
121
- foreach ( $saved_values AS $key => $value )
122
- $values[ $key ] = $value;
123
- }
124
-
125
- // option and values
126
- $this->options[ $this->name .'-'. $option_name ] = $values;
127
- return $this;
128
- }
129
-
130
- /**
131
- * Set current option to use
132
- * @param string $option_name
133
- * @return this
134
- */
135
- public function set_current_option( $option_name ) {
136
- $this->current_option = $this->name .'-'. $option_name;
137
- return $this;
138
- }
139
-
140
- /**
141
- * Get opening form with all nescessary WP fields
142
- * @param boolean $ajaxSave Optional
143
- * @param array $attrs Optional
144
- * @return string
145
- */
146
- public function open_form( $ajaxSave = TRUE, $attrs = array() ) {
147
- // set class for ajax or non-ajax form
148
- $attrs[ 'class' ] = ( ( $ajaxSave ) ? 'ajax-form' : 'no-ajax-form' )
149
- . ( ( key_exists( 'class', $attrs ) ) ? ' '. $attrs[ 'class' ] : '' );
150
-
151
- // show start form
152
- $html = '';
153
- $html .= '<form method="post" action="options.php" '. $this->attrs( $attrs ) .'>';
154
-
155
- if ( $ajaxSave ) {
156
- $html .= wp_nonce_field( 'wpof_update_options', 'wpof-nonce', FALSE, FALSE );
157
- $html .= '<input type="hidden" name="action" value="wpof_update_options" />';
158
- $html .= '<input type="hidden" name="ajax_option_name" value="'. esc_attr( $this->current_option ) .'" />';
159
-
160
- // instead of using settings_fields();
161
- $html .= '<input type="hidden" name="option_page" value="' . esc_attr( $this->current_option ) . '" />';
162
- $html .= wp_nonce_field( $this->current_option . '-options', '_wpnonce', TRUE, FALSE );
163
- } else {
164
- // instead of using settings_fields();
165
- $html .= '<input type="hidden" name="option_page" value="' . esc_attr( $this->current_option ) . '" />';
166
- $html .= '<input type="hidden" name="action" value="update" />';
167
- $html .= wp_nonce_field( $this->current_option . '-options', '_wpnonce', TRUE, FALSE );
168
- }
169
-
170
- return $html;
171
- }
172
-
173
- /**
174
- * Get script for saving screen option
175
- * @param string $option_name
176
- * @param string $key
177
- * @return string
178
- */
179
- public function open_screen_option( $option_name, $key ) {
180
- $this->set_current_option( $option_name );
181
-
182
- $html = '';
183
- $html .= '<script type="text/javascript">' . "\n";
184
- $html .= '//<![CDATA[' . "\n";
185
- $html .= 'jQuery( document ).ready( function( $ ){' . "\n";
186
- $html .= "\t" . '// save screen option' . "\n";
187
- $html .= "\t" . '$( "#screen-meta #'. $key .'" )' . "\n";
188
- $html .= "\t\t" . '.change(function(){' . "\n";
189
- $html .= "\t\t\t" . 'var self = this;' . "\n";
190
- $html .= "\t\t\t" . '$.post( ajaxurl, {' . "\n";
191
- $html .= "\t\t\t\t" . 'action: "wpof_update_options",' . "\n";
192
- $html .= "\t\t\t\t" . '"wpof-nonce": "'. wp_create_nonce( 'wpof_update_options' ) .'",' . "\n";
193
- $html .= "\t\t\t\t" . 'ajax_option_name: "'. $this->current_option .'",' . "\n";
194
- $html .= "\t\t\t\t" . '"'. $this->field_name( $key ) .'": $( this ).val()' . "\n";
195
- $html .= "\t\t\t" . '}, function () {' . "\n";
196
- $html .= "\t\t\t\t" . '$( self ).trigger( "ajax_updated" );' . "\n";
197
- $html .= "\t\t\t" . '});' . "\n";
198
- $html .= "\t\t" . '});' . "\n";
199
- $html .= '});' . "\n";
200
- $html .= '//]]>' . "\n";
201
- $html .= '</script>' . "\n";
202
-
203
- return $html;
204
- }
205
-
206
- /**
207
- * Get closing form
208
- * @return string
209
- */
210
- public function close_form() {
211
- return '</form>';
212
- }
213
-
214
- /**
215
- * Text field
216
- * @param string $key
217
- * @param array $attrs Optional
218
- * @return string
219
- */
220
- public function text( $key, $attrs = array() ) {
221
- if ( ! key_exists( 'class', $attrs ) )
222
- $attrs[ 'class' ] = 'regular-text';
223
-
224
- return '<input type="text" '. $this->attrs( $attrs, $key, $this->value( $key ) ) .' />';
225
- }
226
-
227
- /**
228
- * Text field
229
- * @param string $key
230
- * @param array $attrs Optional
231
- * @return string
232
- */
233
- public function textarea( $key, $attrs = array() ) {
234
- if ( ! key_exists( 'class', $attrs ) )
235
- $attrs[ 'class' ] = 'large-text';
236
-
237
- return '<textarea '. $this->attrs( $attrs, $key ) .'>'. esc_textarea( $this->value( $key ) ) .'</textarea>';
238
- }
239
-
240
- /**
241
- * Radio field
242
- * @param string $key
243
- * @param mixed $value
244
- * @param array $attrs Optional
245
- * @return string
246
- */
247
- public function radio( $key, $value, $attrs = array() ) {
248
- $checked = ( $value == $this->value( $key ) ) ? ' checked="checked"' : '';
249
- return '<input type="radio" '. $this->attrs( $attrs, $key, $value )
250
- . $checked . ' />';
251
- }
252
-
253
- /**
254
- * Checkbox field
255
- * @param string $key
256
- * @param mixed $value
257
- * @param array $attrs Optional
258
- * @return string
259
- */
260
- public function checkbox( $key, $value, $attrs = array() ) {
261
- $checked = ( $value == $this->value( $key ) ) ? ' checked="checked"' : '';
262
- return '<input type="checkbox" '. $this->attrs( $attrs, $key, $value )
263
- . $checked . ' />';
264
- }
265
-
266
- /**
267
- * Select field
268
- * @param string $key
269
- * @param array $options Optional
270
- * @param array $attrs Optional
271
- * @return string
272
- */
273
- public function select( $key, $options = array(), $attrs = array() ) {
274
- $html = '<select '. $this->attrs( $attrs, $key ) .'>';
275
-
276
- foreach ( $options AS $value => $label ) {
277
- $selected = ( $value == $this->value( $key ) ) ? ' selected="selected"' : '';
278
- $html .= '<option value="'. esc_attr( $value ) .'"'. $selected .'>'. $label .'</option>';
279
- }
280
-
281
- $html .= '</select>';
282
- return $html;
283
- }
284
-
285
- /**
286
- * Submit button
287
- * @param array $attrs Optional
288
- * @return string
289
- */
290
- public function submit( $attrs = array() ) {
291
- // set class attr
292
- $attrs[ 'class' ] = 'button-primary'. ( ( key_exists( 'class', $attrs ) ) ? ' '. $attrs[ 'class' ] : '' );
293
-
294
- // show submit
295
- $html = '';
296
- $html .= '<p class="button-controls" style="text-align:right;">';
297
- $html .= '<img alt="" title="" class="ajax-feedback" src="'. get_bloginfo( 'url' ) .'/wp-admin/images/wpspin_light.gif" style="visibility: hidden;" />';
298
- $html .= '<input type="submit" '. $this->attrs( $attrs, '', __( 'Save Changes' ) ) .' />';
299
- $html .= '</p>';
300
- return $html;
301
- }
302
-
303
- /**
304
- * Get field name of given key
305
- * @param string $key
306
- * @return string
307
- */
308
- public function field_name( $key ) {
309
- return $this->current_option . '[' . $key . ']';
310
- }
311
-
312
- /**
313
- * Get value of given option key
314
- * @param string $key
315
- * @param mixed $default_value Optional
316
- * @param boolean $option_name Optional, search in given option_name instead of the current option
317
- * @return mixed
318
- */
319
- public function value( $key, $default_value = NULL, $option_name = NULL ) {
320
- if ( $option_name === NULL ) {
321
- $option = $this->current_option;
322
- } else {
323
- $option = $this->name . '-' . $option_name;
324
- }
325
-
326
- if (!isset($this->options[ $option ])) {
327
- return $default_value;
328
- }
329
-
330
- $values = $this->options[ $option ];
331
-
332
- return ( is_array( $values ) AND key_exists( $key, $values ) AND $values[ $key ] !== NULL ) ? $values[ $key ] : $default_value;
333
- }
334
-
335
- /**
336
- * Delete and unregister option
337
- */
338
- public function delete_options() {
339
- foreach ( $this->options AS $option_name => $values ) {
340
- delete_option( $option_name );
341
- }
342
- }
343
-
344
- /**
345
- * Get string of given attributes
346
- * @param array $attrs
347
- * @param string $key Optional
348
- * @param mixed $value Optional
349
- * @return string
350
- */
351
- protected function attrs( $attrs, $key = NULL, $value = NULL ) {
352
- $str = '';
353
-
354
- // set name, id, value attr
355
- if ( $key !== NULL ) {
356
- $str .= 'name="' . $this->field_name( $key ) .'" ';
357
- if ( ! key_exists( 'id', $attrs ) )
358
- $str .= 'id="' . esc_attr( $key ) .'" ';
359
- }
360
-
361
- if ( $value !== NULL )
362
- $str .= 'value="' . esc_attr( $value ) .'" ';
363
-
364
- foreach ( $attrs AS $attr => $value )
365
- $str .= $attr .'="'. esc_attr( $value ) .'" ';
366
-
367
- return $str;
368
- }
369
-
370
- } // End WP_Option_Forms_01
371
-
372
- endif;
373
-
374
- /* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/admin-wp-external-links.js DELETED
@@ -1,232 +0,0 @@
1
- /* WP External Links - Admin */
2
- /*global jQuery, window*/
3
- jQuery(function ($) {
4
- 'use strict';
5
-
6
- /* Tipsy Plugin */
7
- (function () {
8
- $.fn.tipsy = function (options) {
9
- options = $.extend({}, $.fn.tipsy.defaults, options);
10
-
11
- return this.each(function () {
12
- var opts = $.fn.tipsy.elementOptions(this, options);
13
-
14
- $(this).hover(function () {
15
- $.data(this, 'cancel.tipsy', true);
16
-
17
- var tip = $.data(this, 'active.tipsy');
18
- if (!tip) {
19
- tip = $('<div class="tipsy"><div class="tipsy-inner"/></div>');
20
- tip.css({position: 'absolute', zIndex: 100000});
21
- $.data(this, 'active.tipsy', tip);
22
- }
23
-
24
- if ($(this).attr('title') || typeof $(this).attr('original-title') !== 'string') {
25
- $(this).attr('original-title', $(this).attr('title') || '').removeAttr('title');
26
- }
27
-
28
- var title;
29
- if (typeof opts.title === 'string') {
30
- title = $(this).attr(opts.title === 'title' ? 'original-title' : opts.title);
31
- } else if (typeof opts.title === 'function') {
32
- title = opts.title.call(this);
33
- }
34
-
35
- tip.find('.tipsy-inner')[opts.html ? 'html' : 'text'](title || opts.fallback);
36
-
37
- var pos = $.extend({}, $(this).offset(), {width: this.offsetWidth, height: this.offsetHeight});
38
- tip.get(0).className = 'tipsy'; // reset classname in case of dynamic gravity
39
- tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body);
40
- var actualWidth = tip[0].offsetWidth, actualHeight = tip[0].offsetHeight;
41
- var gravity = (typeof opts.gravity == 'function') ? opts.gravity.call(this) : opts.gravity;
42
-
43
- switch (gravity.charAt(0)) {
44
- case 'n':
45
- tip.css({top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-north');
46
- break;
47
- case 's':
48
- tip.css({top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-south');
49
- break;
50
- case 'e':
51
- tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}).addClass('tipsy-east');
52
- break;
53
- case 'w':
54
- tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}).addClass('tipsy-west');
55
- break;
56
- }
57
-
58
- if (opts.fade) {
59
- tip.css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: 0.9});
60
- } else {
61
- tip.css({visibility: 'visible'});
62
- }
63
-
64
- }, function () {
65
- $.data(this, 'cancel.tipsy', false);
66
- var self = this;
67
- setTimeout(function () {
68
- if ($.data(this, 'cancel.tipsy')) return;
69
- var tip = $.data(self, 'active.tipsy');
70
- if (opts.fade) {
71
- tip.stop().fadeOut(function () { $(this).remove(); });
72
- } else {
73
- tip.remove();
74
- }
75
- }, 100);
76
- });
77
- });
78
- };
79
-
80
- // Overwrite this method to provide options on a per-element basis.
81
- // For example, you could store the gravity in a 'tipsy-gravity' attribute:
82
- // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
83
- // (remember - do not modify 'options' in place!)
84
- $.fn.tipsy.elementOptions = function (ele, options) {
85
- return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
86
- };
87
-
88
- $.fn.tipsy.defaults = {
89
- fade: false,
90
- fallback: '',
91
- gravity: 'w',
92
- html: false,
93
- title: 'title'
94
- };
95
-
96
- $.fn.tipsy.autoNS = function () {
97
- return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
98
- };
99
-
100
- $.fn.tipsy.autoWE = function () {
101
- return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
102
- };
103
-
104
- })(); // End Tipsy Plugin
105
-
106
-
107
- $('#setting-error-settings_updated').click(function () {
108
- $(this).hide();
109
- });
110
-
111
- // option filter page
112
- $('input#filter_page')
113
- .change(function () {
114
- var $i = $('input#filter_posts, input#filter_comments, input#filter_widgets');
115
-
116
- if ($(this).attr('checked')) {
117
- $i.attr('disabled', true)
118
- .attr('checked', true);
119
- } else {
120
- $i.attr('disabled', false);
121
- }
122
- })
123
- .change();
124
-
125
- // option use js
126
- $('input#nofollow')
127
- .change(function () {
128
- var $i = $('input#overwrite_follow');
129
-
130
- if ($(this).attr('checked')) {
131
- $i.attr('disabled', false);
132
- } else {
133
- $i.attr('disabled', true)
134
- .attr('checked', false);
135
- }
136
- })
137
- .change();
138
-
139
- // option use js
140
- $('input#use_js')
141
- .change(function () {
142
- var $i = $('input#load_in_footer');
143
-
144
- if ($(this).attr('checked')) {
145
- $i.attr('disabled', false);
146
- } else {
147
- $i.attr('disabled', true)
148
- .attr('checked', false);
149
- }
150
- })
151
- .change();
152
-
153
- // refresh page when updated menu position
154
- $('#menu_position').parents('form.ajax-form').on('ajax_saved_options', function () {
155
- var s = $(this).val() || '';
156
- window.location.href = s + (s.indexOf('?') > -1 ? '&' : '?') + 'page=wp_external_links&settings-updated=true';
157
- });
158
-
159
- // set tooltips
160
- $('.tooltip-help').css('margin', '0 5px').tipsy({ fade: true, live: true, gravity: 'w', fallback: 'No help text.' });
161
-
162
- // remove class to fix button background
163
- $('*[type="submit"]').removeClass('submit');
164
-
165
- // slide postbox
166
- $('.postbox').find('.handlediv, .hndle').click(function () {
167
- var $inside = $(this).parent().find('.inside');
168
-
169
- if ($inside.css('display') === 'block') {
170
- $inside.css({ display: 'none' });
171
- } else {
172
- $inside.css({ display: 'block' });
173
- }
174
- });
175
-
176
- });
177
-
178
- /* WP Options Form */
179
- /*global jQuery, ajaxurl*/
180
- jQuery(function ($) {
181
- 'use strict';
182
-
183
- // save function
184
- var saveAjaxForm = function (target) {
185
- var $this = $(target),
186
- $form = $this.parents('form'),
187
- // get ajax post values
188
- vals = $form.serializeArray();
189
-
190
- // disable button
191
- $this.attr('disabled', true);
192
-
193
- // show ajax loader
194
- $form.find('.ajax-feedback').css('visibility', 'visible');
195
-
196
- // save option values
197
- $.post(ajaxurl, vals, function (result) {
198
- var $msg = $('<strong>').insertBefore($this);
199
-
200
- if (result === '1') {
201
- $msg.html('Saved');
202
- } else {
203
- // save options, non-ajax fallback
204
- $form.find('[name="action"]').val('update');
205
- // normal submit
206
- $form.submit();
207
- }
208
-
209
- $msg.css({ margin: '0 5px' })
210
- .delay(1000)
211
- .fadeOut(function () {
212
- $(this).remove();
213
- });
214
-
215
- // enable button
216
- $this.attr('disabled', false);
217
-
218
- // hide ajax loader
219
- $form.find('.ajax-feedback').css('visibility', 'hidden');
220
-
221
- // trigger ajax_saved_options
222
- $form.trigger('ajax_saved_options', [result]);
223
- });
224
- };
225
-
226
- // add ajax post
227
- $('form.ajax-form input[type="submit"]').click(function (e) {
228
- saveAjaxForm(this);
229
- e.preventDefault();
230
- });
231
-
232
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/src/admin-wp-external-links.js DELETED
@@ -1,232 +0,0 @@
1
- /* WP External Links - Admin */
2
- /*global jQuery, window*/
3
- jQuery(function ($) {
4
- 'use strict';
5
-
6
- /* Tipsy Plugin */
7
- (function () {
8
- $.fn.tipsy = function (options) {
9
- options = $.extend({}, $.fn.tipsy.defaults, options);
10
-
11
- return this.each(function () {
12
- var opts = $.fn.tipsy.elementOptions(this, options);
13
-
14
- $(this).hover(function () {
15
- $.data(this, 'cancel.tipsy', true);
16
-
17
- var tip = $.data(this, 'active.tipsy');
18
- if (!tip) {
19
- tip = $('<div class="tipsy"><div class="tipsy-inner"/></div>');
20
- tip.css({position: 'absolute', zIndex: 100000});
21
- $.data(this, 'active.tipsy', tip);
22
- }
23
-
24
- if ($(this).attr('title') || typeof $(this).attr('original-title') !== 'string') {
25
- $(this).attr('original-title', $(this).attr('title') || '').removeAttr('title');
26
- }
27
-
28
- var title;
29
- if (typeof opts.title === 'string') {
30
- title = $(this).attr(opts.title === 'title' ? 'original-title' : opts.title);
31
- } else if (typeof opts.title === 'function') {
32
- title = opts.title.call(this);
33
- }
34
-
35
- tip.find('.tipsy-inner')[opts.html ? 'html' : 'text'](title || opts.fallback);
36
-
37
- var pos = $.extend({}, $(this).offset(), {width: this.offsetWidth, height: this.offsetHeight});
38
- tip.get(0).className = 'tipsy'; // reset classname in case of dynamic gravity
39
- tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body);
40
- var actualWidth = tip[0].offsetWidth, actualHeight = tip[0].offsetHeight;
41
- var gravity = (typeof opts.gravity == 'function') ? opts.gravity.call(this) : opts.gravity;
42
-
43
- switch (gravity.charAt(0)) {
44
- case 'n':
45
- tip.css({top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-north');
46
- break;
47
- case 's':
48
- tip.css({top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-south');
49
- break;
50
- case 'e':
51
- tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}).addClass('tipsy-east');
52
- break;
53
- case 'w':
54
- tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}).addClass('tipsy-west');
55
- break;
56
- }
57
-
58
- if (opts.fade) {
59
- tip.css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: 0.9});
60
- } else {
61
- tip.css({visibility: 'visible'});
62
- }
63
-
64
- }, function () {
65
- $.data(this, 'cancel.tipsy', false);
66
- var self = this;
67
- setTimeout(function () {
68
- if ($.data(this, 'cancel.tipsy')) return;
69
- var tip = $.data(self, 'active.tipsy');
70
- if (opts.fade) {
71
- tip.stop().fadeOut(function () { $(this).remove(); });
72
- } else {
73
- tip.remove();
74
- }
75
- }, 100);
76
- });
77
- });
78
- };
79
-
80
- // Overwrite this method to provide options on a per-element basis.
81
- // For example, you could store the gravity in a 'tipsy-gravity' attribute:
82
- // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
83
- // (remember - do not modify 'options' in place!)
84
- $.fn.tipsy.elementOptions = function (ele, options) {
85
- return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
86
- };
87
-
88
- $.fn.tipsy.defaults = {
89
- fade: false,
90
- fallback: '',
91
- gravity: 'w',
92
- html: false,
93
- title: 'title'
94
- };
95
-
96
- $.fn.tipsy.autoNS = function () {
97
- return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
98
- };
99
-
100
- $.fn.tipsy.autoWE = function () {
101
- return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
102
- };
103
-
104
- })(); // End Tipsy Plugin
105
-
106
-
107
- $('#setting-error-settings_updated').click(function () {
108
- $(this).hide();
109
- });
110
-
111
- // option filter page
112
- $('input#filter_page')
113
- .change(function () {
114
- var $i = $('input#filter_posts, input#filter_comments, input#filter_widgets');
115
-
116
- if ($(this).attr('checked')) {
117
- $i.attr('disabled', true)
118
- .attr('checked', true);
119
- } else {
120
- $i.attr('disabled', false);
121
- }
122
- })
123
- .change();
124
-
125
- // option use js
126
- $('input#nofollow')
127
- .change(function () {
128
- var $i = $('input#overwrite_follow');
129
-
130
- if ($(this).attr('checked')) {
131
- $i.attr('disabled', false);
132
- } else {
133
- $i.attr('disabled', true)
134
- .attr('checked', false);
135
- }
136
- })
137
- .change();
138
-
139
- // option use js
140
- $('input#use_js')
141
- .change(function () {
142
- var $i = $('input#load_in_footer');
143
-
144
- if ($(this).attr('checked')) {
145
- $i.attr('disabled', false);
146
- } else {
147
- $i.attr('disabled', true)
148
- .attr('checked', false);
149
- }
150
- })
151
- .change();
152
-
153
- // refresh page when updated menu position
154
- $('#menu_position').parents('form.ajax-form').on('ajax_saved_options', function () {
155
- var s = $(this).val() || '';
156
- window.location.href = s + (s.indexOf('?') > -1 ? '&' : '?') + 'page=wp_external_links&settings-updated=true';
157
- });
158
-
159
- // set tooltips
160
- $('.tooltip-help').css('margin', '0 5px').tipsy({ fade: true, live: true, gravity: 'w', fallback: 'No help text.' });
161
-
162
- // remove class to fix button background
163
- $('*[type="submit"]').removeClass('submit');
164
-
165
- // slide postbox
166
- $('.postbox').find('.handlediv, .hndle').click(function () {
167
- var $inside = $(this).parent().find('.inside');
168
-
169
- if ($inside.css('display') === 'block') {
170
- $inside.css({ display: 'none' });
171
- } else {
172
- $inside.css({ display: 'block' });
173
- }
174
- });
175
-
176
- });
177
-
178
- /* WP Options Form */
179
- /*global jQuery, ajaxurl*/
180
- jQuery(function ($) {
181
- 'use strict';
182
-
183
- // save function
184
- var saveAjaxForm = function (target) {
185
- var $this = $(target),
186
- $form = $this.parents('form'),
187
- // get ajax post values
188
- vals = $form.serializeArray();
189
-
190
- // disable button
191
- $this.attr('disabled', true);
192
-
193
- // show ajax loader
194
- $form.find('.ajax-feedback').css('visibility', 'visible');
195
-
196
- // save option values
197
- $.post(ajaxurl, vals, function (result) {
198
- var $msg = $('<strong>').insertBefore($this);
199
-
200
- if (result === '1') {
201
- $msg.html('Saved');
202
- } else {
203
- // save options, non-ajax fallback
204
- $form.find('[name="action"]').val('update');
205
- // normal submit
206
- $form.submit();
207
- }
208
-
209
- $msg.css({ margin: '0 5px' })
210
- .delay(1000)
211
- .fadeOut(function () {
212
- $(this).remove();
213
- });
214
-
215
- // enable button
216
- $this.attr('disabled', false);
217
-
218
- // hide ajax loader
219
- $form.find('.ajax-feedback').css('visibility', 'hidden');
220
-
221
- // trigger ajax_saved_options
222
- $form.trigger('ajax_saved_options', [result]);
223
- });
224
- };
225
-
226
- // add ajax post
227
- $('form.ajax-form input[type="submit"]').click(function (e) {
228
- saveAjaxForm(this);
229
- e.preventDefault();
230
- });
231
-
232
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/src/wp-external-links.js DELETED
@@ -1,69 +0,0 @@
1
- /* WP External Links */
2
- /*global jQuery, window*/
3
- (function (undefined) {
4
- 'use strict';
5
-
6
- var $ = jQuery === undefined ? null : jQuery;
7
-
8
- // add event handler
9
- function addEvt(el, evt, fn) {
10
- if (el.attachEvent) {
11
- // IE method
12
- el.attachEvent('on' + evt, fn);
13
- } else if (el.addEventListener) {
14
- // Standard JS method
15
- el.addEventListener(evt, fn, false);
16
- }
17
- }
18
-
19
- // open external link
20
- function openExtLink(a, evt) {
21
- var target = a.getAttribute('data-wpel-target');
22
- var href = a.getAttribute('href');
23
- var win;
24
-
25
- if (href && target) {
26
- // open link in a new window
27
- win = window.open(href, target);
28
- win.focus();
29
-
30
- // prevent default event action
31
- if (evt) {
32
- if (evt.preventDefault !== undefined) {
33
- evt.preventDefault();
34
- } else if (evt.returnValue !== undefined) {
35
- evt.returnValue = false;
36
- }
37
- }
38
- }
39
- }
40
-
41
- if ($) {
42
- // jQuery DOMready method
43
- $(function () {
44
- $('a').live('click', function (evt) {
45
- openExtLink(this, evt);
46
- });
47
- });
48
- } else {
49
- // use onload when jQuery not available
50
- addEvt(window, 'load', function () {
51
- var links = window.document.getElementsByTagName('a');
52
- var eventClick = function (evt) {
53
- var target = this instanceof Element ? this : evt.target;
54
- openExtLink(target, evt);
55
- };
56
- var a;
57
- var i;
58
-
59
- // check each <a> element
60
- for (i = 0; i < links.length; i += 1) {
61
- a = links[i];
62
-
63
- // click event for opening in a new window
64
- addEvt(a, 'click', eventClick);
65
- }
66
- });
67
- }
68
-
69
- }());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/src/wp-option-forms.js DELETED
@@ -1,55 +0,0 @@
1
- /* WP Options Form */
2
- /*global jQuery, ajaxurl*/
3
- jQuery(function ($) {
4
- 'use strict';
5
-
6
- // save function
7
- var saveAjaxForm = function (target) {
8
- var $this = $(target),
9
- $form = $this.parents('form'),
10
- // get ajax post values
11
- vals = $form.serializeArray();
12
-
13
- // disable button
14
- $this.attr('disabled', true);
15
-
16
- // show ajax loader
17
- $form.find('.ajax-feedback').css('visibility', 'visible');
18
-
19
- // save option values
20
- $.post(ajaxurl, vals, function (result) {
21
- var $msg = $('<strong>').insertBefore($this);
22
-
23
- if (result === '1') {
24
- $msg.html('Saved');
25
- } else {
26
- // save options, non-ajax fallback
27
- $form.find('[name="action"]').val('update');
28
- // normal submit
29
- $form.submit();
30
- }
31
-
32
- $msg.css({ margin: '0 5px' })
33
- .delay(1000)
34
- .fadeOut(function () {
35
- $(this).remove();
36
- });
37
-
38
- // enable button
39
- $this.attr('disabled', false);
40
-
41
- // hide ajax loader
42
- $form.find('.ajax-feedback').css('visibility', 'hidden');
43
-
44
- // trigger ajax_saved_options
45
- $form.trigger('ajax_saved_options', [result]);
46
- });
47
- };
48
-
49
- // add ajax post
50
- $('form.ajax-form input[type="submit"]').click(function (e) {
51
- saveAjaxForm(this);
52
- e.preventDefault();
53
- });
54
-
55
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/wp-external-links.js DELETED
@@ -1,2 +0,0 @@
1
- /* WP External Links */
2
- (function(e){"use strict";function n(e,t,n){if(e.attachEvent){e.attachEvent("on"+t,n)}else if(e.addEventListener){e.addEventListener(t,n,false)}}function r(t,n){var r=t.getAttribute("data-wpel-target");var i=t.getAttribute("href");var s;if(i&&r){s=window.open(i,r);s.focus();if(n){if(n.preventDefault!==e){n.preventDefault()}else if(n.returnValue!==e){n.returnValue=false}}}}var t=jQuery===e?null:jQuery;if(t){t(function(){t("body").on("click","a",function(e){r(this,e)})})}else{n(window,"load",function(){var e=window.document.getElementsByTagName("a");var t=function(e){var t=this instanceof Element?this:e.target;r(t,e)};var i;var s;for(s=0;s<e.length;s+=1){i=e[s];n(i,"click",t)}})}})()
 
 
libs/fwp/class-fwp-debug.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class FWP_Debug_1x0x0
4
+ *
5
+ * @package FWP
6
+ * @category WordPress Library
7
+ * @version 1.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WPRun-WordPress-Development
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ class FWP_Debug_1x0x0 extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * @var array
18
+ */
19
+ private $settings = array(
20
+ 'debug_func_name' => 'debug',
21
+ 'log_hooks' => false,
22
+ );
23
+
24
+ /**
25
+ * @var array
26
+ */
27
+ private static $benchmarks = array();
28
+
29
+ /**
30
+ * Initialize
31
+ * @param array $settings Optional
32
+ */
33
+ protected function init( array $settings = array() )
34
+ {
35
+ $this->settings = wp_parse_args( $settings, $this->settings );
36
+
37
+ $this->create_func();
38
+
39
+ if ( $this->settings[ 'log_hooks' ] ) {
40
+ register_shutdown_function( $this->get_callback( 'log_hooks' ) );
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Create logbal debug function
46
+ * @return void
47
+ */
48
+ private function create_func()
49
+ {
50
+ $func = $this->settings[ 'debug_func_name' ];
51
+
52
+ if ( function_exists( $func ) || !is_callable( $func, true ) ) {
53
+ return;
54
+ }
55
+
56
+ eval( 'function '. $func .'( $entry, $title = "" ) { FWP_Debug_1x0x0::log( $entry, $title ); }' );
57
+ }
58
+
59
+ /**
60
+ * @param mixed $entry
61
+ */
62
+ public static function log( $entry, $title = '' )
63
+ {
64
+ $content = '';
65
+
66
+ if ( !empty($title) ) {
67
+ $content = $title . ': ';
68
+ }
69
+
70
+ $content .= var_export( $entry, true );
71
+
72
+ error_log( $content );
73
+ }
74
+
75
+ /**
76
+ * Log all hooks being applied
77
+ * @global type $merged_filters
78
+ */
79
+ protected function log_hooks()
80
+ {
81
+ global $merged_filters;
82
+
83
+ self::log( $merged_filters, 'WP Hooks' );
84
+ }
85
+
86
+ /**
87
+ *
88
+ */
89
+ public static function start_benchmark( $label = 'benchmark' )
90
+ {
91
+ self::$benchmarks[ $label ][ 'start' ] = microtime( true );
92
+ }
93
+
94
+ /**
95
+ *
96
+ */
97
+ public static function end_benchmark( $label = 'benchmark' )
98
+ {
99
+ $end_time = microtime( true );
100
+ self::$benchmarks[ $label ][ 'end' ] = $end_time;
101
+ $start_time = self::$benchmarks[ $label ][ 'start' ];
102
+
103
+ $total_time = $end_time - $start_time;
104
+
105
+ self::log( $total_time, $label );
106
+
107
+ return $total_time;
108
+ }
109
+
110
+ }
111
+
112
+ /*?>*/
libs/fwp/class-fwp-dom-element.php ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class FWP_DOM_Element_1x0x0
4
+ *
5
+ * @package FWP
6
+ * @category WordPress Library
7
+ * @version 1.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WPRun-WordPress-Development
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ class FWP_DOM_Element_1x0x0 extends DOMElement
14
+ {
15
+
16
+ /**
17
+ * @var DOMDocument
18
+ */
19
+ private static $doc = null;
20
+
21
+ /**
22
+ * Factory method
23
+ * @param string $tagName
24
+ * @param string $content
25
+ * @param array $attributes
26
+ * @return FWP_DOM_Element_1x0x0
27
+ */
28
+ public static function create( $tagName, $content = null, array $attributes = array() )
29
+ {
30
+ if ( null === self::$doc ) {
31
+ self::$doc = self::createDocument();
32
+ }
33
+
34
+ $element = self::$doc->createElement( $tagName );
35
+ $element->setContent( $content );
36
+ $element->setAttributes( $attributes );
37
+
38
+ return $element;
39
+ }
40
+
41
+ /**
42
+ * Factory method
43
+ * @param string $tagName
44
+ * @param string $label
45
+ * @return WPEL_Link
46
+ */
47
+ public static function createDocument()
48
+ {
49
+ $doc = new DOMDocument();
50
+ $doc->registerNodeClass( 'DOMElement', get_called_class() );
51
+ return $doc;
52
+ }
53
+
54
+ /**
55
+ * Set element content
56
+ * @param string $content
57
+ */
58
+ public function setContent( $content )
59
+ {
60
+ $this->nodeValue = $content;
61
+ }
62
+
63
+ /**
64
+ * Get element content
65
+ * @return string
66
+ */
67
+ public function getContent()
68
+ {
69
+ return $this->nodeValue;
70
+ }
71
+
72
+ /**
73
+ * Get all attributes
74
+ * @return array
75
+ */
76
+ public function getAttributes()
77
+ {
78
+ $attributes = array();
79
+
80
+ foreach ( $this->attributes as $name => $node ) {
81
+ $attributes[ $name ] = $node->nodeValue;
82
+ }
83
+
84
+ return $attributes;
85
+ }
86
+
87
+ /**
88
+ * Set multiple attributes
89
+ * @param array $attributes
90
+ */
91
+ public function setAttributes( array $attributes )
92
+ {
93
+ foreach ( $attributes as $name => $value ) {
94
+ $this->setAttribute( $name, $value );
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Has given attribute value
100
+ * Used for attributes with multiple values like "class"
101
+ * @param string $name
102
+ * @param string $value
103
+ * @return boolean
104
+ */
105
+ public function hasAttributeValue( $name, $value )
106
+ {
107
+ if ( ! $this->hasAttribute( $name ) ) {
108
+ return false;
109
+ }
110
+
111
+ $attrValue = $this->getAttribute( $name );
112
+ $attrValues = explode( ' ', $attrValue );
113
+ return in_array( $value, $attrValues );
114
+ }
115
+
116
+ /**
117
+ * Add value to attribute
118
+ * Used for attributes with multiple values like "class"
119
+ * @param string $name
120
+ * @param string $value
121
+ * @return boolean
122
+ */
123
+ public function addToAttribute( $name, $value )
124
+ {
125
+ $attrValue = $this->getAttribute( $name );
126
+
127
+ if ( empty( $attrValue ) ) {
128
+ $this->setAttribute( $name, $value );
129
+ return;
130
+ }
131
+
132
+ if ( $this->hasAttributeValue( $name, $value ) ) {
133
+ return;
134
+ }
135
+
136
+ $this->setAttribute( $name, $attrValue .' '. $value );
137
+ }
138
+
139
+ /**
140
+ * Remove value from attribute
141
+ * Used for attributes with multiple values like "class"
142
+ * @param string $name
143
+ * @param string $value
144
+ * @return boolean
145
+ */
146
+ public function removeFromAttribute( $name, $value )
147
+ {
148
+ if ( ! $this->hasAttributeValue( $name, $value ) ) {
149
+ return;
150
+ }
151
+
152
+ $attrValue = $this->getAttribute( $name );
153
+ $attrValues = explode( ' ', $attrValue );
154
+
155
+ $newAttrValues = array_diff( $attrValues , array( $value ) );
156
+
157
+ $newAttrValue = implode( ' ', $newAttrValues );
158
+ $this->setAttribute( $name, $newAttrValue );
159
+ }
160
+
161
+ /**
162
+ * Remove all childs
163
+ */
164
+ public function removeAllChilds()
165
+ {
166
+ foreach ( $this->childNodes as $childNode ) {
167
+ $this->removeChild( $childNode );
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Prepend child element
173
+ * @param FWP_DOM_Element_1x0x0 $element
174
+ */
175
+ public function prependChild( $element )
176
+ {
177
+ if ( count( $this->childNodes ) > 0 ) {
178
+ $this->insertBefore( $element, $this->childNodes->item( 0 ) );
179
+ } else {
180
+ $this->appendChild( $element );
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Append HTML content
186
+ * @param string $html
187
+ */
188
+ public function appendHTML( $html )
189
+ {
190
+ $tmpDoc = self::createDocument();
191
+ $tmpDoc->loadHTML( $html );
192
+
193
+ foreach ( $tmpDoc->getElementsByTagName( 'body' )->item( 0 )->childNodes as $node ) {
194
+ $node = $this->ownerDocument->importNode( $node );
195
+ $this->appendChild( $node );
196
+ }
197
+
198
+ // method 2:
199
+ //$fragment = self::$doc->createDocumentFragment();
200
+ //$fragment->appendXML( $html );
201
+ //$this->appendChild( $fragment );
202
+ }
203
+
204
+ /**
205
+ * Return valid HTML5
206
+ * Instead of:
207
+ * <code>
208
+ * self::$doc->saveXML( $this );
209
+ * </code>
210
+ *
211
+ * @return string
212
+ */
213
+ public function getHTML()
214
+ {
215
+ $attributes = $this->getAttributes();
216
+
217
+ $link = '<'. $this->tagName;
218
+
219
+ foreach ( $attributes AS $name => $value ) {
220
+ if ( null === $value ) {
221
+ $link .= ' '. $name;
222
+ } else {
223
+ $link .= ' '. $name .'="'. esc_attr( $value ) .'"';
224
+ }
225
+ }
226
+
227
+ if ( null === $this->nodeValue ) {
228
+ $link .= '>';
229
+ } else {
230
+ $link .= '>';
231
+
232
+ foreach ( $this->childNodes as $childNode ) {
233
+ if ( $childNode instanceof DOMText ) {
234
+ $link .= $childNode->wholeText;
235
+ } elseif ( $childNode instanceof FWP_DOM_Element_1x0x0 ) {
236
+ $link .= $childNode->getHTML();
237
+ }
238
+ }
239
+
240
+ $link .= '</'. $this->tagName .'>';
241
+ }
242
+
243
+ return $link;
244
+ }
245
+
246
+ }
247
+
248
+
249
+ /*?>*/
libs/fwp/class-fwp-html-fields.php ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WP_Form_Fields_1x0x0
4
+ *
5
+ * @package FWP
6
+ * @category WordPress Library
7
+ * @version 1.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WPRun-WordPress-Development
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ class FWP_HTML_Fields_1x0x0
14
+ {
15
+
16
+ /**
17
+ * @var array
18
+ */
19
+ private $values = array();
20
+
21
+ /**
22
+ * @var string
23
+ */
24
+ private $field_id_format = '%s';
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ private $field_name_format = '%s';
30
+
31
+ /**
32
+ * Constructor
33
+ */
34
+ public function __construct( array $values = array(), $field_id_format = null, $field_name_format = null )
35
+ {
36
+ $this->values = $values;
37
+ $this->field_id_format = $field_id_format;
38
+ $this->field_name_format = $field_name_format;
39
+ }
40
+
41
+ /**
42
+ * Get form field id
43
+ * @param string $key
44
+ * @return string
45
+ */
46
+ public function get_field_id( $key )
47
+ {
48
+ return sprintf( $this->field_id_format, $key );
49
+ }
50
+
51
+ /**
52
+ * Get form field name
53
+ * @param string $key
54
+ * @return string
55
+ */
56
+ public function get_field_name( $key )
57
+ {
58
+ return sprintf( $this->field_name_format, $key );
59
+ }
60
+
61
+ /**
62
+ * Get value
63
+ * @param string $key
64
+ * @param mixed $default_value Optional
65
+ * @return mixed|null
66
+ */
67
+ public function get_value( $key, $default_value = null )
68
+ {
69
+ $values = $this->values;
70
+
71
+ if ( !isset( $values[ $key ] ) ) {
72
+ return $default_value;
73
+ }
74
+
75
+ return $values[ $key ];
76
+ }
77
+
78
+ /**
79
+ * Show html label
80
+ * @param string $key
81
+ * @param string $label_text
82
+ * @param array $atts Optional
83
+ */
84
+ public function label( $key, $label_text, array $atts = array() )
85
+ {
86
+ echo '<label for="' . $this->get_field_id( $key ) . '"
87
+ ' . $this->get_html_atts( $atts ) . '
88
+ >' . $label_text . '
89
+ </label>';
90
+ }
91
+
92
+ /**
93
+ * Show text input field
94
+ * @param string $key
95
+ * @param array $atts Optional
96
+ */
97
+ public function text( $key, array $atts = array() )
98
+ {
99
+ echo '<input type="text"
100
+ id="' . $this->get_field_id( $key ) . '"
101
+ name="' . $this->get_field_name( $key ) . '"
102
+ value="' . esc_attr( $this->get_value( $key ) ) . '"
103
+ ' . $this->get_html_atts( $atts ) . '
104
+ >';
105
+ }
106
+
107
+ /**
108
+ * Show text input field
109
+ * @param string $key
110
+ * @param array $atts Optional
111
+ */
112
+ public function text_area( $key, array $atts = array() )
113
+ {
114
+ echo '<textarea id="' . $this->get_field_id( $key ) . '"
115
+ name="' . $this->get_field_name( $key ) . '"
116
+ ' . $this->get_html_atts( $atts ) . '
117
+ >'. esc_textarea( $this->get_value( $key ) ) .'</textarea>';
118
+ }
119
+
120
+ /**
121
+ * Show a check field
122
+ * @param string $key
123
+ * @param mixed $checked_value
124
+ * @param mixed $unchecked_value
125
+ * @param array $atts Optional
126
+ */
127
+ public function check( $key, $checked_value = '1', $unchecked_value = '', array $atts = array() )
128
+ {
129
+ // workaround for also posting a value when checkbox is unchecked
130
+ if ( null !== $unchecked_value ) {
131
+ echo '<input type="hidden"
132
+ name="' . $this->get_field_name( $key ) . '"
133
+ value="' . esc_attr( $unchecked_value ) . '"
134
+ >';
135
+ }
136
+
137
+ echo '<input type="checkbox"
138
+ id="' . $this->get_field_id( $key ) . '"
139
+ name="' . $this->get_field_name( $key ) . '"
140
+ value="' . esc_attr( $checked_value ) . '"
141
+ ' . $this->get_checked_attr( $key, $checked_value ) . '
142
+ ' . $this->get_html_atts( $atts ) . '
143
+ >';
144
+ }
145
+
146
+ /**
147
+ * Show a check field with label
148
+ * @param string $key
149
+ * @param string $label_text
150
+ * @param mixed $checked_value
151
+ * @param mixed $unchecked_value
152
+ * @param array $atts Optional
153
+ */
154
+ public function check_with_label( $key, $label_text, $checked_value, $unchecked_value = null, array $atts = array() )
155
+ {
156
+ echo '<label>';
157
+ $this->check( $key, $checked_value, $unchecked_value, $atts );
158
+ echo $label_text;
159
+ echo '</label>';
160
+ }
161
+
162
+ /**
163
+ * Show a radio field
164
+ * @param string $key
165
+ * @param mixed $checked_value
166
+ * @param array $atts Optional
167
+ */
168
+ public function radio( $key, $checked_value, array $atts = array() )
169
+ {
170
+ $id = $this->get_field_id( $key ) . '-' . sanitize_key( $checked_value );
171
+
172
+ echo '<input type="radio"
173
+ id="' . $id . '"
174
+ name="' . $this->get_field_name( $key ) . '"
175
+ value="' . esc_attr( $checked_value ) . '"
176
+ ' . $this->get_checked_attr( $key, $checked_value ) . '
177
+ ' . $this->get_html_atts( $atts ) . '
178
+ >';
179
+ }
180
+
181
+ /**
182
+ * Show a check field with label
183
+ * @param string $key
184
+ * @param string $label_text
185
+ * @param mixed $checked_value
186
+ * @param array $atts Optional
187
+ */
188
+ public function radio_with_label( $key, $label_text, $checked_value, array $atts = array() )
189
+ {
190
+ echo '<label>';
191
+ $this->radio( $key, $checked_value, $atts );
192
+ echo $label_text;
193
+ echo '</label>';
194
+ }
195
+
196
+ /**
197
+ * Show select field with or without options
198
+ * @param string $key
199
+ * @param mixed $checked_value
200
+ * @param array $options
201
+ * @param array $atts Optional
202
+ */
203
+ public function select( $key, array $options = array(), array $atts = array() )
204
+ {
205
+ $value = $this->get_value( $key );
206
+
207
+ echo '<select id="' . $this->get_field_id( $key ) . '"
208
+ name="' . $this->get_field_name( $key ) . '"
209
+ ' . $this->get_html_atts( $atts ) . '
210
+ >';
211
+
212
+ foreach ( $options as $option_value => $option_text ) {
213
+ $this->select_option( $option_text, $option_value, ( $value == $option_value ) );
214
+ }
215
+
216
+ echo '</select>';
217
+ }
218
+
219
+ /**
220
+ * Show a select option
221
+ * @param string $text
222
+ * @param string $value
223
+ * @param boolean $selected
224
+ */
225
+ public function select_option( $text, $value, $selected = false )
226
+ {
227
+ echo '<option value="' . esc_attr( $value ) . '"' . ( $selected ? ' selected' : '' ) . '>
228
+ ' . $text . '
229
+ </option>';
230
+ }
231
+
232
+ /**
233
+ * @param array $atts
234
+ * @return string
235
+ */
236
+ private function get_html_atts( array $atts )
237
+ {
238
+ $html_atts = '';
239
+
240
+ foreach ( $atts as $key => $value ) {
241
+ if ( null === $value ) {
242
+ $html_atts .= ' '. $key;
243
+ } else {
244
+ $html_atts .= ' '. $key .'="'. esc_attr( $value ) .'"';
245
+ }
246
+ }
247
+
248
+ return $html_atts;
249
+ }
250
+
251
+ /**
252
+ * Get the checked attribute
253
+ * @param string $key
254
+ * @param mixed $checked_value
255
+ * @return string
256
+ */
257
+ private function get_checked_attr( $key, $checked_value )
258
+ {
259
+ return ( $this->get_value( $key ) == $checked_value ) ? ' checked' : '';
260
+ }
261
+
262
+ }
263
+
264
+ /*?>*/
libs/fwp/class-fwp-settings-section-fields.php ADDED
@@ -0,0 +1,249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class FWP_Settings_Section_Fields_1x0x0
4
+ *
5
+ * @package FWP
6
+ * @category WordPress Library
7
+ * @version 1.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WPRun-WordPress-Development
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ abstract class FWP_Settings_Section_Fields_1x0x0 extends WPRun_Base_1x0x0
14
+ {
15
+
16
+ /**
17
+ * @var array
18
+ */
19
+ protected $default_settings = array(
20
+ 'section_id' => '',
21
+ 'title' => '',
22
+ 'description' => '',
23
+ 'page_id' => '',
24
+ 'option_name' => '',
25
+ 'option_group' => '',
26
+ 'html_fields_class' => 'FWP_HTML_Fields_1x0x0',
27
+ 'fields' => array(
28
+ //'key' => array(
29
+ // 'label' => '',
30
+ // 'class' => '',
31
+ // 'default_value' => '',
32
+ //),
33
+ ),
34
+ );
35
+
36
+ /**
37
+ * @var array
38
+ */
39
+ private $field_errors = array();
40
+
41
+ /**
42
+ * @var FWP_HTML_Fields_1x0x0
43
+ */
44
+ private $html_fields = null;
45
+
46
+ /**
47
+ * @var array
48
+ */
49
+ private $option_values = array();
50
+
51
+ /**
52
+ * After init
53
+ */
54
+ protected function after_init()
55
+ {
56
+ $this->set_option_values();
57
+ $this->set_html_fields();
58
+ }
59
+
60
+ /**
61
+ * Action for "admin_init"
62
+ */
63
+ protected function action_admin_init()
64
+ {
65
+ $description = $this->get_setting( 'description' );
66
+
67
+ add_settings_section(
68
+ $this->get_setting( 'section_id' ) // id
69
+ , $this->get_setting( 'title' ) // title
70
+ , function () use ( $description ) { // callback
71
+ echo $description;
72
+ }
73
+ , $this->get_setting( 'page_id' ) // page id
74
+ );
75
+
76
+ register_setting(
77
+ $this->get_setting( 'option_group' )
78
+ , $this->get_setting( 'option_name' )
79
+ , $this->get_callback( 'sanitize' )
80
+ );
81
+
82
+ $this->add_fields();
83
+ }
84
+
85
+ /**
86
+ * Set option values
87
+ */
88
+ private function set_option_values()
89
+ {
90
+ $saved_values = $this->get_saved_values();
91
+ $default_values = $this->get_default_values();
92
+
93
+ $values = wp_parse_args( $saved_values, $default_values );
94
+ $this->option_values = $values;
95
+ }
96
+
97
+ /**
98
+ * Get option values
99
+ * @return array
100
+ */
101
+ final public function get_option_values()
102
+ {
103
+ return $this->option_values;
104
+ }
105
+
106
+ /**
107
+ * Get the default option values
108
+ * @return array
109
+ */
110
+ final public function get_default_values()
111
+ {
112
+ $fields = $this->get_setting( 'fields' );
113
+
114
+ $default_values = array_map( function ( $arr ) {
115
+ if ( ! isset( $arr[ 'default_value' ] ) ) {
116
+ return '';
117
+ }
118
+
119
+ return $arr[ 'default_value' ];
120
+ }, $fields );
121
+
122
+ return $default_values;
123
+ }
124
+
125
+ /**
126
+ * Get saved option values from database
127
+ * @return type
128
+ */
129
+ final public function get_saved_values()
130
+ {
131
+ if ( is_network_admin() ) {
132
+ $option = get_site_option( $this->get_setting( 'option_name' ) );
133
+ } else {
134
+ $option = get_option( $this->get_setting( 'option_name' ) );
135
+ }
136
+
137
+ $saved_values = is_array( $option ) ? $option : array();
138
+ return $saved_values;
139
+ }
140
+
141
+ /**
142
+ * Create html fields
143
+ */
144
+ private function set_html_fields()
145
+ {
146
+ $option_name = $this->get_setting( 'option_name' );
147
+
148
+ $html_fields_class = $this->get_setting( 'html_fields_class' );
149
+ $this->html_fields = new $html_fields_class(
150
+ $this->option_values
151
+ , $option_name .'-%s'
152
+ , $option_name .'[%s]'
153
+ );
154
+ }
155
+
156
+ /**
157
+ * @return FWP_HTML_Fields_1x0x0
158
+ */
159
+ final protected function get_html_fields()
160
+ {
161
+ return $this->html_fields;
162
+ }
163
+
164
+ /**
165
+ * Sanitize settings callback
166
+ * @param array $values
167
+ * @return array
168
+ */
169
+ protected function sanitize( $values )
170
+ {
171
+ $old_values = $this->option_values;
172
+
173
+ $this->field_errors = array();
174
+
175
+ $new_values = $this->before_update( $values, $old_values );
176
+
177
+ if ( count ( $this->field_errors ) > 0 ) {
178
+ add_settings_error(
179
+ $this->get_setting( 'option_group' )
180
+ , 'settings_updated'
181
+ , implode( '<br>', $this->field_errors )
182
+ , 'error'
183
+ );
184
+ }
185
+
186
+ return $new_values;
187
+ }
188
+
189
+ /**
190
+ * Validate and sanitize user input before saving to databse
191
+ * @param array $new_values
192
+ * @param array $old_values
193
+ * @return array
194
+ */
195
+ protected function before_update( array $new_values, array $old_values )
196
+ {
197
+ return $new_values;
198
+ }
199
+
200
+ /**
201
+ * Add fields
202
+ */
203
+ protected function add_fields()
204
+ {
205
+ $fields = $this->get_setting( 'fields' );
206
+
207
+ foreach ( $fields as $key => $field_settings ) {
208
+ $label = isset( $field_settings[ 'label' ] ) ? $field_settings[ 'label' ] : '';
209
+ $class = isset( $field_settings[ 'class' ] ) ? $field_settings[ 'class' ] : '';
210
+
211
+ add_settings_field(
212
+ $key
213
+ , $label
214
+ , $this->get_callback( 'field_callback' )
215
+ , $this->get_setting( 'page_id' )
216
+ , $this->get_setting( 'section_id' )
217
+ , array(
218
+ 'key' => $key,
219
+ 'label_for' => $this->html_fields->get_field_id( $key ),
220
+ 'class' => $class,
221
+ )
222
+ );
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Show field callback
228
+ * @param array $args
229
+ */
230
+ final protected function field_callback( array $args )
231
+ {
232
+ $field_method = 'show_'. $args[ 'key' ];
233
+
234
+ if (is_callable( array( $this, $field_method ) ) ) {
235
+ $this->{ $field_method }( $args );
236
+ }
237
+ }
238
+
239
+ /**
240
+ * @param string $message
241
+ */
242
+ final protected function add_error( $message )
243
+ {
244
+ $this->field_errors[] = $message;
245
+ }
246
+
247
+ }
248
+
249
+ /*?>*/
libs/fwp/filter-hooks/class-fwp-final-output.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class FWP_Final_Output_1x0x0
4
+ *
5
+ * @todo multiple versions - multiple filter applies
6
+ *
7
+ * @package FWP
8
+ * @category WordPress Library
9
+ * @version 1.0.0
10
+ * @author Victor Villaverde Laan
11
+ * @link http://www.finewebdev.com
12
+ * @link https://github.com/freelancephp/WPRun-WordPress-Development
13
+ * @license Dual licensed under the MIT and GPLv2+ licenses
14
+ */
15
+ class FWP_Final_Output_1x0x0 extends WPRun_Base_1x0x0
16
+ {
17
+
18
+ const FILTER_NAME = 'final_output';
19
+
20
+ /**
21
+ * Action for "init"
22
+ */
23
+ protected function action_init()
24
+ {
25
+ ob_start( $this->get_callback( 'apply' ) );
26
+ }
27
+
28
+ /**
29
+ * Apply filters
30
+ * @param string $content
31
+ * @return string
32
+ */
33
+ protected function apply( $content )
34
+ {
35
+ $filtered_content = apply_filters( self::FILTER_NAME, $content );
36
+
37
+ // remove filters after applying to prevent multiple applies
38
+ remove_all_filters( self::FILTER_NAME );
39
+
40
+ return $filtered_content;
41
+ }
42
+
43
+ }
44
+
45
+ /*?>*/
libs/fwp/filter-hooks/class-fwp-widget-output.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class Widget_Output_1x0x0
4
+ *
5
+ * This component was inspired by:
6
+ * @todo
7
+ *
8
+ * @todo multiple versions - multiple filter applies
9
+ *
10
+ * @package FWP
11
+ * @category WordPress Library
12
+ * @version 1.0.0
13
+ * @author Victor Villaverde Laan
14
+ * @link http://www.finewebdev.com
15
+ * @link https://github.com/freelancephp/WPRun-WordPress-Development
16
+ * @license Dual licensed under the MIT and GPLv2+ licenses
17
+ */
18
+ class FWP_Widget_Output_1x0x0 extends WPRun_Base_1x0x0
19
+ {
20
+
21
+ const FILTER_NAME = 'widget_output';
22
+
23
+ /**
24
+ * Filter for "dynamic_sidebar_params"
25
+ *
26
+ * @global array $wp_registered_widgets
27
+ * @param array $sidebar_params
28
+ * @return array
29
+ */
30
+ protected function filter_dynamic_sidebar_params( $sidebar_params )
31
+ {
32
+ global $wp_registered_widgets;
33
+
34
+ if ( is_admin() ) {
35
+ return $sidebar_params;
36
+ }
37
+
38
+ $widget_id = $sidebar_params[ 0 ][ 'widget_id' ];
39
+
40
+ // prevent overwriting when already set by another version of the widget output class
41
+ if ( isset( $wp_registered_widgets[ $widget_id ][ '_wo_original_callback' ] ) ) {
42
+ return $sidebar_params;
43
+ }
44
+
45
+ $wp_registered_widgets[ $widget_id ][ '_wo_original_callback' ] = $wp_registered_widgets[ $widget_id ][ 'callback' ];
46
+ $wp_registered_widgets[ $widget_id ][ 'callback' ] = $this->get_callback( 'widget_callback' );
47
+
48
+ return $sidebar_params;
49
+ }
50
+
51
+ /**
52
+ * Widget Callback
53
+ * @global array $wp_registered_widgets
54
+ * @return void
55
+ */
56
+ protected function widget_callback()
57
+ {
58
+ global $wp_registered_widgets;
59
+
60
+ $original_callback_params = func_get_args();
61
+ $widget_id = $original_callback_params[ 0 ][ 'widget_id' ];
62
+
63
+ $original_callback = $wp_registered_widgets[ $widget_id ][ '_wo_original_callback' ];
64
+ $wp_registered_widgets[ $widget_id ][ 'callback' ] = $original_callback;
65
+
66
+ $widget_id_base = $wp_registered_widgets[ $widget_id ][ 'callback' ][ 0 ]->id_base;
67
+
68
+ if ( !is_callable( $original_callback ) ) {
69
+ return;
70
+ }
71
+
72
+ ob_start();
73
+
74
+ call_user_func_array( $original_callback, $original_callback_params );
75
+ $widget_output = ob_get_clean();
76
+
77
+ echo apply_filters( self::FILTER_NAME, $widget_output, $widget_id_base, $widget_id );
78
+ }
79
+
80
+ }
81
+
82
+ /*?>*/
libs/wprun/class-wprun-autoloader.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPRun_Autoloader_1x0x0
4
+ *
5
+ * @package WPRun
6
+ * @category WordPress Library
7
+ * @version 1.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WPRun-WordPress-Development
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ class WPRun_Autoloader_1x0x0
14
+ {
15
+
16
+ /**
17
+ * @var array
18
+ */
19
+ private $settings = array(
20
+ 'file_name_prefix' => 'class-',
21
+ 'replace_dashes_with' => '-',
22
+ );
23
+
24
+ /**
25
+ * @var array
26
+ */
27
+ private $paths = array();
28
+
29
+ /**
30
+ * Constructor
31
+ * @param array $settings Optional
32
+ */
33
+ final public function __construct( array $settings = array() )
34
+ {
35
+ $this->settings = wp_parse_args( $settings, $this->settings );
36
+
37
+ spl_autoload_register( array( $this, 'load_class' ) );
38
+ }
39
+
40
+ /**
41
+ * Add path to folder containing classes
42
+ * @param string $path
43
+ * @param boolean $include_subfolders Optional
44
+ * @return void
45
+ */
46
+ final public function add_path( $path, $include_subfolders = false )
47
+ {
48
+ if ( in_array( $path, $this->paths ) ) {
49
+ return;
50
+ }
51
+
52
+ $this->paths[] = $path;
53
+
54
+ // include subfolders
55
+ if ( true === $include_subfolders ) {
56
+ $entries = scandir( $path );
57
+
58
+ foreach ( $entries as $entry ) {
59
+ if ( '.' === $entry || '..' === $entry ) {
60
+ continue;
61
+ }
62
+
63
+ $item = $path . DIRECTORY_SEPARATOR . $entry;
64
+
65
+ if ( is_dir( $item ) ) {
66
+ $this->add_path( $item, true );
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Get all paths
74
+ * @return array
75
+ */
76
+ final public function get_paths()
77
+ {
78
+ return $this->paths;
79
+ }
80
+
81
+ /**
82
+ * Loads a class file
83
+ * @param string $class_name
84
+ * @return void
85
+ */
86
+ public function load_class( $class_name )
87
+ {
88
+ // remove version postfix
89
+ $pure_class_name = preg_replace( '/_\d+x\d+x\d+/', '', $class_name );
90
+
91
+ $file_name = '';
92
+ $file_name .= $this->settings['file_name_prefix'];
93
+ $file_name .= str_replace( '_', $this->settings['replace_dashes_with'], $pure_class_name );
94
+ $file_name .= '.php';
95
+
96
+ $lower_file_name = strtolower( $file_name );
97
+
98
+ foreach ( $this->paths as $path ) {
99
+ $file_path = $path . DIRECTORY_SEPARATOR . $lower_file_name;
100
+
101
+ if ( file_exists( $file_path ) ) {
102
+ require_once $file_path;
103
+
104
+ // return if class is available else it was probably
105
+ // the wrong version (postfix) and should continue looking
106
+ if ( class_exists( $class_name ) ) {
107
+ return;
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ }
114
+
115
+ /*?>*/
libs/wprun/class-wprun-base.php ADDED
@@ -0,0 +1,402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class WPRun_Base_1x0x0
4
+ *
5
+ * Base class for concrete subclasses
6
+ * All subclasses are singletons and can be instantiated with
7
+ * the static "create()" factory method.
8
+ *
9
+ * @package WPRun
10
+ * @category WordPress Library
11
+ * @version 1.0.0
12
+ * @author Victor Villaverde Laan
13
+ * @link http://www.finewebdev.com
14
+ * @link https://github.com/freelancephp/WPRun-WordPress-Development
15
+ * @license Dual licensed under the MIT and GPLv2+ licenses
16
+ */
17
+ abstract class WPRun_Base_1x0x0
18
+ {
19
+
20
+ const RETURN_VOID = '__VOID__';
21
+
22
+ /**
23
+ * Page hook
24
+ * If page hook isset, will only apply hook methods when page hook is the current screen id
25
+ * @var string
26
+ */
27
+ protected $page_hook = null;
28
+
29
+ /**
30
+ * Automatically set action and filter methods
31
+ * @var boolean
32
+ */
33
+ protected $autoset_hook_methods = true;
34
+
35
+ /**
36
+ * @var string
37
+ */
38
+ protected $action_prefix = 'action_';
39
+
40
+ /**
41
+ * @var string
42
+ */
43
+ protected $filter_prefix = 'filter_';
44
+
45
+ /**
46
+ * @var array
47
+ */
48
+ protected $default_settings = array();
49
+
50
+ /**
51
+ * @var array
52
+ */
53
+ private $settings = array();
54
+
55
+ /**
56
+ * Only for internal use
57
+ * @var string
58
+ */
59
+ private $internal_callback_prefix = '_cb_';
60
+
61
+ /**
62
+ * List of (singleton) instances
63
+ * Only for internal use
64
+ * @var array
65
+ */
66
+ private static $instances = array();
67
+
68
+ /**
69
+ * @var array
70
+ */
71
+ private $arguments = array();
72
+
73
+ /**
74
+ * Factory method
75
+ * @param mixed $param1 Optional, will be passed on to the constructor and init() method
76
+ * @param mixed $paramN Optional, will be passed on to the constructor and init() method
77
+ * @return WPRun_Base_1x0x0
78
+ * @triggers E_USER_NOTICE Class already created
79
+ */
80
+ final public static function create()
81
+ {
82
+ $class_name = get_called_class();
83
+ $arguments = func_get_args();
84
+
85
+ // check if instance of this class already exists
86
+ if ( key_exists( $class_name, self::$instances ) ) {
87
+ trigger_error( 'Class "'. $class_name .'" was already created.' );
88
+ return;
89
+ }
90
+
91
+ // pass all arguments to constructor
92
+ $instance = new $class_name( $arguments );
93
+
94
+ return $instance;
95
+ }
96
+
97
+ /**
98
+ * Constructor
99
+ * @triggers E_USER_NOTICE
100
+ */
101
+ private function __construct( array $arguments )
102
+ {
103
+ $class_name = get_called_class();
104
+ self::$instances[ $class_name ] = $this;
105
+
106
+ $this->arguments = $arguments;
107
+
108
+ if ( true === $this->autoset_hook_methods ) {
109
+ $this->set_hook_methods();
110
+ }
111
+
112
+ $init_methods = array( 'before_init', 'init', 'after_init' );
113
+
114
+ // call init methods
115
+ foreach ( $init_methods as $method_name ) {
116
+ if ( method_exists( $this, $method_name ) ) {
117
+ $method_reflection = new ReflectionMethod( get_called_class(), $method_name );
118
+
119
+ if ( $method_reflection->isProtected() ) {
120
+ call_user_func_array( array( $this, $method_name ), $this->arguments );
121
+ } else {
122
+ trigger_error( 'Method "'. $method_name .'" should be made protected in class "'. get_called_class() .'".' );
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ /**
129
+ * @return WPRun_Base_1x0x0
130
+ */
131
+ final public static function get_instance()
132
+ {
133
+ $class_name = get_called_class();
134
+ return self::$instances[ $class_name ];
135
+ }
136
+
137
+ /**
138
+ * @param string $key
139
+ * @return mixed
140
+ */
141
+ final public function get_setting( $key )
142
+ {
143
+ return $this->settings[ $key ];
144
+ }
145
+
146
+ /**
147
+ * @param array $settings
148
+ */
149
+ final protected function set_settings( array $settings )
150
+ {
151
+ if ( empty( $this->settings ) ) {
152
+ $this->settings = wp_parse_args( $settings, $this->default_settings );
153
+ } else {
154
+ $this->settings = wp_parse_args( $settings, $this->settings );
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Get argument passed on to the constructor
160
+ * @param integer $index Optional, when null return all arguments
161
+ * @return mixed|null
162
+ */
163
+ final protected function get_argument( $index = null )
164
+ {
165
+ // return all arguments when no index given
166
+ if ( null === $index ) {
167
+ return $this->arguments;
168
+ }
169
+
170
+ if ( !isset( $this->arguments[ $index ] ) ) {
171
+ return null;
172
+ }
173
+
174
+ return $this->arguments[ $index ];
175
+ }
176
+
177
+ /**
178
+ * @param string $template_file_path
179
+ * @param array $vars Optional
180
+ * @triggers E_USER_NOTICE Template file not readable
181
+ */
182
+ final public static function show_template( $template_file_path, array $vars = array() )
183
+ {
184
+ if ( is_readable( $template_file_path ) ) {
185
+ // show file
186
+ include $template_file_path;
187
+ } else {
188
+ trigger_error( 'Template file "' . $template_file_path . '" is not readable or may not exists.' );
189
+ }
190
+ }
191
+
192
+ /**
193
+ * @param string $template_file_path
194
+ * @param array $vars Optional
195
+ * @triggers E_USER_NOTICE Template file not readable
196
+ */
197
+ final public static function render_template( $template_file_path, array $vars = array() )
198
+ {
199
+ // start output buffer
200
+ ob_start();
201
+
202
+ // output template
203
+ $this->show_template( $template_file_path, $vars );
204
+
205
+ // get the view content
206
+ $content = ob_get_contents();
207
+
208
+ // clean output buffer
209
+ ob_end_clean();
210
+
211
+ return $content;
212
+ }
213
+
214
+ /**
215
+ * Get a callable to a method in current instance, when called will be
216
+ * caught by __callStatic(), were the magic happens
217
+ * @param string $method_name
218
+ * @return callable
219
+ */
220
+ final protected function get_callback( $method_name )
221
+ {
222
+ return array( get_called_class(), $this->internal_callback_prefix . $method_name );
223
+ }
224
+
225
+ /**
226
+ * @param string $method_name
227
+ * @param array $arguments
228
+ * @return mixed|void
229
+ * @triggers E_USER_NOTICE Method name not exists/callable
230
+ */
231
+ public function __call( $method_name, $arguments )
232
+ {
233
+ $return_value = self::magic_call( $method_name, $arguments );
234
+
235
+ if ( self::RETURN_VOID === $return_value ) {
236
+ trigger_error( 'Method name "'. $method_name .'" does not exists or cannot be called.' );
237
+ }
238
+
239
+ return $return_value;
240
+ }
241
+
242
+ /**
243
+ * @param string $method_name
244
+ * @param array $arguments
245
+ * @return mixed|void
246
+ * @triggers E_USER_NOTICE Method name not exists/callable
247
+ */
248
+ public static function __callStatic( $method_name, $arguments )
249
+ {
250
+ $return_value = self::magic_call( $method_name, $arguments );
251
+
252
+ if ( self::RETURN_VOID === $return_value ) {
253
+ trigger_error( 'Method name "'. $method_name .'" does not exists or cannot be called.' );
254
+ }
255
+
256
+ return $return_value;
257
+ }
258
+
259
+ /**
260
+ * @param string $method_name
261
+ * @param array $arguments
262
+ * @return mixed|void
263
+ */
264
+ final protected static function magic_call( $method_name, $arguments )
265
+ {
266
+ $class_name = get_called_class();
267
+ $instance = self::$instances[ $class_name ];
268
+
269
+ // catch callbacks set by get_callback() method
270
+ // this way callbacks can also be implemented as protected
271
+ $given_callback_name = self::fetch_name_containing_prefix( $instance->internal_callback_prefix, $method_name );
272
+
273
+ // normal callback
274
+ if ( null !== $given_callback_name ) {
275
+ $real_args = $arguments;
276
+
277
+ $given_method_name = $given_callback_name;
278
+
279
+ $callable = array( $instance, $given_method_name );
280
+
281
+ if ( is_callable( $callable ) ) {
282
+ return call_user_func_array( $callable, $real_args );
283
+ }
284
+ }
285
+
286
+ return self::RETURN_VOID;
287
+ }
288
+
289
+ /**
290
+ * Check and auto-initialize methods for hooks
291
+ */
292
+ final protected function set_hook_methods()
293
+ {
294
+ $methods = get_class_methods( $this );
295
+
296
+ foreach ( $methods as $method_name ) {
297
+ $action_name = self::fetch_name_containing_prefix( $this->action_prefix, $method_name );
298
+ if ( null !== $action_name ) {
299
+ self::add_to_hook( $this, 'action', $action_name, $method_name );
300
+ continue;
301
+ }
302
+
303
+ $filter_name = self::fetch_name_containing_prefix( $this->filter_prefix, $method_name );
304
+ if ( null !== $filter_name ) {
305
+ self::add_to_hook( $this, 'filter', $filter_name, $method_name );
306
+ continue;
307
+ }
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Must be public but for internal use only
313
+ * to support PHP 5.3 which doesn't recognize being used within class scope
314
+ * @param string $method_name
315
+ * @return array
316
+ */
317
+ final public function _get_callback( $method_name )
318
+ {
319
+ return $this->get_callback( $method_name );
320
+ }
321
+
322
+ /**
323
+ * Must be public but for internal use only
324
+ * to support PHP 5.3 which doesn't recognize being used within class scope
325
+ * @return string
326
+ */
327
+ final public function _get_page_hook()
328
+ {
329
+ return $this->page_hook;
330
+ }
331
+
332
+ /**
333
+ * @param WPRun_Base_1x0x0 $self
334
+ * @param string $hook_type "action" or "filter"
335
+ * @param string $hook_name
336
+ * @param string $method_name
337
+ * @triggers E_USER_NOTICE
338
+ */
339
+ private static function add_to_hook( $self, $hook_type, $hook_name, $method_name )
340
+ {
341
+ // fetch priority outof method name
342
+ $split_method_Name = explode( '_', $method_name );
343
+ $last = end( $split_method_Name );
344
+
345
+ if ( is_numeric( $last ) ) {
346
+ $priority = (int) $last;
347
+ $wp_hook_name = str_replace( '_' . $last, '', $hook_name );
348
+ } else {
349
+ $priority = 10;
350
+ $wp_hook_name = $hook_name;
351
+ }
352
+
353
+ // get the method's number of params
354
+ $method_reflection = new ReflectionMethod( get_called_class(), $method_name );
355
+ $accepted_args = $method_reflection->getNumberOfParameters();
356
+
357
+ // check if actions and filters are applied for page hook
358
+ $callback = function () use ( $self, $method_name ) {
359
+ if ( function_exists( 'get_current_screen' ) && null !== $self->_get_page_hook() ) {
360
+ if ( is_network_admin() ) {
361
+ $page_hook = $self->_get_page_hook() .'-network';
362
+ } else {
363
+ $page_hook = $self->_get_page_hook();
364
+ }
365
+
366
+ if ( get_current_screen()->id !== $page_hook ) {
367
+ return;
368
+ }
369
+ }
370
+
371
+ return call_user_func_array( $self->_get_callback( $method_name ), func_get_args() );
372
+ };
373
+
374
+ if ( 'action' === $hook_type ) {
375
+ add_action( $wp_hook_name, $callback, $priority, $accepted_args );
376
+ } elseif ('filter' === $hook_type) {
377
+ add_filter( $wp_hook_name, $callback, $priority, $accepted_args );
378
+ } else {
379
+ trigger_error( '"' . $hook_type . '" is not a valid hookType.' );
380
+ }
381
+ }
382
+
383
+ /**
384
+ * @param string $prefix
385
+ * @param string $name
386
+ * @return string|null
387
+ */
388
+ private static function fetch_name_containing_prefix( $prefix, $name )
389
+ {
390
+ $prefix_length = strlen( $prefix );
391
+
392
+ if ( $prefix !== substr( $name, 0, $prefix_length) ) {
393
+ return null;
394
+ }
395
+
396
+ $fetchedName = substr( $name, $prefix_length );
397
+ return $fetchedName;
398
+ }
399
+
400
+ }
401
+
402
+ /*?>*/
public/css/wpel-admin.css ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WP External Links Plugin
3
+ * Admin Style
4
+ */
5
+
6
+ .wpel-no-label,
7
+ .wpel-no-label th,
8
+ .wpel-no-label td {
9
+ padding-top: 0;
10
+ }
11
+ .wpel-no-label th:after {
12
+ content: '\00a0';
13
+ }
14
+ .wpel-hidden {
15
+ display: none;
16
+ }
17
+
18
+ a.toplevel_page_wpel-settings-page .wp-menu-image:before {
19
+ font-family: FontAwesome;
20
+ content: '\f14c';
21
+ }
22
+
23
+ a.toplevel_page_wpel-network-settings-page .wp-menu-image:before {
24
+ font-family: FontAwesome;
25
+ content: '\f14c';
26
+ }
27
+
28
+ .wpel-admin-settings .nav-tab i {
29
+ margin-right: 5px;
30
+ }
31
+
32
+ .wpel-admin-settings .nav-tab-support {
33
+ float: right;
34
+ }
35
+
36
+ .wpel-admin-settings form {
37
+ position: relative;
38
+ }
39
+
40
+ .wpel-admin-settings form:before {
41
+ content: "\f14c";
42
+ font-family: FontAwesome;
43
+ opacity: 0.2;
44
+ color: #aaa;
45
+ font-size: 200pt;
46
+ position: absolute;
47
+ top: 120px;
48
+ right: 40px;
49
+ z-index: -1;
50
+ }
51
+
52
+ .js-wpel-apply-child td {
53
+ padding-left: 30px;
54
+ }
55
+
56
+ .wpel-icon-type-image-column {
57
+ min-width: 10em;
58
+ float: left;
59
+ }
60
+ .wpel-icon-type-image-column:after {
61
+ clear: both;
62
+ }
63
+
64
+ ::-webkit-input-placeholder {
65
+ color: #ccc;
66
+ font-style: italic;
67
+ }
68
+ :-moz-placeholder { /* Firefox 18- */
69
+ color: #ccc;
70
+ font-style: italic;
71
+ }
72
+ ::-moz-placeholder { /* Firefox 19+ */
73
+ color: #ccc;
74
+ font-style: italic;
75
+ }
76
+ :-ms-input-placeholder {
77
+ color: #ccc;
78
+ font-style: italic;
79
+ }
public/css/wpel.css ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WP External Links Plugin
3
+ * Front Style
4
+ */
5
+
6
+ .wpel-icon.dashicons-before:before {
7
+ font-size: inherit;
8
+ line-height: inherit;
9
+ width: auto;
10
+ }
11
+
12
+ .wpel-icon-left i.wpel-icon.dashicons-before {
13
+ margin-right: 0.2em;
14
+ }
15
+ .wpel-icon-right i.wpel-icon.dashicons-before {
16
+ margin-left: 0.2em;
17
+ }
18
+ .wpel-icon-left i.wpel-icon {
19
+ margin-right: 0.3em;
20
+ }
21
+ .wpel-icon-right i.wpel-icon {
22
+ margin-left: 0.3em;
23
+ }
24
+
25
+ .wpel-icon.wpel-image {
26
+ display: inline-block;
27
+ width: 10px;
28
+ height: 10px;
29
+ background-repeat: no-repeat;
30
+ background-position: 100% 50%;
31
+ }
32
+ .wpel-icon-left .wpel-icon.wpel-image {
33
+ margin-right: 0.3em;
34
+ }
35
+ .wpel-icon-right .wpel-icon.wpel-image {
36
+ margin-left: 0.3em;
37
+ }
38
+
39
+ .wpel-icon-1 { background-image: url('../images/wpel-icons/icon-1.png'); }
40
+ .wpel-icon-2 { background-image: url('../images/wpel-icons/icon-2.png'); }
41
+ .wpel-icon-3 { background-image: url('../images/wpel-icons/icon-3.png'); }
42
+ .wpel-icon-4 { background-image: url('../images/wpel-icons/icon-4.png'); }
43
+ .wpel-icon-5 { background-image: url('../images/wpel-icons/icon-5.png'); }
44
+ .wpel-icon-6 { background-image: url('../images/wpel-icons/icon-6.png'); }
45
+ .wpel-icon-7 { background-image: url('../images/wpel-icons/icon-7.png'); }
46
+ .wpel-icon-8 { background-image: url('../images/wpel-icons/icon-8.png'); }
47
+ .wpel-icon-9 { background-image: url('../images/wpel-icons/icon-9.png'); }
48
+ .wpel-icon-10 { background-image: url('../images/wpel-icons/icon-10.png'); }
49
+ .wpel-icon-11 { background-image: url('../images/wpel-icons/icon-11.png'); }
50
+ .wpel-icon-12 { background-image: url('../images/wpel-icons/icon-12.png'); }
51
+ .wpel-icon-13 { background-image: url('../images/wpel-icons/icon-13.png'); }
52
+ .wpel-icon-14 { background-image: url('../images/wpel-icons/icon-14.png'); }
53
+ .wpel-icon-15 { background-image: url('../images/wpel-icons/icon-15.png'); }
54
+ .wpel-icon-16 { background-image: url('../images/wpel-icons/icon-16.png'); }
55
+ .wpel-icon-17 { background-image: url('../images/wpel-icons/icon-17.png'); }
56
+ .wpel-icon-18 { background-image: url('../images/wpel-icons/icon-18.png'); }
57
+ .wpel-icon-19 { background-image: url('../images/wpel-icons/icon-19.png'); }
58
+ .wpel-icon-20 { background-image: url('../images/wpel-icons/icon-20.png'); }
{images → public/images}/link-icon-example.png RENAMED
File without changes
images/ext-icons/ext-icon-1.png → public/images/wpel-icons/icon-1.png RENAMED
File without changes
images/ext-icons/ext-icon-10.png → public/images/wpel-icons/icon-10.png RENAMED
File without changes
images/ext-icons/ext-icon-11.png → public/images/wpel-icons/icon-11.png RENAMED
File without changes
images/ext-icons/ext-icon-12.png → public/images/wpel-icons/icon-12.png RENAMED
File without changes
images/ext-icons/ext-icon-13.png → public/images/wpel-icons/icon-13.png RENAMED
File without changes
images/ext-icons/ext-icon-14.png → public/images/wpel-icons/icon-14.png RENAMED
File without changes
images/ext-icons/ext-icon-15.png → public/images/wpel-icons/icon-15.png RENAMED
File without changes
images/ext-icons/ext-icon-16.png → public/images/wpel-icons/icon-16.png RENAMED
File without changes
images/ext-icons/ext-icon-17.png → public/images/wpel-icons/icon-17.png RENAMED
File without changes
images/ext-icons/ext-icon-18.png → public/images/wpel-icons/icon-18.png RENAMED
File without changes
images/ext-icons/ext-icon-19.png → public/images/wpel-icons/icon-19.png RENAMED
File without changes
images/ext-icons/ext-icon-2.png → public/images/wpel-icons/icon-2.png RENAMED
File without changes
images/ext-icons/ext-icon-20.png → public/images/wpel-icons/icon-20.png RENAMED
File without changes
images/ext-icons/ext-icon-3.png → public/images/wpel-icons/icon-3.png RENAMED
File without changes
images/ext-icons/ext-icon-4.png → public/images/wpel-icons/icon-4.png RENAMED
File without changes
images/ext-icons/ext-icon-5.png → public/images/wpel-icons/icon-5.png RENAMED
File without changes
images/ext-icons/ext-icon-6.png → public/images/wpel-icons/icon-6.png RENAMED
File without changes
images/ext-icons/ext-icon-7.png → public/images/wpel-icons/icon-7.png RENAMED
File without changes
images/ext-icons/ext-icon-8.png → public/images/wpel-icons/icon-8.png RENAMED
File without changes
images/ext-icons/ext-icon-9.png → public/images/wpel-icons/icon-9.png RENAMED
File without changes
public/js/wpel-admin.js ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WP External Links Plugin
3
+ * Admin
4
+ */
5
+ /*global jQuery, window*/
6
+ jQuery(function ($) {
7
+ 'use strict';
8
+
9
+ var $wrapper = $('.wpel-admin-settings');
10
+
11
+ /**
12
+ * Apply Sections Settings
13
+ */
14
+ $wrapper.on('change', '.js-wpel-apply input', function () {
15
+ var apply_all = $(this).is(':checked');
16
+ var $items = $wrapper.find('.js-wpel-apply-child');
17
+
18
+ if (apply_all) {
19
+ $items.hide();
20
+ } else {
21
+ $items.show();
22
+ }
23
+ });
24
+
25
+ // trigger immediatly
26
+ $wrapper.find('.js-wpel-apply input').change();
27
+
28
+
29
+ /**
30
+ * Link Settings
31
+ */
32
+ $wrapper.on('change', '.js-apply-settings input', function () {
33
+ var $items = $wrapper.find('.form-table tr').not('.js-apply-settings');
34
+
35
+ if ($(this).prop('checked')) {
36
+ $items.show();
37
+ $wrapper.find('.js-icon-type select').change();
38
+ } else {
39
+ $items.hide();
40
+ }
41
+ });
42
+
43
+ // trigger immediatly
44
+ $wrapper.find('.js-apply-settings input').change();
45
+
46
+ $wrapper.on('change', '.js-icon-type select', function () {
47
+ var iconType = $(this).val();
48
+ var $itemsChild = $wrapper.find('.js-icon-type-child');
49
+ var $itemsDepend = $wrapper.find('.js-icon-type-depend');
50
+
51
+ $itemsChild.hide();
52
+
53
+ if (iconType === 'image') {
54
+ $itemsDepend.show();
55
+ $itemsChild.filter('.js-icon-type-image').show();
56
+ } else if (iconType === 'dashicon') {
57
+ $itemsDepend.show();
58
+ $itemsChild.filter('.js-icon-type-dashicon').show();
59
+ } else if (iconType === 'fontawesome') {
60
+ $itemsDepend.show();
61
+ $itemsChild.filter('.js-icon-type-fontawesome').show();
62
+ } else {
63
+ $itemsDepend.hide();
64
+ }
65
+ });
66
+
67
+ // trigger immediatly
68
+ $wrapper.find('.js-icon-type select').change();
69
+
70
+ /**
71
+ * Support
72
+ */
73
+ $wrapper.on('click', '.js-wpel-copy', function (e) {
74
+ e.preventDefault();
75
+
76
+ var node = $wrapper.find('.js-wpel-copy-target').get(0);
77
+ node.select();
78
+
79
+ var range = document.createRange();
80
+ range.selectNode(node);
81
+ window.getSelection().addRange(range);
82
+
83
+ try {
84
+ document.execCommand('copy');
85
+ } catch(err) {
86
+ console.log('Unable to copy');
87
+ }
88
+ });
89
+
90
+ /**
91
+ * Help documentation links/buttons
92
+ */
93
+ $wrapper.on('click', '[data-wpel-help]', function () {
94
+ var helpKey = $(this).data('wpel-help');
95
+
96
+ if (helpKey) {
97
+ // activate given tab
98
+ $('#tab-link-'+ helpKey +' a').click();
99
+ } else {
100
+ // activate first tab
101
+ $('.contextual-help-tabs li a').first().click();
102
+ }
103
+
104
+ $('#contextual-help-link[aria-expanded="false"]').click();
105
+ });
106
+
107
+
108
+ });
readme.txt CHANGED
@@ -1,37 +1,56 @@
1
- === WP External Links (nofollow new window seo) ===
2
  Contributors: freelancephp
3
- Tags: links, external, icon, target, _blank, _new, _none, rel, nofollow, new window, new tab, javascript, xhtml, seo
4
- Requires at least: 3.6.0
5
  Tested up to: 4.5.2
6
- Stable tag: 1.81
 
 
7
 
8
- Open external links in a new window or tab, adding "nofollow", set link icon, styling, SEO friendly options and more. Easy install and go.
9
 
10
  == Description ==
11
 
12
- Configure settings for all external links on your site.
 
 
 
13
 
14
  = Features =
15
- * Open in new window or tab
16
- * Add "nofollow"
17
- * Choose from 20 icons
18
- * Set other link options (like classes, title etc)
19
- * Make it SEO friendly
20
- * Compatible with WPMU (Multisite)
 
 
 
 
 
 
 
21
 
22
  = Easy to use =
23
- After activating the plugin all options are already set to make your external links SEO friendly. Optionally you can also set the target for opening in a new window or tab or styling options, like adding an icon.
24
 
25
  = On the fly =
26
- The plugin will change the output of the (external) links on the fly. So when you deactivate the plugin, all contents will remain the same as it was before installing the plugin.
 
 
 
 
 
 
 
 
27
 
28
  = Sources =
29
  * [Documentation](http://wordpress.org/extend/plugins/wp-external-links/other_notes/)
30
  * [FAQ](http://wordpress.org/extend/plugins/wp-external-links/faq/)
31
  * [Github](https://github.com/freelancephp/WP-External-Links)
32
 
33
- = Like this plugin? =
34
- [Send your review](http://wordpress.org/support/view/plugin-reviews/wp-external-links-plugin).
35
 
36
 
37
  == Installation ==
@@ -41,62 +60,45 @@ The plugin will change the output of the (external) links on the fly. So when yo
41
  1. Search for `WP External Links` and click 'Install Now' OR click on the `upload` link to upload `wp-external-links.zip`
42
  1. Click on `Activate plugin`
43
 
44
- == Frequently Asked Questions ==
45
-
46
- = How to treat internal links as external links? =
47
-
48
- You could add `rel="external"` to those internal links that should be treated as external. The plugin settings will also be applied to those links.
49
-
50
- = Why are links to my own domain treated as external links? =
51
-
52
- Only links pointing to your WordPress site (`wp_url`) are by default threaded as internal links.
53
- There is an option to mark all links to your domain (and subdomains) as internal links.
54
-
55
- = How to treat links to subdomains as internal links? =
56
-
57
- Add your main domain to the option "Ingore links (URL) containing..." and they will not be treated as external.
58
-
59
- = How to create a redirect for external links? =
60
-
61
- By using the `wpel_external_link` filter. Add this code to functions.php of your theme:
62
-
63
- `function redirect_external_link($created_link, $original_link, $label, $attrs = array()) {
64
- $href = $attrs['href'];
65
-
66
- // create redirect url
67
- $href_new = get_bloginfo('wpurl') . '/redirect.php?url=' . urlencode($attrs['href']);
68
 
69
- return str_replace($href, $href_new, $created_link);
70
- }
71
 
72
- add_filter('wpel_external_link', 'redirect_external_link', 10, 4);`
73
 
74
- = Set a font icon for external links, like [Font Awesome Icons](http://fortawesome.github.io/Font-Awesome/)? =
 
 
75
 
76
- Use the `wpel_external_link` filter and add this code to functions.php of your theme:
 
 
77
 
78
- `function set_font_icon_on_external_link($created_link, $original_link, $label, $attrs = array()) {
79
- $label_with_font = $label . ' <i class="fa fa-external-link"></i>';
80
- return str_replace($label, $label_with_font, $created_link);
81
- }
82
 
83
- add_filter('wpel_external_link', 'set_font_icon_on_external_link', 10, 4);`
84
 
85
- The CSS of Font Awesome Icons alse needs to be loaded. To do so also add this code:
86
 
87
- `function add_font_awesome_style() {
88
- wp_enqueue_style('font-awesome', '//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css');
89
- }
 
 
90
 
91
- add_action('wp_enqueue_scripts', 'add_font_awesome_style');`
 
 
 
 
92
 
93
- = How to open external links in popup browser window with a certain size? =
94
 
95
  By adding this JavaScript code to your site:
96
 
97
  `jQuery(function ($) {
98
 
99
- $('a[rel*="external"]').click(function (e) {
100
  // open link in popup window
101
  window.open($(this).attr('href'), '_blank', 'width=800, height=600');
102
 
@@ -115,7 +117,7 @@ Add this JavaScript code to your site:
115
 
116
  `jQuery(function ($) {
117
 
118
- $('a[rel*="external"]').click(function (e) {
119
  if (!confirm('Are you sure you want to open this link?')) {
120
  // cancelled
121
  e.preventDefault();
@@ -125,98 +127,98 @@ Add this JavaScript code to your site:
125
 
126
  });`
127
 
128
- = How to make all internal links "follow"? =
129
 
130
- By using the `wp_internal_link` filter. Add this code to functions.php of your theme:
131
 
132
- `function set_follow_to_internal_link($link, $label, $attrs) {
133
- return str_replace('nofollow', 'follow', $link);
134
- }
135
 
136
- add_filter('wpel_internal_link', 'set_follow_to_internal_link', 10, 3);
137
- `
 
138
 
139
 
140
- [Do you have a question? Please ask me](http://www.freelancephp.net/contact/)
141
 
142
- == Screenshots ==
143
 
144
- 1. Link Icon on the Site
145
- 1. Admin Settings Page
146
 
147
- == Documentation ==
 
 
 
148
 
149
- After activating the plugin all options are already set to make your external links SEO friendly. Optionally you can also set the target for opening in a new window or tab or styling options, like adding an icon.
 
150
 
151
- = Action hook: wpel_ready =
152
- The plugin also has a hook when ready, f.e. to add extra filters:
153
- `function extra_filters($filter_callback, $object) {
154
- add_filter('some_filter', $filter_callback);
155
- }
156
 
157
- add_action('wpel_ready', 'extra_filters');`
158
 
159
- = Filter hook 1: wpel_external_link =
160
- The `wpel_external_link` filter gives you the possibility to manipulate output of all external links, like:
161
- `add_filter('wpel_external_link', 'wpel_special_external_link', 10, 5);
 
162
 
163
- function wpel_special_external_link($created_link, $original_link, $label, $attrs, $is_ignored_link) {
164
- // skip links that contain the class "not-external"
165
- if (isset($attrs['class']) && strpos($attrs['class'], 'not-external') !== false) {
166
- return $original_link;
167
- }
168
 
169
- return '<b>'. $created_link .'</b>';
170
- }`
171
 
172
- Now all external links will be processed and wrapped around a `<b>`-tag. And links containing the class "not-external" will not be processed by the plugin at all (and stay the way they are).
173
 
174
- = Filter hook 2: wpel_external_link_attrs =
175
 
176
- The `wpel_external_link_attrs` filter can be used to manipulate attributes of external links.
177
- `add_filter('wpel_external_link_attrs', 'wpel_custom_title', 10, 3);
178
 
179
- function wpel_custom_title($attrs, $original_attrs, $label) {
180
- if (empty($attrs['title']) && isset($attrs['href'])) {
181
- $attrs['title'] = $attrs['href'];
182
- }
183
 
184
- return $attrs;
185
- }`
186
 
187
- In this example when an external links has an empty title, the title will contain the url.
188
 
189
- = Filter hook 3: wpel_ignored_external_link =
190
- With the `wpel_ignored_external_link` filter you can manipulate the output of the ignored external links.
191
- `add_filter('wpel_ignored_external_link', 'wpel_custom_ignored_link', 10, 3);
192
 
193
- function wpel_custom_ignored_link($link, $label, $attrs) {
194
- return '<del>'. $link .'</del>';
195
- }`
196
 
197
- In this case all ignored links will be marked as deleted (strikethrough).
198
 
199
- = Filter hook 4: wpel_internal_link =
200
- With the `wpel_internal_link` filter you can manipulate the output of all internal links on your site. F.e.:
201
- `add_filter('wpel_internal_link', 'special_internal_link', 10, 3);
202
 
203
- function special_internal_link($link, $label, $attrs) {
204
- return '<b>'. $link .'</b>';
205
- }`
206
 
207
- In this case all internal links will be made bold.
 
208
 
 
209
 
210
- See [FAQ](https://wordpress.org/plugins/wp-external-links/faq/) for more possibilities of using these filters.
211
 
 
 
 
 
 
 
 
212
 
213
- = Credits =
214
- * [jQuery Tipsy Plugin](http://plugins.jquery.com/project/tipsy) made by [Jason Frame](http://onehackoranother.com/)
215
- * [phpQuery](http://code.google.com/p/phpquery/) made by [Tobiasz Cudnik](http://tobiasz123.wordpress.com)
216
- * [Icon](http://findicons.com/icon/164579/link_go?id=427009) made by [FatCow Web Hosting](http://www.fatcow.com/)
217
 
218
  == Changelog ==
219
 
 
 
 
 
 
 
 
 
 
220
  = 1.81 =
221
  * Security update (reported by Vulnerability Lab)
222
  * Some small changes
1
+ === WP External Links (nofollow new tab seo) ===
2
  Contributors: freelancephp
3
+ Tags: links, new window, new tab, external links, nofollow, follow, seo, noopener, noreferrer, internal links, link icon, link target, _blank, wpmu
4
+ Requires at least: 4.2.0
5
  Tested up to: 4.5.2
6
+ Stable tag: 2.0.0
7
+
8
+ Open external links in a new tab / window, add "nofollow", "noopener" and font icons, SEO and more. Also for internal links.
9
 
 
10
 
11
  == Description ==
12
 
13
+ Configure settings for all internal and external links on your site.
14
+
15
+ > <strong>=== NEW: Version 2 ===</strong><br>
16
+ > WPEL plugin was rebuilt completely and has lots of new features, like "noopener", font icons, internal links options and WPMU settings.
17
 
18
  = Features =
19
+ * Open links in new window or tab
20
+ * Add "follow" or "nofollow"
21
+ * Add "noopener" and "noreferrer" (for security)
22
+ * Add link icons (font icons: font awesome, dashicons)
23
+ * Set other attributes like title and CSS classes
24
+ * Scan posts, comments, widgets or the whole page
25
+ * Better SEO
26
+
27
+ = And more... =
28
+ * Network Settings (WPMU support)
29
+ * Use template tag to apply plugin settings on specific contents
30
+ * Set data-attribute to change how individual links will be treated
31
+ * Use action and filters to implement your specific needs
32
 
33
  = Easy to use =
34
+ After activating you can set all options for external and internal links on the plugins admin page.
35
 
36
  = On the fly =
37
+ The plugin filters the output and changes the links on the fly. The real contents (posts, pages, widget etcetera) will not be changed in the database.
38
+ When deactivating the plugin, all contents will be the same as it was before.
39
+
40
+ = Requirements =
41
+ This plugin needs:
42
+ * PHP version 5.3 or up
43
+ * WordPress version 4.2 or up
44
+
45
+ If you want support for older versions of PHP or WordPress then download and install [version 1.81](https://downloads.wordpress.org/plugin/wp-external-links.1.81.zip) of this plugin.
46
 
47
  = Sources =
48
  * [Documentation](http://wordpress.org/extend/plugins/wp-external-links/other_notes/)
49
  * [FAQ](http://wordpress.org/extend/plugins/wp-external-links/faq/)
50
  * [Github](https://github.com/freelancephp/WP-External-Links)
51
 
52
+ > <strong>Like this plugin?</strong><br>
53
+ > [Rate it](http://wordpress.org/support/view/plugin-reviews/wp-external-links-plugin) to support the development of this plugin.
54
 
55
 
56
  == Installation ==
60
  1. Search for `WP External Links` and click 'Install Now' OR click on the `upload` link to upload `wp-external-links.zip`
61
  1. Click on `Activate plugin`
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
+ == Frequently Asked Questions ==
 
65
 
66
+ = I want certain posts or pages to be ignored by the plugin. How? =
67
 
68
+ `add_action( 'wpel_apply_settings', function () {
69
+ global $post;
70
+ $excluded_post_ids = array( 1, 2, 4 );
71
 
72
+ if ( in_array( $post->ID, $excluded_post_ids ) ) {
73
+ return false;
74
+ }
75
 
76
+ return true;
77
+ }, 10 );`
 
 
78
 
79
+ = How to create a redirect for external links? (f.e. affiliate links) =
80
 
81
+ Create redirect by using the `wpel_link` action. Add some code to functions.php of your theme, like:
82
 
83
+ `add_action( 'wpel_link', function ( $link_object ) {
84
+ // check if link is an external links
85
+ if ( $link_object->isExternal() ) {
86
+ // get current url
87
+ $url = $link_object->getAttribute( 'href' );
88
 
89
+ // set redirect url
90
+ $redirect_url = '//somedom.com?url='. urlencode( $url );
91
+ $link_object->setAttribute( 'href', $redirect_url );
92
+ }
93
+ }, 10, 1 );`
94
 
95
+ = How to open external links in a new popup window? =
96
 
97
  By adding this JavaScript code to your site:
98
 
99
  `jQuery(function ($) {
100
 
101
+ $('a[data-wpel-link="external"]').click(function (e) {
102
  // open link in popup window
103
  window.open($(this).attr('href'), '_blank', 'width=800, height=600');
104
 
117
 
118
  `jQuery(function ($) {
119
 
120
+ $('a[data-wpel-link="external"]').click(function (e) {
121
  if (!confirm('Are you sure you want to open this link?')) {
122
  // cancelled
123
  e.preventDefault();
127
 
128
  });`
129
 
130
+ [Do you have a question? Please ask me](http://www.finewebdev.comcontact/)
131
 
 
132
 
133
+ == Screenshots ==
 
 
134
 
135
+ 1. Link Icons
136
+ 1. Admin Settings Page
137
+ 1. WPMU Network Settings Page
138
 
139
 
140
+ == Documentation ==
141
 
142
+ After activating you can set all options for external and internal links.
143
 
144
+ = Data attribute "data-wpel-link" =
 
145
 
146
+ It's possible to add a special data attribute to individual links, so they will be marked (and treated)
147
+ as external link, internal link, excluded link or completely ignored.
148
+
149
+ `<a href="http://somedomain.com" data-wpel-link="ignore">Go to somedomain</a>`
150
 
151
+ Possible values are `external`, `internal`, `exclude` or `ignore`. All links filtered
152
+ by the plugin will contain this data attribute and the value on how they were treated.
153
 
154
+ = Action "wpel_link" =
 
 
 
 
155
 
156
+ Use this action to change the link object after all plugin settings have been applied.
157
 
158
+ `add_action( 'wpel_link', ( $link_object ) {
159
+ if ( $link_object->isExternal() ) {
160
+ // get current url
161
+ $url = $link_object->getAttribute( 'href' );
162
 
163
+ // set redirect url
164
+ $redirect_url = '//somedom.com?url='. urlencode( $url );
165
+ $link_object->setAttribute( 'href', $redirect_url );
166
+ }
167
+ }, 10, 1 )`
168
 
169
+ The link object is an instance of WPEL_Link class (a subclass of [DOMElement](http://php.net/manual/en/class.domelement.php)).
 
170
 
 
171
 
172
+ = Filter hook "wpel_apply_settings" =
173
 
174
+ When filter returns false the plugin settings will not be applied. Can be used when f.e. certain posts or pages should be ignored by this plugin.
 
175
 
176
+ `add_filter( 'wpel_apply_settings', '__return_false' );`
 
 
 
177
 
178
+ = Filter hook "wpel_before_filter" =
 
179
 
180
+ Filter the content before searching for links and apply setttings.
181
 
182
+ `add_filter( 'wpel_before_filter', function ( $content ) {
183
+ // some code..
 
184
 
185
+ return $changed_content;
186
+ }, 10 );`
 
187
 
188
+ = Filter hook "wpel_after_filter" =
189
 
190
+ Filter the content after applying link setttings.
 
 
191
 
192
+ `add_filter( 'wpel_after_filter', function ( $content ) {
193
+ // some code..
 
194
 
195
+ return $changed_content;
196
+ }, 10 );`
197
 
198
+ = Filter hook "wpel_regexp_link" =
199
 
200
+ A filter for changing the regular expression for links. Should only be used to solve bugs on your site.
201
 
202
+ `add_filter( 'wpel_regexp_link', function ( $regexp_link ) {
203
+ $custom_regexp_link = '/<a (.*?)>(.*?)<\/a>/is';
204
+ return $custom_regexp_link;
205
+ }, 10 );`
206
+
207
+
208
+ See [FAQ](https://wordpress.org/plugins/wp-external-links/faq/) for more info.
209
 
 
 
 
 
210
 
211
  == Changelog ==
212
 
213
+ = 2.0.0 =
214
+ * REQUIREMENTS: PHP 5.3+
215
+ * Complete rebuilt
216
+ * Added `noopener` and `noreferrer`
217
+ * Added font icons (font awesome and dashicons)
218
+ * Added options for internal links
219
+ * Added Network settings (WPMU support)
220
+ * Contribution: David Page solving bug `home_url()`
221
+
222
  = 1.81 =
223
  * Security update (reported by Vulnerability Lab)
224
  * Some small changes
screenshot-1.png CHANGED
Binary file
screenshot-2.png CHANGED
Binary file
screenshot-3.png ADDED
Binary file
templates/network-page/help-tabs/under-construction.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Help Tab: Data Attributes
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ ?>
14
+ <h3><?php _e( 'Under Construction', 'wpel' ) ?></h3>
15
+ <p>
16
+ <?php _e( 'Still working on the documentation...', 'wpel' ); ?>
17
+ </p>
templates/network-page/main.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Network Admin Settings
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ *
13
+ * @var array $vars
14
+ * @option array "tabs"
15
+ * @option string "current_tab"
16
+ * @option string "page_url"
17
+ * @option string "menu_url"
18
+ * @option string "own_admin_menu"
19
+ */
20
+ ?>
21
+ <div class="wrap wpel-admin-settings">
22
+ <h1><?php echo get_admin_page_title(); ?></h1>
23
+ <?php
24
+ if ( $vars[ 'own_admin_menu' ] ):
25
+ settings_errors();
26
+ endif;
27
+
28
+ // nav tabs
29
+ $nav_tabs_template = WPEL_Plugin::get_plugin_dir( '/templates/partials/nav-tabs.php' );
30
+ WPEL_Plugin::show_template( $nav_tabs_template, $vars );
31
+
32
+ // get current option group
33
+ $tab = $vars[ 'tabs' ][ $vars[ 'current_tab' ] ];
34
+
35
+ if ( isset( $tab[ 'fields' ] ) ) {
36
+ $option_group = $tab[ 'fields' ]->get_setting( 'option_group' );
37
+ $action_url = 'edit.php?action='. $option_group;
38
+ } else {
39
+ $action_url = '';
40
+ }
41
+ ?>
42
+
43
+ <form method="post" action="<?php echo $action_url; ?>">
44
+ <?php
45
+ wp_referer_field();
46
+
47
+ $content_tab_template = __DIR__ .'/tab-contents/'. $vars[ 'current_tab' ] .'.php';
48
+ $default_tab_template = WPEL_Plugin::get_plugin_dir( '/templates/partials/tab-contents/'. $vars[ 'current_tab' ] .'.php' );
49
+
50
+ if ( is_readable( $content_tab_template ) ):
51
+ WPEL_Plugin::show_template( $content_tab_template, $vars );
52
+ elseif ( is_readable( $default_tab_template ) ):
53
+ WPEL_Plugin::show_template( $default_tab_template, $vars );
54
+ else:
55
+ $content_tab_template = WPEL_Plugin::get_plugin_dir( '/templates/partials/tab-contents/fields-default.php' );
56
+
57
+ if ( is_readable( $content_tab_template ) ):
58
+ WPEL_Plugin::show_template( $content_tab_template, $vars );
59
+ endif;
60
+ endif;
61
+ ?>
62
+ </form>
63
+ </div>
templates/partials/nav-tabs.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tab Nav
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ *
13
+ * @var array $vars
14
+ * @option array "tabs"
15
+ * @option string "current_tab"
16
+ * @option string "page_url"
17
+ */
18
+ $set_tab_active_class = function ( $tab ) use ( $vars ) {
19
+ if ( $tab === $vars[ 'current_tab' ] ) {
20
+ echo ' nav-tab-active';
21
+ }
22
+ };
23
+ ?>
24
+ <h2 class="nav-tab-wrapper">
25
+ <?php foreach ( $vars[ 'tabs' ] as $tab_key => $tab_values ): ?>
26
+ <a class="nav-tab<?php $set_tab_active_class( $tab_key ); ?> nav-tab-<?php echo $tab_key; ?>" href="<?php echo $vars[ 'page_url' ]; ?>&tab=<?php echo $tab_key; ?>">
27
+ <?php echo $tab_values[ 'icon' ]; ?> <?php echo $tab_values[ 'title' ]; ?>
28
+ </a>
29
+ <?php endforeach; ?>
30
+ </h2>
templates/partials/tab-contents/fields-default.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tab Default Content
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ *
13
+ * @var array $vars
14
+ * @option array "tabs"
15
+ * @option string "current_tab"
16
+ */
17
+
18
+ $current_tab = $vars [ 'current_tab' ];
19
+ $tab_values = $vars[ 'tabs' ][ $current_tab ];
20
+ $fields = $tab_values[ 'fields' ];
21
+
22
+ settings_fields( $fields->get_setting( 'option_group' ) );
23
+ do_settings_sections( $fields->get_setting( 'page_id' ) );
24
+
25
+ submit_button();
26
+ ?>
templates/partials/tab-contents/support.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tab Support
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ *
13
+ * @var array $vars
14
+ * @option array "tabs"
15
+ */
16
+ ?>
17
+ <h2><?php _e( 'Support', 'wpel' ); ?></h2>
18
+
19
+ <h3><?php _e( 'Documentation', 'wpel' ); ?></h3>
20
+ <p><?php _e( 'Take a look at the <a href="#" data-wpel-help>help section</a> for documentation', 'wpel' ); ?></p>
21
+
22
+ <h3><?php _e( 'FAQ', 'wpel' ); ?></h3>
23
+ <p><?php _e( 'On the <a href="https://wordpress.org/plugins/wp-external-links/faq/" target="_blank">FAQ page</a> you can find some additional tips & trics.', 'wpel' ); ?></p>
24
+
25
+ <h3><?php _e( 'Reported issues', 'wpel' ); ?></h3>
26
+ <p><?php _e( 'When you experience problems using this plugin please look if your problem was <a href="https://wordpress.org/support/plugin/wp-external-links" target="_blank">already reported</a>.', 'wpel' ); ?></p>
27
+
28
+ <h3><?php _e( 'Send your issue', 'wpel' ); ?></h3>
29
+ <p><?php _e( 'If it wasn\'t yet reported then you can <a href="https://wordpress.org/support/plugin/wp-external-links#postform" target="_blank">send your problem</a>.', 'wpel' ); ?>
30
+ <?php _e( 'Please after reporting send me the following technical information <a href="http://www.finewebdev.com/contact" target="_blank">by mail</a>. That will make it easier to solve the problem.', 'wpel' ); ?>
31
+ </p>
32
+ <p>
33
+ <button class="button js-wpel-copy"><?php _e( 'Copy Technical Info', 'wpel' ); ?></button>
34
+ </p>
35
+ <p>
36
+ <textarea id="plugin-settings" class="large-text js-wpel-copy-target" rows="8" readonly="readonly">
37
+ <?php _e( 'WP url:', 'wpel' ); ?> <?php bloginfo( 'wpurl' ); ?>
38
+
39
+ <?php _e( 'WP version:', 'wpel' ); ?> <?php bloginfo( 'version' ); ?>
40
+
41
+ <?php _e( 'PHP version:', 'wpel' ); ?> <?php echo phpversion(); ?>
42
+
43
+ <?php _e( 'Installed Plugins:', 'wpel' ); ?>
44
+
45
+ <?php
46
+ $plugins = get_plugins() ;
47
+
48
+ foreach ( $plugins as $plugin ) {
49
+ echo ' - '. $plugin[ 'Name' ] .', version: '. $plugin[ 'Version' ] ."\n";
50
+ }
51
+ ?>
52
+
53
+ <?php _e( 'WPEL Settings:', 'wpel' ); ?>
54
+
55
+ array(
56
+ <?php
57
+ foreach ( $vars[ 'tabs' ] as $tab_key => $values ) {
58
+ if ( ! isset( $values[ 'fields' ] ) ) {
59
+ continue;
60
+ }
61
+
62
+ $option_values = $values[ 'fields' ]->get_option_values();
63
+ $option_name = $values[ 'fields' ]->get_setting( 'option_name' );
64
+
65
+ echo "'$option_name' => ";
66
+ var_export( $option_values );
67
+ echo ",\n";
68
+ }
69
+ ?>
70
+ );
71
+ </textarea>
72
+ </p>
templates/requirements-notice.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Requirements Notice
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ ?>
14
+ <div class="notice notice-error is-dismissible">
15
+ <p>
16
+ <?php _e('The plugin <strong>WP External Links</strong> requires'
17
+ .' PHP version 5.3 or up and WordPress version 3.6 or up.'
18
+ .'<br>Please upgrade your PHP and/or WordPress.'
19
+ .' Deactivate the plugin to remove this message.', 'wpel' );
20
+ ?>
21
+ </p>
22
+ </div>
templates/settings-page/help-tabs/data-attributes.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Help Tab: Data Attributes
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ ?>
14
+ <h3><?php _e( 'Exclude or include by data-attribute', 'wpel' ) ?></h3>
15
+ <p>
16
+ <?php _e( 'The <code>data-wpel-link</code> attribute can be set on links and forces the plugin to treat those links that way.', 'wpel' ); ?>
17
+ </p>
18
+ <ul>
19
+ <li><?php _e( 'Links with <code>data-wpel-link="internal"</code> will be treated as internal links.', 'wpel' ); ?></li>
20
+ <li><?php _e( 'Links with <code>data-wpel-link="external"</code> will be treated as external links.', 'wpel' ); ?></li>
21
+ <li><?php _e( 'Links with <code>data-wpel-link="exclude"</code> will be treated as excluded links (which optionally can be treated as internal links).', 'wpel' ); ?></li>
22
+ <li><?php _e( 'Links with <code>data-wpel-link="ignore"</code> will be completely ignored by this plugin.', 'wpel' ); ?></li>
23
+ </ul>
templates/settings-page/help-tabs/under-construction.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Help Tab: Data Attributes
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ */
13
+ ?>
14
+ <h3><?php _e( 'Under Construction', 'wpel' ) ?></h3>
15
+ <p>
16
+ <?php _e( 'Still working on the documentation...', 'wpel' ); ?>
17
+ </p>
templates/settings-page/main.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Settings
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ *
13
+ * @var array $vars
14
+ * @option array "tabs"
15
+ * @option string "current_tab"
16
+ * @option string "page_url"
17
+ * @option string "menu_url"
18
+ * @option string "own_admin_menu"
19
+ */
20
+ ?>
21
+ <div class="wrap wpel-admin-settings">
22
+ <h1><?php echo get_admin_page_title(); ?></h1>
23
+ <?php
24
+ if ( $vars[ 'own_admin_menu' ] ):
25
+ settings_errors();
26
+ endif;
27
+
28
+ // nav tabs
29
+ $nav_tabs_template = WPEL_Plugin::get_plugin_dir( '/templates/partials/nav-tabs.php' );
30
+ WPEL_Plugin::show_template( $nav_tabs_template, $vars );
31
+ ?>
32
+
33
+ <form method="post" action="options.php">
34
+ <?php
35
+ $content_tab_template = __DIR__ .'/tab-contents/'. $vars[ 'current_tab' ] .'.php';
36
+ $default_tab_template = WPEL_Plugin::get_plugin_dir( '/templates/partials/tab-contents/'. $vars[ 'current_tab' ] .'.php' );
37
+
38
+ if ( is_readable( $content_tab_template ) ):
39
+ WPEL_Plugin::show_template( $content_tab_template, $vars );
40
+ elseif ( is_readable( $default_tab_template ) ):
41
+ WPEL_Plugin::show_template( $default_tab_template, $vars );
42
+ else:
43
+ $content_tab_template = WPEL_Plugin::get_plugin_dir( '/templates/partials/tab-contents/fields-default.php' );
44
+
45
+ if ( is_readable( $content_tab_template ) ):
46
+ WPEL_Plugin::show_template( $content_tab_template, $vars );
47
+ endif;
48
+ endif;
49
+ ?>
50
+ </form>
51
+ </div>
templates/settings-page/tab-contents/exceptions.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tab Exception
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link http://www.finewebdev.com
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ *
13
+ * @var array $vars
14
+ * @option array "tabs"
15
+ * @option string "current_tab"
16
+ */
17
+
18
+ $current_tab = $vars [ 'current_tab' ];
19
+ $tab_values = $vars[ 'tabs' ][ $current_tab ];
20
+ $fields = $tab_values[ 'fields' ];
21
+
22
+ settings_fields( $fields->get_setting( 'option_group' ) );
23
+ do_settings_sections( $fields->get_setting( 'page_id' ) );
24
+ ?>
25
+ <p class="description"><?php _e( 'De data-attribute <a href="#" data-wpel-help="data-attributes"><code>data-wpel-link</code></a> can be set on individual links to treat them as internal, external or excluded.' ); ?></p>
26
+ <?php submit_button(); ?>
wp-external-links.php CHANGED
@@ -1,68 +1,80 @@
1
- <?php defined('ABSPATH') OR die('No direct access.');
2
- /*
3
- Plugin Name: WP External Links
4
- Plugin URI: http://www.freelancephp.net/wp-external-links-plugin
5
- Description: Open external links in a new window/tab, add "external" / "nofollow" to rel-attribute, set icon, XHTML strict, SEO friendly...
6
- Author: Victor Villaverde Laan
7
- Version: 1.81
8
- Author URI: http://www.freelancephp.net
9
- License: Dual licensed under the MIT and GPL licenses
10
- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
- // constants
13
- if (!defined('WP_EXTERNAL_LINKS_FILE')) { define('WP_EXTERNAL_LINKS_FILE', __FILE__); }
14
- if (!defined('WP_EXTERNAL_LINKS_VERSION')) { define('WP_EXTERNAL_LINKS_VERSION', '1.81'); }
15
- if (!defined('WP_EXTERNAL_LINKS_KEY')) { define('WP_EXTERNAL_LINKS_KEY', 'wp_external_links'); }
16
- if (!defined('WP_EXTERNAL_LINKS_DOMAIN')) { define('WP_EXTERNAL_LINKS_DOMAIN', 'wp-external-links'); }
17
- if (!defined('WP_EXTERNAL_LINKS_OPTIONS_NAME')) { define('WP_EXTERNAL_LINKS_OPTIONS_NAME', 'WP_External_Links_options'); }
18
- if (!defined('WP_EXTERNAL_LINKS_ADMIN_PAGE')) { define('WP_EXTERNAL_LINKS_ADMIN_PAGE', 'wp-external-links-settings'); }
19
 
20
- // wp_version var was used by older WP versions
21
- if (!isset($wp_version)) {
22
- $wp_version = get_bloginfo('version');
23
- }
24
 
25
- // check plugin compatibility
26
- if (version_compare($wp_version, '3.6', '>=') && version_compare(phpversion(), '5.2.4', '>=')) {
27
 
28
- // include classes
29
- require_once('includes/wp-plugin-dev-classes/class-wp-meta-box-page.php');
30
- require_once('includes/wp-plugin-dev-classes/class-wp-option-forms.php');
31
- require_once('includes/class-admin-external-links.php');
32
- require_once('includes/class-wp-external-links.php');
33
 
34
- // create instance
35
- $WP_External_Links = new WP_External_Links();
 
36
 
37
- // Warning for the next update to version 2.x
38
- if (!function_exists('wpel_update_notice')) {
39
- function wpel_update_notice()
40
- {
41
- echo '<p style="color:#f00; font-weight:bold;">ATTENTION: This update has some major changes. Please check the changelog first!</p>';
 
 
42
  }
43
- add_action('in_plugin_update_message-' . plugin_basename(__FILE__), 'wpel_update_notice');
44
- }
45
 
46
- // init test
47
- if (class_exists('Test_WP_External_Links')) {
48
- $Test_WP_External_Links = new Test_WP_External_Links;
 
 
 
 
49
  }
50
 
51
- } else {
52
-
53
- // set error message
54
- if (!function_exists('wpel_error_notice')):
55
- function wpel_error_notice() {
56
- $plugin_title = get_admin_page_title();
57
-
58
- echo '<div class="error">'
59
- . sprintf(__('<p>Warning - The plugin <strong>%s</strong> requires PHP 5.2.4+ and WP 3.6+. Please upgrade your PHP and/or WordPress.'
60
- . '<br/>Disable the plugin to remove this message.</p>'
61
- , WP_EXTERNAL_LINKS_KEY), $plugin_title)
62
- . '</div>';
63
- }
64
 
65
- add_action('admin_notices', 'wpel_error_notice');
66
- endif;
67
 
68
- }
1
+ <?php
2
+ /**
3
+ * WP External Links Plugin
4
+ *
5
+ * @package WPEL
6
+ * @category WordPress Plugin
7
+ * @version 2.0.0
8
+ * @author Victor Villaverde Laan
9
+ * @link https://wordpress.org/plugins/wp-external-links/
10
+ * @link https://github.com/freelancephp/WP-External-Links
11
+ * @license Dual licensed under the MIT and GPLv2+ licenses
12
+ *
13
+ * @wordpress-plugin
14
+ * Plugin Name: WP External Links
15
+ * Version: 2.0.0
16
+ * Plugin URI: https://wordpress.org/plugins/wp-external-links/
17
+ * Description: Open external links in a new tab or window, adding "nofollow" and "noopener", set font icon, SEO friendly options and more.
18
+ * Author: Victor Villaverde Laan
19
+ * Author URI: http://www.finewebdev.com
20
+ * License: Dual licensed under the MIT and GPLv2+ licenses
21
+ * Text Domain: wpel
22
+ * Domain Path: /languages
23
+ */
24
+ if ( ! function_exists( 'wpel_init' ) ):
25
+ function wpel_init()
26
+ {
27
+ // only load in WP environment
28
+ if ( ! defined( 'ABSPATH' ) ) {
29
+ die();
30
+ }
31
+
32
+ // check requirements
33
+ $wp_version = get_bloginfo( 'version' );
34
+ $php_version = phpversion();
35
 
36
+ if ( version_compare( $wp_version, '3.6', '<' ) || version_compare( $php_version, '5.3', '<' ) ) {
37
+ if ( ! function_exists( 'wpel_requirements_notice' ) ) {
38
+ function wpel_requirements_notice()
39
+ {
40
+ include __DIR__ .'/templates/requirements-notice.php';
41
+ }
 
42
 
43
+ add_action( 'admin_notices', 'wpel_requirements_notice' );
44
+ }
 
 
45
 
46
+ return;
47
+ }
48
 
49
+ /**
50
+ * Autoloader
51
+ */
52
+ require_once __DIR__ . '/libs/wprun/class-wprun-autoloader.php';
 
53
 
54
+ $autoloader = new WPRun_Autoloader_1x0x0();
55
+ $autoloader->add_path( __DIR__ . '/libs/', true );
56
+ $autoloader->add_path( __DIR__ . '/includes/', true );
57
 
58
+ /**
59
+ * Load debugger
60
+ */
61
+ if ( true === constant( 'WP_DEBUG' ) ) {
62
+ FWP_Debug_1x0x0::create( array(
63
+ 'log_hooks' => false,
64
+ ) );
65
  }
 
 
66
 
67
+ /**
68
+ * Set plugin vars
69
+ */
70
+ WPEL_Plugin::create(
71
+ defined( 'TEST_WPEL_PLUGIN_FILE' ) ? TEST_WPEL_PLUGIN_FILE : __FILE__
72
+ , __DIR__
73
+ );
74
  }
75
 
76
+ wpel_init();
77
+ endif;
 
 
 
 
 
 
 
 
 
 
 
78
 
 
 
79
 
80
+ /*?>*/