Jump to content

MoritzLost

Members
  • Posts

    366
  • Joined

  • Last visited

  • Days Won

    18

Everything posted by MoritzLost

  1. Interesting, I was thinking that ProcessWire could do with a couple of alternatives to the dated CK Editor. EditorJS is one of those 'modern' editors (I was also looking at Quill), I definitely like the block-based output in JSON format. In the end I think it would be nice to be able to change the editor implementation library for textarea fields on the fly – like having a textarea field where you can select between CK Editor, EditorJS, TinyMCE etc. and being able to easily add other editors through a hook or something. Edit: Obviously this should be changed in the field settings per site, not configurable by editors themselves!
  2. This module allows you to integrate hCaptcha bot / spam protection into ProcessWire forms. hCaptcha is a great alternative to Google ReCaptcha, especially if you are in the EU and need to comply with privacy regulations. The development of this module is sponsored by schwarzdesign. The module is built as an Inputfield, allowing you to integrate it into any ProcessWire form you want. It's primarily intended for frontend forms and can be added to Form Builder forms for automatic spam protection. There's a step-by-step guide for adding the hCaptcha widget to Form Builder forms in the README, as well as instructions for API usage. Features Inputfield that displays an hCaptcha widget in ProcessWire forms. The inputfield verifies the hCaptcha response upon submission, and adds a field error if it is invalid. All hCaptcha configuration options for the widget (theme, display size etc) can be changed through the inputfield configuration, as well as programmatically. hCaptcha script options can be changed through a hook. Error messages can be translated through ProcessWire's site translations. hCaptcha secret keys and site-keys can be set for each individual inputfield or globally in your config.php. Error codes and failures are logged to help you find configuration errors. Please check the README for setup instructions. Links Github Repository and documentation InputfieldHCaptcha in the module directory Screenshots (configuration) Screenshots (hCaptcha widget)
  3. You can disable autocomplete on input elements with the autocomplete attribute. You can modify built-in forms through hooks by looking up which Process module is creating the form and then hooking after it's buildForm method. In this case, it's ProcessPageAdd. This hook sets autocomplete="off" on both the title and the name input fields: // site/init.php wire()->addHookAfter('ProcessPageAdd::buildForm', function (HookEvent $e) { $form = $e->return; // disable autocomplete for the title field $title = $form->getChildByName('title'); $title->attr('autocomplete', 'off'); // disable autocomplete for the name field $pwPageName = $form->getChildByName('_pw_page_name'); $pwPageName->attr('autocomplete', 'off'); });
  4. This error indicates that you have not added your site's domain to the allowed referers for your Google Maps API key. See the error documentation here: https://developers.google.com/maps/documentation/javascript/error-messages#deverrorcodes This didn't use to be required, but Google Maps is constantly becoming more strict with their API usage limits. Go to the cloud console and make sure to add your site's domain to the allowed referers for the key you're using, then it should start working again.
  5. Cacheable Placeholders This module allows you to have pieces of dynamic content inside cached output. This aims to solve the common problem of having a mostly cacheable site, but with pieces of dynamic output here and there. Consider this simple example, where you want to output a custom greeting to the current user: <h1>Good morning, <?= ucfirst($user->name) ?></h1> This snippet means you can't use the template cache (at least for logged-in users), because each user has a different name. Even if 99% of your output is static, you can only cache the pieces that you know won't include this personal greeting. A more common example would be CSRF tokens for HTML forms - those need to be unique by definition, so you can't cache the form wholesale. This module solves this problem by introducing cacheable placeholders - small placeholder tokens that get replaced during every request. The replacement is done inside a Page::render hook so it runs during every request, even if the response is served from the template cache. So you can use something like this: <h1>Good morning, {{{greeting}}}</h1> Replacement tokens are defined with a callback function that produces the appropriate output and added to the module through a simple hook: // site/ready.php wire()->addHookAfter('CachePlaceholders::getTokens', function (HookEvent $e) { $tokens = $e->return; $tokens['greeting'] = [ 'callback' => function (array $tokenData) { return ucfirst(wire('user')->name); } ]; $e->return = $tokens; }); Tokens can also include parameters that are parsed and passed to the callback function. There are more fully annotated examples and step-by-step instructions in the README on Github! Features A simple and fast token parser that calls the appropriate callback and runs automatically. Tokens may include multiple named or positional parameters, as well as multi-value parameters. A manual mode that allows you to replace tokens in custom pieces of cached content (useful if you're using the $cache API). Some built-in tokens for common use-cases: CSRF-Tokens, replacing values from superglobals and producing random hexadecimal strings. The token format is completely customizable, all delimiters can be changed to avoid collisions with existing tag parsers or template languages. Links Github Repository & documentation Module directory If you are interested in learning more, the README is very extensive, with more usage examples, code samples and usage instructions!
  6. Is that site still alive? Last sporadic activity in the repo was in 2018 ...
  7. Thanks! I'm not sure this needs to be posted anywhere else – there's already a lot of information on those topics out there. I primarily wrote this to (1) present all that information in a ProcessWire context, using ProcessWire modules and the class loader as examples and (2) to have it as a resource I can link to when questions regarding those topics come up. But maybe I should cross-post this to Medium for some ProcessWire exposure, there's a lot of useless stuff there anyway ?
  8. The last time I had a problem like this it was because a directory with the exact same name existed in the webroot. In this case, the request isn't routed to ProcessWire by Apache because the .htaccess by default let's through request for existing folders and files (!-f / !-d). But since that directory didn't contain an index.php / index.html, there was nothing Apache could display, so it resulted in a server-side 404 independent of ProcessWire. Not sure if this even applies to you if you are using IIS, but I'd check if you have a folder named "informationssicherheit" in your web root ...
  9. There are multiple ways to do this. The "standard" approach I'd say would be event tracking using a JavaScript tracker. If you're already using some tracking system like Google Analytics or Matomo, you can do that by attaching an event listener to the download link / button and triggering an event (guides for Google Analytics and Matomo). Of course, in this case the events will end up in your analytics software, not in the user page on your site (though if you are using some tracking software already this may be preferable). You can also roll your own version of this, by sending your JavaScript-triggered events to a custom API endpoint on your site that records the download for the current user (you can use the active session for this, or send the user ID alongside the event). Another approach which completely avoids JavaScript is to not link to your downloads directly, but instead link to yet another custom endpoint which will (1) log the request and (2) serve the download manually. I have written a bit about manually serving downloads here and here.
  10. Yes, that looks good! In both of the named groups, at least one of the selectors has to match for the entire group to match, like you wanted.
  11. Thanks for the fix @adrian, I just tested it and it seems to be working! I'm not getting errors any more with both modules I tested before ? Just butting in here: I don't believe you want to use Strict, this may break the debugger for if you visit the site by clicking on a link from somewhere else. Since you can't really use Secure cookies because of the local dev environments like you said, you'll probably be best of with SameSite=Lax to most cookies. This article on web.dev is a good resource for the SameSite changes.
  12. As an alternative to this approach, my new Cache Control module also includes an optional feature for browser cache busting. You retrieve a version string (by default, a timestamp) through the module that is stored inside the cache. As soon as you clear the cache through the module, this version string gets cleared as well, and a new version string is generated during the next request. This ensures that any assets cached by the browser are cleared alongside all server-side caches. Just throwing it out there ?
  13. You're using OR-groups, which means only one of the selectors in parentheses needs to match. This means if standort_reference or standort_alle match for a page, the other selectors don't matter. If you remove the parentheses around the selectors for standort_reference and standort_alle, the selector should work as intended. Alternatively, use named OR-groups if you want to use multiple groups of alternative selectors in your query: https://processwire.com/docs/selectors/#or-groups
  14. @adrian I have tested my other modules; I think the error only occurs when the module does NOT extend WireData, since the getArray method comes from there. TextformatterPageTitleLinks extends Textformatter which extends WireData -> RequestInfoPanel is working. ProcessCacheControl extends Process which extends WireData -> RequestInfoPanel is working. TrelloWire extends Wire (instead of WireData) -> RequestInfoPanel displays the error. When I change the class definition to extend WireData instead of Wire, the error disappears. I have tested & confirmed this behaviour with another module I'm working on (currently not publicly available, will probably publish this next week or so). So I think it comes down to modules that don't extend WireData at all, which isn't required (at least according to the docs). I guess it's mostly a cosmetic choice, but for the modules that don't really keep track of data I didn't want to extend WireData. Maybe the RequestInfoPage could check for that case and use getModuleConfig instead?
  15. I don't know why the limit is hardcoded, but I can offer a workaround for this. If you want to modify a core module without having to worry about updates, just copy the entire module folder / file from the core to your site's module directory (/site/modules/). Then execute Modules -> Refresh through the CMS. This will create a notice about the duplicate module asking you to select which module file you use. There you can select the copied module. Once whatever is not working is fixed in the core, just change that setting back and delete the duplicated module. You still have to check for changes inside the core module when you update the core, in case your older copied version is not compatible anymore. In this case, just copy the module again and reapply your changes.
  16. I don't really know that site profile, but looking at the repository there's a SASS folder, you're probably supposed to compile the CSS from that. You can do that with sass or node-sass. Just for the sake of completion: This is horrible for a live site. You usually want to have something like this during development so you don't have to do a full reload every time, but on a live site this will prevent all your visitors from ever caching your site's CSS. This will have a bad impact on page speed, so make sure to remove that on the live site!
  17. Hi @adrian, I'm getting the following error in the RequestInfo panel with many of my custom modules, do you know what's causing this? ProcessWire\WireException: Method TrelloWire::getArray does not exist or is not callable in this context in /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/wire/core/Wire.php:529 Stack trace: #0 /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/wire/core/Wire.php(386): ProcessWire\Wire->___callUnknown() #1 /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/wire/core/WireHooks.php(823): ProcessWire\Wire->_callMethod() #2 /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/wire/core/Wire.php(450): ProcessWire\WireHooks->runHooks() #3 /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/wire/core/Wire.php(453): ProcessWire\Wire->__call() #4 /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/site/assets/cache/FileCompiler/site/modules/TracyDebugger/panels/RequestInfoPanel.php(295): ProcessWire\Wire->__call() #5 /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/site/assets/cache/FileCompiler/site/modules/TracyDebugger/tracy-2.7.x/src/Tracy/Bar/Bar.php(150): RequestInfoPanel->getPanel() #6 /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/site/assets/cache/FileCompiler/site/modules/TracyDebugger/tracy-2.7.x/src/Tracy/Bar/Bar.php(122): Tracy\Bar->renderPanels() #7 /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/site/assets/cache/FileCompiler/site/modules/TracyDebugger/tracy-2.7.x/src/Tracy/Bar/Bar.php(98): Tracy\Bar->renderHtml() #8 /var/www/vhosts/klforexpats.preview-server.dev/httpdocs/public/site/assets/cache/FileCompiler/site/modules/TracyDebugger/tracy-2.7.x/src/Tracy/Debugger/Debugger.php(293): Tracy\Bar->render() #9 [internal function]: Tracy\Debugger::shutdownHandler() #10 {main} This happens in the module configuration (in this case the module is TrelloWire, but I get this with most custom modules). Is this indicative of some problem with my module? Or just some strange interaction between the module config and Tracy?
  18. I've seen a couple of questions regarding namespaces and autoloading floating around the forum recently, so I decided to write a little tutorial. In general, I often see people getting confused when they try to wrap their head around namespaces, autoloading, Composer and the mapping of namespaces to directory structures all at once. In fact, those are very much independent, distinct concepts, and it is much easier to explain and understand them separately. So this guide is structured as follows: How namespaces work in PHP. How autoloading works in PHP. Conventions for mapping namespaces to directory structures: PSR-4. How autoloading works in Composer and ProcessWire's class loader. How to use the class loader in a ProcessWire module. Feel free to skip the sections you're already familiar with. Namespaces in PHP The purpose of namespaces in PHP is to avoid naming conflicts between classes, functions and constants, especially when you're using external libraries and frameworks. Nothing more. It's important to understand that this has nothing at all to do with autoloading, directory structures or file names. You can put namespaced stuff everywhere you want. You can even have multiple namespaces inside a single file (don't try this at home). Namespaces only exist to be able to use a generic name – for example, ProcessWire's Config class – multiple times in different contexts without getting a naming conflict. Without namespaces, I couldn't use any library that includes a Config class of it's own, because that name is already taken. With namespaces, you can have a distinction between the classes ProcessWire\Config and MoritzLost\Config. You can also use sub-namespaces to further segregate your code into logical groups. For example, I can have two classes MoritzLost\Frontend\Config and MoritzLost\Backend\Config– a class name only needs to be unique within it's namespace. You can declare the namespace for a PHP file using the namespace statement at the top: // file-one.php <?php namespace ProcessWire; // file-two.php <?php namespace MoritzLost\Frontend; This way, all classes, methods and constants defined inside this file are placed in that namespace. All ProcessWire classes live in the ProcessWire namespace. Now to use one of those classes – for example, to instantiate it – you have a couple of options. You can either use it's fully qualified class name or import it into the current namespace. Also, if you are inside a namespaced file, any reference to a class is relative to that namespace. Unless it starts with a backward slash, in this case it's relative to the global namespace. So all of those examples are equivalent: // example-one.php <?php namespace ProcessWire; $page = new Page(); // example-two.php <?php use ProcessWire\Page; $page = new Page(); // example-three.php <?php $page = new ProcessWire\Page(); // example-four.php <?php namespace MoritzLost\Somewhere\Over\The\Rainbow; $page = new \ProcessWire\Page(); The use statement in the second example can be read like this: “Inside this file, all references to Page refer to the class \ProcessWire\Page” How autoloading works Every PHP program starts with one entry file – for ProcessWire, that's usually it's index.php. But you don't want to keep all your code in one file, that would get out of hand quickly. Once you start to split your code into several individual files however, you have to take care of manually including them with require or include calls. That becomes very tedious as well. The purpose of autoloading is to be able to add new code in new files without having to import them manually. This, again, has nothing to do with namespaces, not even something with file locations. Autoloading is a pretty simple concept: If you try to use a class that hasn't been loaded yet, PHP calls upon it's registered autoloaders as a last-ditch attempt to load them before throwing an exception. Let's look at a simple example: // classes.php <?php class A { /** class stuff */ } class B { /** class stuff */ } // index.php <?php spl_autoload_register(function ($class) { include_once 'classes.php'; }); new A(); new B(); This is a complete and functional autoloader. If you don't believe me, go ahead and save those two files (classes.php and index.php) and run the index.php with php -f index.php. Then comment out the include_once call and run it again, then you'll get an error that class A was not found. Now here's what happens when index.php is executed (with the autoloader active): Our anonymous function is added to the autoload queue through spl_autoload_register. PHP tries to instantiate class A, but can't because it's not loaded yet. If there was no autoloader registered, the program would die with a fatal error at this point. But since there is an autoloader ... The autoloader is called. Our autoloader includes classes.php with the class definition. That was a close one! Since the class has been loaded, execution goes back to the index.php which can now proceed to instantiate A and B. If the class was still not loaded at this point, PHP would go back to the original plan and die. One thing to note is that the autoloader will only be called once in this example. That's because both A and B are in the same file and that file is included during the first call to the autoloader. Autoloading works on files, not on classes! The important takeaway is that PHP doesn't know if the autoloader knows where to find the class it asks for or, if there are multiple autoloader, which one can load it. PHP just calls each registered autoloader in turn and checks if the class has been loaded after each one. If the class still isn't loaded after the last autoloader is done, it's error time. What the autoloader actually does is pretty much wild wild west as well. It takes the name of the class PHP is trying to load as an argument, but it doesn't have to do anything with it. Our autoloader ignores it entirely. Instead, it just includes classes.php and says to itself “My job here is done”. If class A was in another file, it wouldn't have worked. This process has two main advantages: Since autoloaders are only called on-demand to load classes just in time, we only include the files we actually need. If in the example above class A and B are not used in some scenarios, the classes.php will not be included, which will result in better performance for larger projects (though this isn't as cut and dry, since autoloading has it's own overhead, so if you load most classes anyway during a single request, it will actually be less efficient). If the autoloader is smart enough to somehow map class names to the files they're located in, we can just let the autoloader handle including the classes we need, without having to worry about jamming include statements everywhere. That brings us to ... PSR-4, namespaces and directory structures As you see, namespaces and autoloading are both pretty limited concepts. And they aren't inherently linked to each other. You can namespace your classes without ever adding an autoloader, and you can autoload classes that are all in the same namespace. But they become useful when you put them together. At the core of all that autoloading talk is a simple idea: By putting classes in files named after their class names, and putting those files in directory hierarchies based on the namespace hierarchy, the autoloader can efficiently find and load those files based on the namespace. All it needs is a list of root namespaces with their corresponding directories. The exact way class names and namespaces are mapped to directory structures and file names is purely conventional. The accepted convention for this is PSR-4. This is a super simple standard which basically just sums up the ideas above: A base namespace is mapped to a specific directory in the file system. When the autoloader is asked to load a class in that namespace (or a sub-namespace of it), it starts looking in that folder. This "base" namespace may include multiple parts – for example, I could use MoritzLost\MyAwesomeLibrary as a base and map that to my source directory. PSR-4 calls this a "namespace prefix". Each sub-namespace corresponds to a sub-directory. So by looking at the namespace, you can follow subdirectories to the location where you expect to find the class file. Finally, the class name is mapped directly to the file name. So MyCoolClass needs to be put inside MyCoolClass.php. This all sounds simple and straightforward - and it absolutely is! It's only once you mash everything together, mix up language features, accepted conventions and proprietary implementations like Composer on top that it becomes hard to grasp in one go. Composer and ProcessWire's class loader Now all that's left is to talk about how Composer and ProcessWire provide autoloading. Composer, of course, is primarily a tool for dependency management. But because most libraries use namespaces and most developers want to have the libraries they're using autoloaded, those topics become a prerequisite to understanding what Composer does in this regard. Composer can use different autoloading mechanisms; for example, you can just give it a static list of files to include for every request, or use the older PSR-0 standard. But most modern libraries use PSR-4 to autoload classes. So all Composer needs to function is a mapping of namespace prefixes to directories. Each library maintains this mapping for it's PSR-4-structured classes through the autoload information in their composer.json. You can do this for your own site to: Just include the autoload information as shown in the documentation and point it to the directory of your class files. Composer collects all that information and uses it to generate a custom file at vendor/autoload.php — that's the one you need to include somewhere whenever you set up Composer in one of your projects. Bells and whistles aside, this file just registers an autoloader function that will use all the information collected from your own and your included libraries' composer.json to locate and include class files on demand. You can read more about how to optimize Composer's autoloader for production usage here. If you want to read up on how to set up Composer for your own sites, read my ProcessWire + Composer integration guide instead. And finally, what does ProcessWire do to handle all this? Turns out, ProcessWire has it's own autoloader implementation that is more or less PSR-4 compliant. You can access it as an API variable ($classLoader or wire('classLoader'), depending on context). Instead of using a static configuration file like Composer, the namespace -> directory mapping is added during the runtime by calling $classLoader->addNamespace. As you would expect, this function accepts a namespace and a directory path. You can use this to register your own custom namespaces. Alternatively, if you have site-specific classes within the ProcessWire namespace, you can just add their location to the class loader using the same method: $classLoader->addNamespace('ProcessWire', '/path/to/your/classes/'). Utilizing custom namespaces and autoloading in ProcessWire modules Now as a final remark, I wanted to give an example of how to use custom namespaces and the class loader in your own modules. I'll use my TrelloWire module as an example: Decide what namespace you're going to use. The main module file should live in the ProcessWire namespace, but if you have other classes in your module, they can and should use a custom namespace to avoid collisions with other modules. TrelloWire uses ProcessWire\TrelloWire, but you can also use something outside the ProcessWire namespace. You need to make sure to add the namespace to the class loader as early as possible. If either you or a user of your module tries to instantiate one of your custom classes before that, it will fail. Good places to start are the constructor of your main module file, or their init or ready methods. Here's a complete example. The module uses only one custom namespaced class: ProcessWire\TrelloWire\TrelloWireApi, located in the src/ directory of the module. But with this setup, I can add more classes whenever I need without having to modify anything else. /** * The constructor registers the TrelloWire namespace used by this module. */ public function __construct() { $namespace = 'ProcessWire\\TrelloWire'; $classLoader = $this->wire('classLoader'); if (!$classLoader->hasNamespace($namespace)) { $srcPath = $this->wire('config')->paths->get($this) . 'src/'; $classLoader->addNamespace($namespace, $srcPath); } } Source Thanks for making it through to the very end! I gotta learn to keep those things short. Anyway, I hope this clears up some questions about namespaces and autoloading. Let me know if I got something wrong, and feel free to add your own tips and tricks!
  19. Sorry for the convoluted title. I have a problem with Process modules that define a custom page using the page key through getModuleInfo (as demonstrated in this excellent tutorial by @bernhard). Those pages are created automatically when the module is installed. The problem is that the title of the page only gets set in the current language. That's not a problem if the current language (language of the superuser who is installing the module) is the default language; if it isn't, the Process page is missing a title in the default language. This has the very awkward effect that a user using the backend in the default language (or any other language) will see an empty entry in the setup menu: This screenshot comes from my Cache Control module which includes a Process page. Now I realize the description sounds obscure, but for us it's a common setup: We a multiple bilingual sites where the default language is German and the second language is English. While the clients use the CMS in German, as a developer I prefer the English interface, so whenever I install a Process module I get this problem. As a module author, is there a way to handle this situation? I guess it would be possible to use post-installation hooks or create the pages manually, but I very much prefer the declarative approach. The page title is already translatable (through the __ function), but of course at the time of installation there is no translation, and as far as I'm aware it's not possible to ship translations with a module so they are used automatically. Could this situation be handled better in the core? I would prefer if the module installation process would always set the title of the Process page in the default language, instead of the language of the current user.
  20. @Greg Lumley I'd actually recommend setting up your blog and categories on your own – you'll realize it's dead simple. Especially when coming from WordPress development, it's important to realize that often the best solution is not a pre-made module, but a custom data structure. This will leave you in full control, and also help you understand how to use pages and template to structure your content.
  21. I usually use regular pages for categories, and keep categories in a completely separate sub-section of the page tree. This requires two templates: category-index: This is the "master" page, which holds all the categories. Set to allow only one page, and only allow children of type "category". category: Each category page represents a category. Category pages are set to allow only the category-index as it's parent page – or allow other category pages, this way you can build hierarchical categories. Then you only need a Page Reference field targeting the category pages. This field can either be a single-option select if you only want to allow one category per post, or a multivalue field for multiple categories. The benefit of this approach is that editors can easily create new categories, and you can use normal page access controls to decide who can edit and add categories. You can easily change their sort setting to sort categories alphabetically or whatever you want. Pages are also way easier to access, search for and translate from the API side. For example, getting a list of categories is as easy as: $pages->get('template=category-index')->children()
  22. Can we see your complete code? Because currently you're looping over the individual levels, but nothing gets added to your array, so I don't quite see how you're building your 5-dimensional array. In any case, you can use array_map to convert an array of page objects to an array of page fields. This function expects a normal array, whereas $level4->children returns a WireArray. But you can use WireArray::getArray() to get the objects in the WireArray as a plain array: $pagesData = array_map(function ($artikel) { return [ 'field1' => $artikel->field1, 'field2' => $artikel->field2, 'field3' => $artikel->field3, ]; }, $level4->children->getArray()); // $pagesData now holds an array of arrays corresponding to pages which in turn contain the fields for that page
  23. Great ? You can tell what kind of tiles you're using by the API endpoint your tileLayer is calling. My code above uses /tiles/ after the style which is the endpoint for raster tiles. Or check the network tab in the Devtools, if your tile requests are returning PNGs you're using raster tiles, for vector tiles it would probably be JSON. You're not supposed to scroll out ? No idea honestly, IIRC we adapted one of the default styles slightly, might've been an oversight. Doesn't really matter since the map just focusses around Cologne, so normally you won't see the country names ?
  24. We are using Mapbox and a Studio style with Leaflet for the Architekturführer Köln Map. Are you using raster or vector tiles? We're using raster tiles, in this case it may be caused by the zoomOffset and tileSize in your tileLayer. For reference, here's our tileLayer definition: const tileLayer = L.tileLayer( "https://api.mapbox.com/styles/v1/schwarzdesign/{STYLE_ID}/tiles/256/{z}/{x}/{y}@2x?access_token={TOKEN}", { maxZoom: 18, tileSize: 256, zoomOffset: 0, attribution: '<a href="https://www.openstreetmap.org/copyright">© OpenStreetMap</a> | <a href="https://www.mapbox.com/map-feedback/#/-74.5/40/10">Improve this map</a>', } ); Previously we used tileSize: 512 and zoomOffset: -1 to cut down on the number of tile requests. This way each tile was much larger, which resulted in much larger text. In any case, you could use those settings to offset your scaling issues (though this is of course a workaround, not a fix). If you're using vector tiles I have no idea ?
  25. Based on the explode documentation I agree with your expected result. The reason your code doesn't work is that the callback is never called if the WireArray items are not objects. Not sure if that is intentional, but the documentation is definitely misleading on this one. I also see no reason why the callback variant shouldn't work for non-object items. You should open an issue on Github for this bug!
×
×
  • Create New...