For Your Eyes Only is a 1981 British spy film directed by John Glen (in his feature directorial debut) and produced by Albert R. Broccoli. The film stars Roger Moore as the fictional MI6 agent James Bond, and co-stars Carole Bouquet, Chaim Topol, Lynn-Holly Johnson, and Julian Glover.
The twelfth film in the James Bond franchise produced by Eon Productions, For Your Eyes Only was written by Richard Maibaum and Michael G. Wilson, based on two Ian Fleming short stories "For Your Eyes Only" and "Risico". In the plot, Bond attempts to locate a missile command system while becoming tangled in a web of deception spun by rival Greek businessmen along with Melina Havelock, a woman seeking to avenge the murder of her parents. Some writing elements were inspired by the novels Live and Let Die, Goldfinger, and On Her Majesty's Secret Service.
After the science fiction-focused Moonraker, the producers wanted a return to the style of the early Bond films and the works of 007 creator Fleming. For Your Eyes Only followed a grittier, more realistic approach and a narrative theme of revenge and its consequences rather than the fantasy narrative of Moonraker. Filming locations included Greece, Italy and England, while underwater footage was shot in The Bahamas. Scottish pop star Sheena Easton performed the title theme song.
For Your Eyes Only was released in the UK on 24 June 1981 and in the US two days later; it received a mixed to positive critical reception. The film's reputation has improved over time, with reviewers praising the more serious tone. The film was a financial success, generating $195.3 million worldwide. This was the final Bond film to be distributed solely by United Artists; the company was absorbed by Metro-Goldwyn-Mayer soon after this film's release.
Plot[]
The British information gathering vessel St Georges, which holds the Automatic Targeting Attack Communicator (ATAC), the system used by the Ministry of Defence to co-ordinate the Royal Navy's fleet of Polaris submarines, is sunk after accidentally trawling an old naval mine in the Ionian Sea. A marine archaeologist, Sir Timothy Havelock, is asked by the British to secretly locate the St Georges. However, he and his wife are murdered by a Cuban hitman, Hector Gonzales. His daughter, Melina Havelock, witnesses the murder and vows revenge.
The head of the KGB, General Gogol, has also learned of the fate of the St Georges and already notified his contact in Greece. MI6 agent James Bond is ordered by the Minister of Defence, Sir Frederick Gray, and MI6 Chief of Staff Bill Tanner to retrieve the ATAC before the Soviets since the transmitter could order attacks by the submarines' Polaris ballistic missiles. Bond goes to Spain to find out who hired Gonzales.
While spying on Gonzales's villa, Bond is captured by his men, but escapes as Gonzales is killed by a crossbow bolt. Outside, he finds the assassin was Melina and the two escape. With the help of Bond, Q uses computerised technology to identify the man Bond saw paying off Gonzales as Emile Leopold Locque, and then goes to Locque's possible base in Cortina, Italy. There Bond meets his contact, Luigi Ferrara, and a well-connected Greek businessman and intelligence informant, Aris Kristatos, who tells Bond that Locque is employed by Milos Columbo, known as "the Dove" in the Greek underworld, Kristatos's former resistance partner during the Second World War. After Bond goes with Kristatos's protégée, figure skater Bibi Dahl, to a biathlon course, a group of three men, which includes East German biathlete Eric Kriegler, chases Bond, trying to kill him. Bond escapes and then goes with Ferrara to bid Bibi farewell in an ice rink, where he fends off another attempt on his life by men in ice hockey gear. Ferrara is killed in Bond's car, with a dove pin in his hand. Bond then travels to Corfu in pursuit of Columbo.
There, at the casino, Bond meets Kristatos and asks how to meet Columbo, not knowing that Columbo's men are secretly recording their conversation. After Columbo and his mistress, Countess Lisl von Schlaf, argue, Bond offers to escort her home with Kristatos's car and driver. The two then spend the night together. In the morning, Lisl and Bond are ambushed by Locque and Lisl is killed. Bond is captured by Columbo's men before Locque can kill him; Columbo then tells Bond that Locque was actually hired by Kristatos, who is working for the KGB to retrieve the ATAC. Bond accompanies Columbo and his crew on a raid on one of Kristatos's opium-processing warehouses in Albania, where Bond uncovers naval mines similar to the one that sank the St Georges, suggesting it was not an accident. After the base is destroyed, Bond chases Locque and kills him.
Afterwards, Bond meets Melina, and they recover the ATAC from the wreckage of the St Georges, but Kristatos is waiting for them when they surface and he takes the ATAC. After the two escape an assassination attempt, they discover Kristatos's rendezvous point when Melina's parrot repeats the phrase "ATAC to St Cyril's". With the help of Columbo and his men, Bond and Melina break into St Cyril's, an abandoned mountaintop monastery. As Columbo confronts Kristatos, Bond kills Kriegler.
Bond retrieves the ATAC system and stops Melina from killing Kristatos after he surrenders. Kristatos tries to kill Bond with a hidden flick knife, but is killed by a knife thrown by Columbo; Gogol arrives by helicopter to collect the ATAC, but Bond throws it off the cliff. Bond and Melina later spend a romantic evening aboard her father's yacht when he receives a call from the Prime Minister.
Cast[]
- Roger Moore as James Bond, MI6 agent 007, who is sent to retrieve a stolen "ATAC" system that could be misused for controlling British submarines.
- Carole Bouquet as Melina Havelock, the daughter of marine archaeologists who are murdered while tracking down the ATAC's whereabouts. Bouquet had auditioned for the role of Holly Goodhead in Moonraker, but was unsuccessful.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
- Topol as Milos Columbo, Kristatos's enemy and former smuggling partner. He convinces Bond to side with him. Named after Gioacchino Colombo, the Ferrari engine designer, specifically Ferrari 125, which Fleming admired.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Topol suggested the pistachios as a trademark of the character, which are used in the warehouse assault scene to orient Columbo's men on where to shoot.[3]
- Lynn-Holly Johnson as Bibi Dahl, an ice-skating prodigy who has a crush on Bond; she is training with the financial support of Kristatos. Johnson was an ice skater before turning to acting, and achieved second place at the novice level of the 1974 United States Figure Skating Championships.[4] Michael Wilson explained that she was written as "a character that antagonised Bond."[5]
- Julian Glover as Aristotle Kristatos, a former war hero turned smuggler. Initially shown as an ally, later as the main villain, planning to expand his fortune by selling the ATAC to the KGB. Glover had been shortlisted as a possible Bond for Live and Let Die, eventually losing out to Moore.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
- Cassandra Harris as Countess Lisl von Schlaf, Columbo's mistress. At the time of filming Harris was married to future Bond actor Pierce Brosnan, and the couple lunched with the film's producer Albert R. Broccoli during filming.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
- Michael Gothard as Emile Leopold Locque, a Belgian hired assassin known by his octagonal-rimmed glasses. He turns out to be an associate of Kristatos.
- Jill Bennett as Jacoba Brink, Bibi's skating coach.
- Jack Hedley as Sir Timothy Havelock, Melina's father and a marine archaeologist hired by the British Secret Service to secretly locate the wreck of St. Georges.
- Walter Gotell as General Anatoly Gogol, head of the KGB.
- James Villiers as Bill Tanner, MI6 Chief of Staff. The role of Tanner first appeared on film in The Man with the Golden Gun, although in an un-credited capacity. Villiers presumed he would play the role of M in subsequent films and was disappointed not to be asked; the producers thought him too young for the role and wanted an actor in his 70s.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
- Desmond Llewelyn as Q, the head of MI6's technical department.
- John Moreno as Luigi Ferrara, 007's MI6 contact in northern Italy.
- Geoffrey Keen as Sir Frederick Gray (credited as Minister of Defence), a politician in the British government. The role, along with Bill Tanner as Chief of Staff, was used to brief Bond in place of M, following the death of Bernard Lee.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
- Lois Maxwell as Miss Moneypenny, M's secretary.
- John Wyman as Eric Kriegler, an East German Olympic class biathete. He is revealed to be Kristatos's second in-command and a KGB contact.
John Hollis plays the bald villain in wheelchair,Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). voiced by Robert Rietti.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). The character appears in the pre-credits sequence and is both unnamed and uncredited. The character contains a number of characteristics of Ernst Stavro Blofeld,Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). but could not be identified as such because of the legal reasons surrounding the Thunderball controversy with Kevin McClory claiming sole rights to the Blofeld character, a claim disputed by Eon.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Bob Simmons, who previously portrayed Bond in the gun barrel sequences in the first three films and SPECTRE agent Colonel Jacques Bouvar in Thunderball, cameos as another villain as Gonzales's henchman who falls victim to Bond's exploding Lotus.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Victor Tourjansky, the assistant director, has his third cameo in the Bond films as a drinking tourist;[6] he is credited as part of the Ski Team for Stunts. Janet Brown plays the British Prime Minister Margaret Thatcher, who appears in the closing scene alongside John Wells as Denis Thatcher.[7]
Production[]
- “We had gone as far as we could into space. We needed a change of some sort, back to the grass roots of Bond. We wanted to make the new film more of a thriller than a romp, without losing sight of what made Bond famous – its humour.”
- ―John Glen[8]
Ian Fleming wrote the original story "For Your Eyes Only" as an episode of a cancelled James Bond television series at CBS in 1958.[5] Eon Productions originally intended to produce For Your Eyes Only after The Spy Who Loved Me.[9][10] However, after the success of Star Wars in 1977 the producers decided to produce Moonraker instead.[11] Moonraker was successful yet was very expensive to produce, and shortly afterwards United Artists suffered a major financial flop with Michael Cimino's Heaven's Gate. This, along with the early 1980s recession, required For Your Eyes Only to have a lower budget. For Your Eyes Only marked a change in the make up of the production crew. The previous series directors Terence Young, Guy Hamilton, Lewis Gilbert, and Peter Hunt were unable to direct because the studio could not afford to hire them, and John Glen was promoted from his duties as a film editor to director, a position he would occupy for four subsequent films.[5][12] Glen brought on much of his second-unit direction team from The Spy Who Loved Me and Moonraker, including cinematographer Alan Hume.[5] The transition in directors and lower budget resulted in a harder-edged directorial style, with less emphasis on gadgetry and large action sequences in huge arenas as was favored by Gilbert in the previous two films.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Emphasis was placed on tension, plot and character in addition to a return to Bond's more serious roots,Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). whilst For Your Eyes Only "showed a clear attempt to activate some lapsed and inactive parts of the Bond mythology."Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
The film was also a deliberate effort to bring the series more back to reality, following the success of Moonraker in 1979.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). As co-writer Michael G. Wilson pointed out, "If we went through the path of Moonraker things would just get more outlandish, so we needed to get back to basics".[12] To that end, the story that emerged was simpler, not one in which the world was at risk, but returning the series to that of a Cold War thriller;[12] Bond would also rely more on his wits than gadgets to survive.[13] Glen decided to symbolically represent it with a scene where Bond's Lotus blows itself up and forces 007 to rely on Melina's more humble Citroën 2CV.[12] Since Ken Adam was busy with Pennies from Heaven, Peter Lamont, who had worked in the art department since Goldfinger, was promoted to production designer. Following a suggestion of Glen, Lamont created realistic scenery, instead of the elaborate set pieces for which the series had been known.[12]
Writing[]
Before the project was postponed in favor of Moonraker, Tom Mankiewicz had written a storyline and Christopher Wood submitted a first draft in January 1978. However, their screenplay did not influence the final film.[9][10] Richard Maibaum was once again the scriptwriter for the story, assisted by Michael G. Wilson.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). According to Wilson, the ideas from stories could have come from anyone as the outlines were worked out in committee that could include Broccoli, Maibaum, Wilson and stunt coordinators.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Much of the inspiration for the stories for the film came from two Ian Fleming short stories from the collection For Your Eyes Only: Risico and For Your Eyes Only.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Another set-piece from the novel of Live and Let Die – the keelhauling – which was unused in the film of the same name, was also inserted into the plot.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Other ideas from Fleming were also used in For Your Eyes Only, such as the Identigraph which comes from the novel Goldfinger, where it was originally called the "Identicast".Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). These elements from Fleming's stories were mixed with a Cold War story centred on the MacGuffin of the ATAC.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). An initial treatment for this film was submitted by Ronald Hardy, an English novelist and screenwriter in 1979. Hardy's treatment included the involvement of a character named Julia Havelock whose parents were assassinated by a man named Gonzales.[14]
The pre-title sequence of For Your Eyes Only has been described as either "out-of place and disappointing"Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). or "roaringly enjoyable".Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). The scene was shot to introduce a potential new Bond to audiences, thus linking the new actor to elements from previous Bond filmsLua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). (see casting, below).
The sequence begins with Bond laying flowers at the grave of his wife Tracy Bond, before a Universal Exports helicopter picks him up for an emergency. Control of the helicopter is taken over by remote control by a bald man in a grey Nehru jacket with a white cat. This character is unnamed in either the film or the credits, although he looks and sounds like Ernst Stavro Blofeld as played by Donald Pleasence or Telly Savalas.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Director John Glen referred to the identity of the villain obliquely: "We just let people use their imaginations and draw their own conclusions ... It's a legal thing".Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Originally the character was going to be explicitly identified as Blofeld, but was deliberately not named due to copyright restrictions with Kevin McClory, who owned the film rights to Thunderball, which supposedly includes the character Ernst Stavro Blofeld, the organisation SPECTRE, and other material associated with the development of Thunderball.[5][15] Eon disputed McClory's ownership of the Blofeld character, but decided not to use him again: the scene was "a deliberate statement by Broccoli of his lack of need to use the character."Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
Maibaum later said "We tried to return to the earlier films with For Your Eyes Only but we didn't have Sean to make it real. And I was very disappointed with the way the love story was handled. The whole idea was that the great lover James Bond can't get to first base with this woman because she was so obsessed with avenging her parents' death. Nothing was ever done with it. It was as if the director didn't feel there was a love story there at all."[16]
Casting[]
Roger Moore had originally signed a three-film contract with Eon Productions, which covered his first three appearances up to The Spy Who Loved Me. Subsequent to this, the actor negotiated contracts on a film-by-film basis.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Uncertainty surrounding his involvement in For Your Eyes Only, considering his age, led to other actors being considered to take over, including Lewis Collins, known in the UK for his portrayal of Bodie in The Professionals;[17] Ian Ogilvy, known for role as Simon Templar in Return of the Saint (played by Moore in the original series);[18] Michael Billington, who previously appeared in The Spy Who Loved Me as Agent XXX's ill-fated lover Sergei Barzov, (Billington's screen test for For Your Eyes Only was one of the five occasions he auditioned for the role of Bond)[19] and Michael Jayston, who had appeared as the eponymous spy in the British TV series of QuillerLua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). (Jayston eventually played Bond in a BBC Radio production of You Only Live Twice in 1985).Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Timothy Dalton was strongly considered but Dalton declined, as he disliked the direction the series was taking at the time.[18][20][21][22] When Moore discovered that Broccoli and Saltzman were screen-testing actors without his knowledge, he announced in the Daily Mail that he would not return to play Bond.[23] However, the producers convinced him to return just two weeks later.[24]
Bernard Lee died of cancer on 16 January 1981, after filming began on For Your Eyes Only, but before he could film his scenes as M, the head of MI6, as he had done in the previous eleven films of the series. Out of respect, no new actor was hired to assume the role as Broccoli refused to have the character recast and, instead, the script was re-written so that the character is said to be on leave, letting Chief of Staff Bill Tanner take over the role as acting head of MI6 and briefing Bond alongside the Minister of Defence.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). This is only the second time M has been omitted from a Bond production, the first being the 1954 TV adaptation of Casino Royale. Ironically, the original "For Your Eyes Only" short story dealt with Bond being given a personal mission by M, one of the few times in the Fleming canon that 007 did his superior a direct favor.
Chaim Topol was cast following a suggestion by Broccoli's wife Dana, while Julian Glover joined the cast as the producers felt he was stylish – Glover was even considered to play Bond at some point, but Michael G. Wilson stated that "when we first thought of him he was too young, and by the time of For Your Eyes Only he was too old".[12] Carole Bouquet was a suggestion of United Artists publicist Jerry Juroe, and after Glen and Broccoli saw her in That Obscure Object of Desire, they went to Rome to invite Bouquet for the role of Melina.[25]
Filming[]
A Citroën 2CV 007, the one used in the film.
Production of For Your Eyes Only began on 2 September 1980 in the North Sea, with three days shooting exterior scenes with the St Georges.[12][26] Although the previous film had been shot almost entirely outside of the United Kingdom, the new Conservative Prime Minister Margaret Thatcher's tax cuts allowed the shoot to return to Britain.[5] The interiors were shot later in Pinewood Studios, as well as the ship's explosion, which was done with a miniature in Pinewood's tank on the 007 Stage.[27] On 15 September principal photography started on Corfu at the Villa Sylva at Kanoni, above Corfu Town,Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). which acted as the location of the Spanish villa. Many of the local houses were painted white for scenographic reasons.[28] Glen opted to use the local slopes and olive trees for the chase scene between Melina's Citroën 2CV and Gonzales's men driving Peugeot 504s.[25] The scene was shot across twelve days, with stunt driver Rémy Julienne – who would remain in the series up until GoldenEye – driving the Citroën.[29] Four 2CVs were used, with modifications for the stunts – all had more powerful flat-four engines, and one received a special revolving plate on its roof so it could get turned upside down.[30][31][32]
In October filming moved to other Greek locations, including Meteora and the Achilleion.[33] In November, the main unit moved to England, which included interior work in Pinewood, while the second unit shot underwater scenes in The Bahamas. On 1 January 1981, production moved to Cortina d'Ampezzo in Italy, where filming wrapped in February.[12] Since it was not snowing in Cortina d'Ampezzo by the time of filming, the producers had to pay for trucks to bring snow from nearby mountains, which was then dumped in the city's streets.[34]
Many of the underwater scenes, especially involving close-ups of Bond and Melina, were faked on a dry soundstage. A combination of lighting effects, slow-motion photography, wind, and bubbles added in post-production, gave the illusion of the actors being underwater.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Actress Carole Bouquet reportedly had a pre-existing health condition that prevented her from performing underwater stunt work.[12] Aquatic scenes were done by a team led by Al Giddings, who had previously worked on The Deep, and filmed in either Pinewood's tank on the 007 Stage or an underwater set built in the Bahamas. Production designer Peter Lamont and his team developed two working props for the submarine Neptune, as well as a mock-up with a fake bottom.[35]
Roger Moore was reluctant to film the scene of Bond kicking a car, with Locque inside, over the edge of a cliff, saying that it "was Bond-like, but not Roger Moore Bond-like."[12] Michael G. Wilson later said that Moore had to be persuaded to be more ruthless than he felt comfortable.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Wilson also added that he and Richard Maibaum, along with John Glen, toyed with other ideas surrounding that scene, but ultimately everyone, even Moore, agreed to do the scene as originally written.[12]
The Monastery of the Holy Trinity in Meteora served as a location
For the Meteora shoots, a Greek bishop was paid to allow filming in the monasteries, but the uninformed Eastern Orthodox monks were mostly critical of production rolling in their installations. After a trial in the Greek Supreme Court, it was decided that the monks' only property were the interiors – the exteriors and surrounding landscapes were from the local government. In protest, the monks remained shut inside the monasteries during the shooting,[12][28] and tried to sabotage production as much as possible, hanging their washing out of their windowsLua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). and covering the principal monastery with plastic bunting and flags to spoil the shots, and placing oil drums to prevent the film crew from landing helicopters. The production team solved the problem with back lighting, matte paintings, and building both a similar scenographic monastery on a nearby unoccupied rock, and a monastery set in Pinewood.[26]
Roger Moore said he had a great fear of heights, and to do the climbing in Greece, he resorted to moderate drinking to calm his nerves.[12] Later in that same sequence, Rick Sylvester, a stuntman who had previously performed the pre-credits ski jump in The Spy Who Loved Me, undertook the stunt of Bond falling off the side of the cliff.[36] The stunt was dangerous, since the sudden rope jerk at the bottom could be fatal. Special effects supervisor Derek Meddings developed a system that would dampen the stop, but Sylvester recalled that his nerves nearly got the better of him: "From where we were [shooting], you could see the local cemetery; and the box [to stop my fall] looked like a casket. You didn't need to be an English major to connect the dots." The stunt went off without a problem.[12]
Bond cameraman and professional skier Willy Bogner, Jr. was promoted to director of a second unit involving ski footage.[34] Bogner designed the ski chase on the bobsleigh track of Cortina d'Ampezzo hoping to surpass his work in both On Her Majesty's Secret Service and The Spy Who Loved Me.[12] To allow better filming, Bogner developed both a system where he was attached to a bobsleigh, allowing to film the vehicle or behind it,[36] and a set of skis that allowed him to ski forwards and backwards to get the best shots.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). In February 1981, on the final day of filming the bobsleigh chase, one of the stuntmen driving a sleigh, 23-year-old Paolo Rigon, was killed when he became trapped under the bob. The incident, which took place a week after the FIBT World Championships 1981 where USA-1 bobsled driver James Morgan was killed in a crash during the four-man sled competition, resulted in the shortening of the track for future FIBT events.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
The pre-credits sequence used a church in Stoke Poges, Buckinghamshire as a cemetery, while the helicopter scenes were filmed at the abandoned Beckton Gas Works in London.[33] The gas works were also the location for some of Stanley Kubrick's film, Full Metal Jacket (1987).Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Director John Glen gained the idea for the remote-controlled helicopter after seeing a child playing with an RC car.[12] Since flying a helicopter through a warehouse was thought to be too dangerous, the scene was shot using forced perspective. A smaller mock-up was built by Derek Meddings' team closer to the camera that the stunt pilot Marc Wolff flew behind and this made it seem as if the helicopter was entering the warehouse. The footage inside the building was shot on location, though with a life-sized helicopter model which stood over a rail. Stuntman Martin Grace was a stand-in as Bond when the agent is dangling outside the flying helicopter, while Roger Moore himself was used in the scenes inside the model.[25][37]
Music[]
- Main article: For Your Eyes Only (soundtrack)
The score of For Your Eyes Only was written by Bill Conti, who retained some John Barry-influenced brass elements in the score, but also added elements of dance and funk music.[38] While one reviewer observed that "Bill Conti's score is a constant source of annoyance",[39] another claimed that "In the end, For Your Eyes Only stands as one of the best James Bond film scores of the 1980s."[38]
The title song, written by Conti and Michael Leeson, was sung by Sheena Easton, who was the first title song artist to appear on screen in a Bond film,Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). as designer Maurice Binder liked Easton's appearance and decided to add her to the opening credits.[40] The producers of the film hired Debbie Harry to sing Conti and Leeson's song, but she quit when the producers refused to allow her band Blondie to write and perform an original song for the film.[41] Blondie's rejected song, titled "For Your Eyes Only," can be found on their 1982 album, The Hunter.[42]
Release and reception[]
For Your Eyes Only was premiered at the Odeon Leicester Square in London on Template:Nowrap,[43] setting an all-time opening-day record for any film at any cinema in the UK with a gross of £14,998[44] (£Template:Inflation in Template:Inflation-year poundsTemplate:Inflation-fn).[44] The film entered general release in the UK the same day. For Your Eyes Only had its North American premiere in Canada and the US on Friday 26 June, at approximately 1,100 cinemas.[44]
For Your Eyes Only watch, released as part of the merchandising for the film
The film grossed $54.8 million in the United States,[45] (equivalent to $101.5 million at 2011 ticket prices[46] or $Template:Inflation million in Template:Inflation-year dollars,Template:Inflation-fn adjusted for general inflation) and $195.3 million worldwide,Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). becoming the second highest grossing Bond film after its predecessor, Moonraker.[45] This was the last James Bond film to be solely released by United Artists, as by this time its owner, Transamerica Corporation, finalized the sale of the company to MGM. Following the MGM and United Artists merger, later runs including future entries were released under "MGM/UA Distribution Co".
The promotional cinema poster for the film featured a woman holding a crossbow; she was photographed from behind, and her outfit left the bottom half of her buttocks exposed. The effect was achieved by having the model wear a pair of bikini bottoms backwards, so that the part seen on her backside is the front of the suit.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). The poster caused some furor—largely in the US—with The Boston Globe and the Los Angeles Times considering the poster so unsuitable they edited out everything above the knee,[47] whilst the Pittsburgh Press editors painted a pair of shorts over the legs.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). There was significant speculation as to the identity of the model before photographer Morgan Kane identified her as Joyce Bartle.[47]
A number of items of merchandising were issued to coincide with the film, including a 007 digital watch and a copy of Melina's Citroën 2CV by Corgi Toys.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Citroën itself produced a special "007" edition of the 2CV, which even had decorative bullet holes on the door.[48] Marvel Comics also did a comic book adaptation (see section below).Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
Contemporary reviews[]
Derek Malcolm in The Guardian disliked the film, saying it was "too long ... and pretty boring between the stunts", although he admitted that the stunts were of a high quality.[49] According to Malcolm, Bond "inhabits a fantasy-land of more or less bloodless violence, groinless sex and naivety masked as superior sophistication", with Moore playing him as if in a "nicely lubricated daze".[49] Although Malcolm tipped the film for international box office success, he observed that he "can't quite see why the series has lasted so long and so strong in people's affections."[49] Writing in The Observer, Philip French commented that "not for the first time the pre-credits sequence is the best thing about the film."[50] French was dismissive of Moore's Bond, saying that Bond was "impersonated by Moore" and referred to Moore's advancing years.[50]
David Robinson, writing in The Times bemoaned the fact that the "dramatic bits between the set pieces don't count for much." Like other critics at the time his praise was more directed towards the stunt crews; they were "better than ever in this one."[51] The film critic for the magazine Time Out was brief and pithy: "no plot and poor dialogue, and Moore really is old enough to be the uncle of those girls."[52]
For the US press, Gary Arnold in The Washington Post thought the film was "undeniably easy on the eyes", and further added "maybe too easy to prevent the mind from wandering and the lids from drooping."[53] Arnold was also critical of the large set pieces, calling them "more ponderous than sensational" and that there was "no equivalent of the classic action highlights that can be recalled readily from From Russia with Love or You Only Live Twice or The Spy Who Loved Me or Moonraker. This is a Bond waiting for something inspired to push it over the top."[53] The New York Times critic Vincent Canby said that "For Your Eyes Only is not the best of the series by a long shot" although he did say that the film is "slick entertainment" with a tone that is "consistently comic even when the material is not."[54]
Jack Kroll in Newsweek dismissed the film, saying it was "an anthology of action episodes held together by the thinnest of plot lines", although he did concede that these set pieces are "terrific in their exhilaratingly absurd energy."[55] For Time magazine, Richard Corliss concentrated on the stunts, saying the team "have devised some splendid optional features for For Your Eyes Only" whilst also commenting on Roger Moore, saying that his "mannequin good looks and waxed-fruit insouciance" show him to be "the best-oiled cog in this perpetual motion machine."[56] Jay Scott of The Globe and Mail included it on his list of the year's worst films,[57] calling it "repellant"[58] and "ambitiously bad".[57]
French filmmaker Robert Bresson admired the film: "It filled me with wonder because of its cinematographic writing ... if I could have seen it twice in a row and again the next day, I would have done."[59][60] Elsewhere Bresson said he also loved the film's ski chase.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
Retrospective reviews[]
Opinion on For Your Eyes Only has improved with the passing of time, though some reviews are still mixed to positive: Template:As of, the film holds a 73% 'fresh' rating from Rotten Tomatoes,[61] being ranked eleventh among the 24 Bond films.[62] Ian Nathan of Empire gives the film only two of a possible five stars, observing that the film "still ranks as one of the most forgettable Bonds on record."[63] In 2006, IGN chose For Your Eyes Only as the sixth best Bond film, claiming it is "a good old-fashioned espionage tale",[64] a placement shared by Norman Wilner of MSN, who considered it "the one Moore film that seems to reach back to Connery's heyday",[65] and Entertainment Weekly chose it as the tenth best in 2008, saying it was a "return to low-tech, low-key Bond [with] ... some of the best stunts since the early days".[66] In October 2008 Time Out re-issued a review of For Your Eyes Only and observed that the film is "admirable in intent" but that it "feels a little spare", largely because the plot has been "divested of the bells and whistles that hallmark the franchise".[39]
James Berardinelli wrote that the film was "a solid adventure, although it could have been better",[67] while Danny Peary thought "There are exciting moments, but most of it is standard Bond fare," going on to describe For Your Eyes Only as "an attempt to mix spectacle with [the] tough, believable storylines of early Bond films ... [it] is enjoyable while you're watching it. Afterward, it's one of the most forgettable of the Bond series."Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). Raymond Benson, the author of nine Bond novels, thought For Your Eyes Only was Roger Moore's best Bond film.[68]
Although Chris Nashawaty of Entertainment Weekly ranks Carole Bouquet playing Melina as the "worst babe" of the seven Roger Moore James Bond films,[69] his colleague, Joshua Rich disagreed, putting her tenth in the overall 10 Best Bond Girls listing from the 21 films released up to that point.[70] Entertainment Weekly also ranked Lynn-Holly Johnson as Bibi Dahl as ninth on their list of the 10 worst Bond girls from the 21 films that had been released.[71] After 20 films had been released, IGN ranked Bouquet as fifth in their 'top 10 Bond Babes' list,[72] and The Times thought she was sixth on their list of the Top 10 most fashionable Bond girls after 21 films had been released.[73]
Accolades[]
The title song For Your Eyes Only performed by singer Sheena Easton was nominated for a Best Original Song at the 39th Golden Globe Awards[74] and Best Original Song at the 1981 Academy Awards, losing out at both ceremonies to "Arthur's Theme" from the film Arthur.[75] However, the 1981 Academy Awards ceremony did see the presenting of the Irving G. Thalberg Memorial Award to producer Albert R. Broccoli.[76] The Writers Guild of America nominated the script by Michael G. Wilson and Richard Maibaum for Best Adapted Screenplay – Comedy or Musical Picture.[77] In 2004 the American Film Institute nominated the song "For Your Eyes Only" from the film for AFI's 100 Years...100 Songs.[78]
Adaptations[]
Two-part For Your Eyes Only comic book adaptation by Marvel Comics
- Main article: James Bond comic books
As part of the merchandising of For Your Eyes Only, Marvel Comics published an adaptation of the movie as issue 19 of Marvel Comics Super Special; this was also repackaged as a two-issue comic book adaptation of the film.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value). The first issue was released in October 1981 and was soon followed by the second issue in November of the same year. The adaptation was written by Larry Hama, pencilled by Howard Chaykin, inked by Vincent Colletta, and edited by Dennis O'Neil.Lua error in Module:Footnotes at line 53: attempt to index local 'anchor_id_list' (a nil value).
It was the second film in the series to have a comic book tie-in, following a Dr. No comic in 1962. Marvel Comics would go on to publish an Octopussy comic book adaptation in 1983.
In a 2015 interview with CBC Radio One Lee Demarbre and Ian Driscoll announced their plans to make a Canadian film adaptation of For Your Eyes Only set in Ottawa, which they intend to be the first instalment in a four-film series.[79] The production of a film is possible because Canada enforces the Berne Convention and the Copyright Act automatically releasing an author's entire collected works to the public domain 50 years after their death, causing Ian Fleming's works to enter the public domain in Canada in 2014.[80] Demarbre and Driscoll expressed interest in casting Ryan Reynolds as James Bond; Jessica Paré as Judy Havelock; Donald Sutherland, Christopher Plummer, and Michael Ironside as supporting characters; and the Dead Obies as the performers of the title song. Although a film adaptation will not be releasable in the United States and Europe due to differing copyright law they believed it could recoup its profits through a release in Asia, including China.[81]
See also[]
- Outline of James Bond
References[]
- ↑ For Your Eyes Only. European Audiovisual Observatory.
- ↑ Moses, Antoinette. Sight and Sound; London Vol. 51, Iss. 4, (Fall 1982): 258.
- ↑ Template:Cite video
- ↑ Brady, Shirley. Good Skate. People.
- ↑ 5.0 5.1 5.2 5.3 5.4 5.5 Field, Matthew (2015). Some kind of hero : 007 : the remarkable story of the James Bond films. ISBN 978-0-7509-6421-0.
- ↑ /.
- ↑ (2008) My Word is My Bond: A Memoir. London: Michael O’Mara Publications. ISBN 9780732288716.
- ↑ Hibbin, Sally (1989). The making of Licence to kill. Salem House, page 14. ISBN 978-0-88162-453-3.
- ↑ 9.0 9.1 For Your Eyes Only.
- ↑ 10.0 10.1 Another Interview With: Christopher Wood) @ Universal Exports, The Home of James Bond, 007.
- ↑ Rothman, Lily. "'Moonraker' Was Made Because of 'Star Wars' | James Bond, Declassified: 50 Things You Didn't Know About 007", Time, 25 September 2012. (in en-US)
- ↑ 12.00 12.01 12.02 12.03 12.04 12.05 12.06 12.07 12.08 12.09 12.10 12.11 12.12 12.13 12.14 12.15 Template:Cite video
- ↑ Template:Cite video
- ↑ Duncan, Paul (2012). The James Bond Archives. Taschen, page 305.
- ↑ Boshoff, Alison. "Double agent Bond held hostage in studio wars", 22 October 1997.
- ↑ Template:Cite magazine
- ↑ Roberts, Andrew. "The Bond bunch", 8 November 2006.
- ↑ 18.0 18.1 MI6 :: The Home of James Bond.
- ↑ Solomans, Jason. "My kingdom for a Bond: More than 70 contenders and still no new 007", 17 April 2005.
- ↑ Heritage, Stuart (6 April 2014). For Your Eyes Only recap: just when Bond show his soft side, out come the guns.
- ↑ Timothy Dalton Chat Group.
- ↑ For Your Eyes Only.
- ↑ ROGER MOORE BOWS OUT. (1980, Jul 08). Los Angeles Times
- ↑ Pictures: Broccoli confirms moore as 007 in 'eyes' pic. (1980, Jul 23). Variety, 299, 31.
- ↑ 25.0 25.1 25.2 Template:Cite video
- ↑ 26.0 26.1 Glen, John (August 1981). "The director talks about For Your Eyes Only". American Cinematographer. http://www.ianfleming.org/mkkbb/magazine/81acm34.shtml.
- ↑ Template:Cite video
- ↑ 28.0 28.1 Template:Cite video
- ↑ Template:Cite video
- ↑ Lightman, Herb A. (August 1981). "Behind the scenes of James Bond 007: For Your Eyes Only". American Cinematographer. http://ianfleming.org/mkkbb/magazine/81acm12.shtml.
- ↑ Samuelson, David (August 1981). "For Your Eyes Only and Its Special Effects". American Cinematographer. http://www.ianfleming.org/mkkbb/magazine/81acm34.shtml.
- ↑ Template:Cite episode
- ↑ 33.0 33.1 Template:Cite video
- ↑ 34.0 34.1 Template:Cite video
- ↑ Template:Cite video
- ↑ 36.0 36.1 Template:Cite video
- ↑ Template:Cite video
- ↑ 38.0 38.1 For Your Eyes Only (Original Motion Picture Soundtrack). Rovi Corporation.
- ↑ 39.0 39.1 Lee Davies, Adam. 'For Your Eyes Only' revisited. Time Out.
- ↑ Template:Cite video
- ↑ Kielty, Martin (March 13, 2020). Why James Bond Rejected Blondie's Version of 'For Your Eyes Only' (en).
- ↑ O'Brien, Glenn (January 1986). "Debbie's Back". Spin 1 (9): 49. Template:Citation/identifier. https://books.google.com/books?id=O8JuDPDsl1gC&pg=PA49. Retrieved 23 September 2016.
- ↑ "Premieres", 27 June 1981.
- ↑ 44.0 44.1 44.2 "DATELINE: London, June 26", 26 June 1981.
- ↑ 45.0 45.1 Box Office History for James Bond Movies. The Numbers. Nash Information Services, LLC..
- ↑ All Time Highest Grossing Movies in the United States Since 1977, Inflation Adjusted. The Numbers. Nash Information Services, LLC..
- ↑ 47.0 47.1 Template:Cite magazine
- ↑ "The 10 worst cars driven by James Bond: Citroën 2CV (For Your Eyes Only)", MSN, 10 November 2010.
- ↑ 49.0 49.1 49.2 Malcolm, Derek. "The Bond wagon rolls on", 25 June 1981.
- ↑ 50.0 50.1 French, Philip. "Bergman and Bond", 28 June 1981.
- ↑ Robinson, David. "Bergman's inimitable view of human relationships", 26 June 1981.
- ↑ For Your Eyes Only (1981). From Time Out Film Guide. Time Out.
- ↑ 53.0 53.1 Arnold, Gary. "'Your Eyes Only': Smooth but Lacking", 26 June 1981.
- ↑ Canby, Vincent. "Bond in 'For Your Eyes Only'", 26 June 1981.
- ↑ Kroll, Jack. "Down to 001", 29 June 1981.
- ↑ Template:Cite magazine
- ↑ 57.0 57.1 Scott, Jay. "Something old, but not much new Abel Gance's aged Napoleon one shimmering superstar in a year of gloomy omens", 2 January 1982, p. E.3.
- ↑ Scott, Jay. "Plenty Moore of the same 007 has lost a few steps but is still in the running", 10 June 1983, p. E.1.
- ↑ Bresson in Germany (11 May 2010).The interview may be seen on New Yorker Films' DVD release of Bresson's film L'Argent.
- ↑ Robert Bresson (July 2005). "Chatterbox: Armond White & Robert Bresson". Film Comment 41 (4): 8.
- ↑ For Your Eyes Only. Rotten Tomatoes.
- ↑ In 2008 --[[ ---------------------------------- Lua module implementing the Template loop detected: Template:Webarchive template. A merger of the functionality of three templates: Template:Wayback, Template:Webcite and Template:Cite archives ]] --[[--------------------------< D E P E N D E N C I E S >------------------------------------------------------ ]] require('Module:No globals'); local getArgs = require ('Module:Arguments').getArgs; --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local categories = {}; -- category names local config = {}; -- global configuration settings local digits = {}; -- for i18n; table that translates local-wiki digits to western digits local err_warn_msgs = {}; -- error and warning messages local excepted_pages = {}; local month_num = {}; -- for i18n; table that translates local-wiki month names to western digits local prefixes = {}; -- service provider tail string prefixes local services = {}; -- archive service provider data from local s_text = {}; -- table of static text strings used to build final rendering local uncategorized_namespaces = {}; -- list of namespaces that we should not categorize local uncategorized_subpages = {}; -- list of subpages that should not be categorized --[[--------------------------< P A G E S C O P E I D E N T I F I E R S >---------------------------------- ]] local non_western_digits; -- boolean flag set true when data.digits.enable is true local this_page = mw.title.getCurrentTitle(); local track = {}; -- Associative array to hold tracking categories local ulx = {}; -- Associative array to hold template data --[[--------------------------< S U B S T I T U T E >---------------------------------------------------------- Populates numbered arguments in a message string using an argument table. ]] local function substitute (msg, args) return args and mw.message.newRawMessage (msg, args):plain() or msg; end --[[--------------------------< tableLength >----------------------- Given a 1-D table, return number of elements ]] local function tableLength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end --[=[-------------------------< M A K E _ W I K I L I N K >---------------------------------------------------- Makes a wikilink; when both link and display text is provided, returns a wikilink in the form D; if only link is provided, returns a wikilink in the form L; if neither are provided or link is omitted, returns an empty string. ]=] local function make_wikilink (link, display, no_link) if nil == no_link then if link and ( ~= link) then if display and ( ~= display) then return table.concat ({'', display, ''}); else return table.concat ({'', link, ''}); end end return display or ; -- link not set so return the display text else -- no_link if display and ( ~= display) then -- if there is display text return display; -- return that else return link or ; -- return the target article name or empty string end end end --[[--------------------------< createTracking >----------------------- Return data in track[] ie. tracking categories ]] local function createTracking() if not excepted_pages[this_page.fullText] then -- namespace:title/fragment is allowed to be categorized (typically this module's / template's testcases page(s)) if uncategorized_namespaces[this_page.nsText] then return ; -- this page not to be categorized so return empty string end for _,v in ipairs (uncategorized_subpages) do -- cycle through page name patterns if this_page.text:match (v) then -- test page name against each pattern return ; -- this subpage type not to be categorized so return empty string end end end local out = {}; if tableLength(track) > 0 then for key, _ in pairs(track) do -- loop through table table.insert (out, make_wikilink (key)); -- and convert category names to links end end return table.concat (out); -- concat into one big string; empty string if table is empty end --[[--------------------------< inlineError >----------------------- Critical error. Render output completely in red. Add to tracking category. This function called as the last thing before abandoning this module ]] local function inlineError (msg, args) track[categories.error] = 1 return table.concat ({ 'Error in ', -- open the error message span config.tname, -- insert the local language template name ' template: ', substitute (msg, args), -- insert the formatted error message '.', -- close the span createTracking() -- add the category }) end --[[--------------------------< inlineRed >----------------------- Render a text fragment in red, such as a warning as part of the final output. Add tracking category. ]] local function inlineRed(msg, trackmsg) if trackmsg == "warning" then track[categories.warning] = 1; elseif trackmsg == "error" then track[categories.error] = 1; end return '' .. msg .. '' end --[[--------------------------< base62 >----------------------- Convert base-62 to base-10 Credit: https://de.wikipedia.org/wiki/Modul:Expr ]] local function base62( value ) local r = 1 -- default return value is input value is malformed if value:match ('%W') then -- value must only be in the set [0-9a-zA-Z] return; -- nil return when value contains extraneous characters end local n = #value -- number of characters in value local k = 1 local c r = 0 for i = n, 1, -1 do -- loop through all characters in value from ls digit to ms digit c = value:byte( i, i ) if c >= 48 and c <= 57 then -- character is digit 0-9 c = c - 48 elseif c >= 65 and c <= 90 then -- character is ascii a-z c = c - 55 else -- must be ascii A-Z c = c - 61 end r = r + c * k -- accumulate this base62 character's value k = k * 62 -- bump for next end -- for i return r end --[[--------------------------< D E C O D E _ D A T E >-------------------------------------------------------- Given a date string, return it in iso format along with an indicator of the date's format. Except that month names must be recognizable as legitimate month names with proper capitalization, and that the date string must match one of the recognized date formats, no error checking is done here; return nil else ]] local function decode_date (date_str) local patterns = { ['dmy'] = {'^(%d%d?) +([^%s%d]+) +(%d%d%d%d)$', 'd', 'm', 'y'}, -- %a does not recognize unicode combining characters used by some languages ['mdy'] = {'^([^%s%d]+) (%d%d?), +(%d%d%d%d)$', 'm', 'd', 'y'}, ['ymd'] = {'^(%d%d%d%d) +([^%s%d]+) (%d%d?)$', 'y', 'm', 'd'}, -- not mos compliant at en.wiki but may be acceptible at other wikis }; local t = {}; if non_western_digits then -- this wiki uses non-western digits? date_str = mw.ustring.gsub (date_str, '%d', digits); -- convert this wiki's non-western digits to western digits end if date_str:match ('^%d%d%d%d%-%d%d%-%d%d$') then -- already an iso format date, return western digits form return date_str, 'iso'; end for k, v in pairs (patterns) do local c1, c2, c3 = mw.ustring.match (date_str, patterns[k][1]); -- c1 .. c3 are captured but we don't know what they hold if c1 then -- set on match t = { -- translate unspecified captures to y, m, and d [patterns[k][2]] = c1, -- fill the table of captures with the captures [patterns[k][3]] = c2, -- take index names from src_pattern table and assign sequential captures [patterns[k][4]] = c3, }; if month_num[t.m] then -- when month not already a number t.m = month_num[t.m]; -- replace valid month name with a number else return nil, 'iso'; -- not a valid date form because month not valid end return mw.ustring.format ('%.4d-%.2d-%.2d', t.y, t.m, t.d), k; -- return date in iso format end end return nil, 'iso'; -- date could not be decoded; return nil and default iso date end --[[--------------------------< makeDate >----------------------- Given year, month, day numbers, (zero-padded or not) return a full date in df format where df may be one of: mdy, dmy, iso, ymd on entry, year, month, day are presumed to be correct for the date that they represent; all are required in this module, makeDate() is sometimes given an iso-format date in year: makeDate (2018-09-20, nil, nil, df) this works because table.concat() sees only one table member ]] local function makeDate (year, month, day, df) local format = { ['dmy'] = 'j F Y', ['mdy'] = 'F j, Y', ['ymd'] = 'Y F j', ['iso'] = 'Y-m-d', }; local date = table.concat ({year, month, day}, '-'); -- assemble year-initial numeric-format date (zero padding not required here) if non_western_digits then -- this wiki uses non-western digits? date = mw.ustring.gsub (date, '%d', digits); -- convert this wiki's non-western digits to western digits end return mw.getContentLanguage():formatDate (format[df], date); end --[[--------------------------< I S _ V A L I D _ D A T E >---------------------------------------------------- Returns true if date is after 31 December 1899 (why is 1900 the min year? shouldn't the internet's date-of-birth be min year?), not after today's date, and represents a valid date (29 February 2017 is not a valid date). Applies Gregorian leapyear rules. all arguments are required ]] local function is_valid_date (year, month, day) local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local month_length; local y, m, d; local today = os.date ('*t'); -- fetch a table of current date parts if not year or == year or not month or == month or not day or == day then return false; -- something missing end y = tonumber (year); m = tonumber (month); d = tonumber (day); if 1900 > y or today.year < y or 1 > m or 12 < m then -- year and month are within bounds TODO: 1900? return false; end if (2==m) then -- if February month_length = 28; -- then 28 days unless if (0==(y%4) and (0~=(y%100) or 0==(y%400))) then -- is a leap year? month_length = 29; -- if leap year then 29 days in February end else month_length=days_in_month[m]; end if 1 > d or month_length < d then -- day is within bounds return false; end -- here when date parts represent a valid date return os.time({['year']=y, ['month']=m, ['day']=d, ['hour']=0}) <= os.time(); -- date at midnight must be less than or equal to current date/time end --[[--------------------------< decodeWebciteDate >----------------------- Given a URI-path to Webcite (eg. /67xHmVFWP) return the encoded date in df format returns date string in df format - webcite date is a unix timestamp encoded as bae62 or the string 'query' ]] local function decodeWebciteDate(path, df) local dt = {}; local decode; dt = mw.text.split(path, "/") -- valid URL formats that are not base62 -- http://www.webcitation.org/query?id=1138911916587475 -- http://www.webcitation.org/query?url=http..&date=2012-06-01+21:40:03 -- http://www.webcitation.org/1138911916587475 -- http://www.webcitation.org/cache/73e53dd1f16cf8c5da298418d2a6e452870cf50e -- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e if dt[2]:find ('query', 1, true) or dt[2]:find ('cache', 1, true) or dt[2]:find ('getfile', 1, true) or tonumber(dt[2]) then return 'query'; end decode = base62(dt[2]); -- base62 string -> exponential number if not decode then return nil; -- nil return when dt[2] contains characters not in %w end dt = os.date('*t', string.format("%d", decode):sub(1,10)) -- exponential number -> text -> first 10 characters (a unix timestamp) -> a table of date parts decode = makeDate (dt.year, dt.month, dt.day, 'iso'); -- date comparisons are all done in iso format with western digits if non_western_digits then -- this wiki uses non-western digits? decode = mw.ustring.gsub (decode, '%d', digits); -- convert this wiki's non-western digits to western digits end return decode; end --[[--------------------------< decodeWaybackDate >----------------------- Given a URI-path to Wayback (eg. /web/20160901010101/http://example.com ) or Library of Congress Web Archives (eg. /all/20160901010101/http://example.com) or UK Government Web Archive (eg. /ukgwa/20160901010101/http://example.com or /tna/20160901010101/http://example.com) return the formatted date eg. "September 1, 2016" in df format Handle non-digits in snapshot ID such as "re_" and "-" and "*" returns two values: first value is one of these: valid date string in df format - wayback date is valid (including the text string 'index' when date is '/*/') empty string - wayback date is malformed (less than 8 digits, not a valid date) nil - wayback date is '/save/' or otherwise not a number second return value is an appropriate 'message' may or may not be formatted ]] local function decodeWaybackDate(path, df) local msg, snapdate; snapdate = path:gsub ('^/web/', ):gsub ('^/all/', ):gsub ('^/ukgwa/', ):gsub ('^/tna/', ):gsub ('^/', ); -- remove leading /web/, /all/, /ukgwa/, /tna/, or / snapdate = snapdate:match ('^[^/]+'); -- get timestamp if snapdate == "*" then -- eg. /web/*/http.., etc. return 'index'; -- return indicator that this url has an index date end snapdate = snapdate:gsub ('%a%a_%d?$', ):gsub ('%-', ); -- from date, remove any trailing "re_", dashes msg = ; if snapdate:match ('%*$') then -- a trailing '*' causes calendar display at archive .org snapdate = snapdate:gsub ('%*$', ); -- remove so not part of length calc later msg = inlineRed (err_warn_msgs.ts_cal, 'warning'); -- make a message end if not tonumber(snapdate) then return nil, 'ts_nan'; -- return nil (fatal error flag) and message selector end local dlen = snapdate:len(); if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits? return , inlineRed (err_warn_msgs.ts_short, 'error'); -- return empty string and error message end local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here if not is_valid_date (year, month, day) then return , inlineRed (err_warn_msgs.ts_date, 'error'); -- return empty string and error message end snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format if 14 == dlen then return snapdate, msg; -- return date with message if any else return snapdate, msg .. inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message(s) end end --[[--------------------------< decodeArchiveisDate >----------------------- Given an Archive.is "long link" URI-path (e.g. /2016.08.28-144552/http://example.com) return the date in df format (e.g. if df = dmy, return 28 August 2016) Handles "." and "-" in snapshot date, so 2016.08.28-144552 is same as 20160828144552 returns two values: first value is one of these: valid date string in df format - archive.is date is valid (including the text string 'short link' when url is the short form) empty string - wayback date is malformed (not a number, less than 8 digits, not a valid date) nil - wayback date is '/save/' second return value is an appropriate 'message' may or may not be formatted ]] local function decodeArchiveisDate(path, df) local snapdate if path:match ('^/%w+$') then -- short form url path is '/' followed by some number of base 62 digits and nothing else return "short link" -- e.g. http://archive.is/hD1qz end snapdate = mw.text.split (path, '/')[2]:gsub('[%.%-]', ); -- get snapshot date, e.g. 2016.08.28-144552; remove periods and hyphens local dlen = string.len(snapdate) if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits? return , inlineRed (err_warn_msgs.ts_short, 'error'); -- return empty string and error message end local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here if not is_valid_date (year, month, day) then return , inlineRed (err_warn_msgs.ts_date, 'error'); -- return empty string and error message end snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format if 14 == dlen then return snapdate; -- return date else return snapdate, inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message end end --[[--------------------------< serviceName >----------------------- Given a domain extracted by mw.uri.new() (eg. web.archive.org) set tail string and service ID ]] local function serviceName(host, no_link) local tracking; local index; host = host:lower():gsub ('^web%.(.+)', '%1'):gsub ('^www%.(.+)', '%1'); -- lowercase, remove web. and www. subdomains if services[host] then index = host; else for k, _ in pairs (services) do if host:find ('%f[%a]'..k:gsub ('([%.%-])', '%%%1')) then index = k; break; end end end if index then local out = {}; -- empty string in [1] so that concatenated result has leading single space ulx.url1.service = services[index][4] or 'other'; tracking = services[index][5] or categories.other; -- build tail string if false == services[index][1] then -- select prefix table.insert (out, prefixes.at); elseif true == services[index][1] then table.insert (out, prefixes.atthe); else table.insert (out, services[index][1]); end table.insert (out, make_wikilink (services[index][2], services[index][3], no_link)); -- add article wikilink if services[index][6] then -- add tail postfix if it exists table.insert (out, services[index][6]); end ulx.url1.tail = table.concat (out, ' '); -- put it all together; result has leading space character else -- here when unknown archive ulx.url1.service = 'other'; tracking = categories.unknown; ulx.url1.tail = table.concat ({, prefixes.at, host, inlineRed (err_warn_msgs.unknown_url, error)}, ' '); end track[tracking] = 1 end --[[--------------------------< parseExtraArgs >----------------------- Parse numbered arguments starting at 2, such as url2..url10, date2..date10, title2..title10 For example: Template loop detected: Template:Webarchive Three url arguments not in numeric sequence (1..4..7). Function only processes arguments numbered 2 or greater (in this case 4 and 7) It creates numeric sequenced table entries like: urlx.url2.url = <argument value for url4> urlx.url3.url = <argument value for url7> Returns the number of URL arguments found numbered 2 or greater (in this case returns "2") ]] local function parseExtraArgs(args) local i, j, argurl, argurl2, argdate, argtitle j = 2 for i = 2, config.maxurls do argurl = "url" .. i if args[argurl] then argurl2 = "url" .. j ulx[argurl2] = {} ulx[argurl2]["url"] = args[argurl] argdate = "date" .. i if args[argdate] then ulx[argurl2]["date"] = args[argdate] else ulx[argurl2]["date"] = inlineRed (err_warn_msgs.date_miss, 'warning'); end argtitle = "title" .. i if args[argtitle] then ulx[argurl2]["title"] = args[argtitle] else ulx[argurl2]["title"] = nil end j = j + 1 end end if j == 2 then return 0 else return j - 2 end end --[[--------------------------< comma >----------------------- Given a date string, return "," if it's MDY ]] local function comma(date) return (date and date:match ('%a+ +%d%d?(,) +%d%d%d%d')) or ; end --[[--------------------------< createRendering >----------------------- Return a rendering of the data in ulx[][] ]] local function createRendering() local displayfield local out = {}; local index_date, msg = ulx.url1.date:match ('(index)(.*)'); -- when ulx.url1.date extract 'index' text and message text (if there is a message) ulx.url1.date = ulx.url1.date:gsub ('index.*', 'index'); -- remove message if 'none' == ulx.url1.format then -- For Template:Wayback, Template:Webcite table.insert (out, '['); -- open extlink markup table.insert (out, ulx.url1.url); -- add url if ulx.url1.title then table.insert (out, ' ') -- the required space table.insert (out, ulx.url1.title) -- the title table.insert (out, ']'); -- close extlink markup table.insert (out, ulx.url1.tail); -- tail text if ulx.url1.date then table.insert (out, ' ('); -- open date text; TODO: why the html entity? replace with regular space? table.insert (out, 'index' == ulx.url1.date and s_text.archive or s_text.archived); -- add text table.insert (out, ' '); -- insert a space table.insert (out, ulx.url1.date); -- add date table.insert (out, ')'); -- close date text end else -- no title if index_date then -- when url date is 'index' table.insert (out, table.concat ({' ', s_text.Archive_index, ']'})); -- add the index link label table.insert (out, msg or ); -- add date mismatch message when url date is /*/ and |date= has valid date else table.insert (out, table.concat ({' ', s_text.Archived, '] '})); -- add link label for url has timestamp date (will include mismatch message if there is one) end if ulx.url1.date then if 'index' ~= ulx.url1.date then table.insert (out, ulx.url1.date); -- add date when data is not 'index' end table.insert (out, comma(ulx.url1.date)); -- add ',' if date format is mdy table.insert (out, ulx.url1.tail); -- add tail text else -- no date table.insert (out, ulx.url1.tail); -- add tail text end end if 0 < ulx.url1.extraurls then -- For multiple archive URLs local tot = ulx.url1.extraurls + 1 table.insert (out, '.') -- terminate first url table.insert (out, table.concat ({' ', s_text.addlarchives, ': '})); -- add header text for i=2, tot do -- loop through the additionals local index = table.concat ({'url', i}); -- make an index displayfield = ulx[index]['title'] and 'title' or 'date'; -- choose display text table.insert (out, '['); -- open extlink markup table.insert (out, ulx[index]['url']); -- add the url table.insert (out, ' '); -- the required space table.insert (out, ulx[index][displayfield]); -- add the label table.insert (out, ']'); -- close extlink markup table.insert (out, i==tot and '.' or ', '); -- add terminator end end return table.concat (out); -- make a big string and done else -- For Template:Cite archives if 'addlarchives' == ulx.url1.format then -- Multiple archive services table.insert (out, table.concat ({s_text.addlarchives, ': '})); -- add header text else -- Multiple pages from the same archive table.insert (out, table.concat ({s_text.addlpages, ' '})); -- add header text table.insert (out, ulx.url1.date); -- add date to header text table.insert (out, ': '); -- close header text end local tot = ulx.url1.extraurls + 1; for i=1, tot do -- loop through the additionals local index = table.concat ({'url', i}); -- make an index table.insert (out, '['); -- open extlink markup table.insert (out, ulx[index]['url']); -- add url table.insert (out, ' '); -- add required space displayfield = ulx[index]['title']; if 'addlarchives' == ulx.url1.format then if not displayfield then displayfield = ulx[index]['date'] end else -- must be addlpages if not displayfield then displayfield = table.concat ({s_text.Page, ' ', i}); end end table.insert (out, displayfield); -- add title, date, page label text table.insert (out, ']'); -- close extlink markup table.insert (out, (i==tot and '.' or ', ')); -- add terminator end return table.concat (out); -- make a big string and done end end --[[--------------------------< P A R A M E T E R _ N A M E _ X L A T E >-------------------------------------- for internaltionalization, translate local-language parameter names to their English equivalents TODO: return error message if multiple aliases of the same canonical parameter name are found? returns two tables: new_args - holds canonical form parameters and their values either from translation or because the parameter was already in canonical form origin - maps canonical-form parameter names to their untranslated (local language) form for error messaging in the local language unrecognized parameters are ignored ]] local function parameter_name_xlate (args, params, enum_params) local name; -- holds modifiable name of the parameter name during evaluation local enum; -- for enumerated parameters, holds the enumerator during evaluation local found = false; -- flag used to break out of nested for loops local new_args = {}; -- a table that holds canonical and translated parameter k/v pairs local origin = {}; -- a table that maps original (local language) parameter names to their canonical name for local language error messaging local unnamed_params; -- set true when unsupported positional parameters are detected for k, v in pairs (args) do -- loop through all of the arguments in the args table name = k; -- copy of original parameter name if 'string' == type (k) then if non_western_digits then -- true when non-western digits supported at this wiki name = mw.ustring.gsub (name, '%d', digits); -- convert this wiki's non-western digits to western digits end enum = name:match ('%d+$'); -- get parameter enumerator if it exists; nil else if not enum then -- no enumerator so looking for non-enumnerated parameters -- TODO: insert shortcut here? if params[name] then name holds the canonical parameter name; no need to search further for pname, aliases in pairs (params) do -- loop through each parameter the params table for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table if name == alias then new_args[pname] = v; -- create a new entry in the new_args table origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name found = true; -- flag so that we can break out of these nested for loops break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair end end if found then -- true when we found an alias that matched name found = false; -- reset the flag break; -- go do next args k/v pair end end else -- enumerated parameters name = name:gsub ('%d$', '#'); -- replace enumeration digits with place holder for table search -- TODO: insert shortcut here? if num_params[name] then name holds the canonical parameter name; no need to search further for pname, aliases in pairs (enum_params) do -- loop through each parameter the num_params table for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table if name == alias then pname = pname:gsub ('#$', enum); -- replace the '#' place holder with the actual enumerator new_args[pname] = v; -- create a new entry in the new_args table origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name found = true; -- flag so that we can break out of these nested for loops break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair end end if found then -- true when we found an alias that matched name found = false; -- reset the flag break; -- go do next args k/v pair end end end else unnamed_params = true; -- flag for unsupported positional parameters end end -- for k, v return new_args, origin, unnamed_params; end --[[--------------------------< W E B A R C H I V E >---------------------------------------------------------- template entry point ]] local function webarchive(frame) local args = getArgs (frame); local data = mw.loadData (table.concat ({ -- make a data module name; sandbox or live 'Module:Webarchive/data', frame:getTitle():find('sandbox', 1, true) and '/sandbox' or -- this instance is ./sandbox then append /sandbox })); categories = data.categories; -- fill in the forward declarations config = data.config; if data.digits.enable then digits = data.digits; -- for i18n; table of digits in the local wiki's language non_western_digits = true; -- use_non_western_digits end err_warn_msgs = data.err_warn_msgs; excepted_pages = data.excepted_pages; month_num = data.month_num; -- for i18n; table of month names in the local wiki's language prefixes = data.prefixes; services = data.services; s_text = data.s_text; uncategorized_namespaces = data.uncategorized_namespaces; uncategorized_subpages = data.uncategorized_subpages; local origin = {}; -- holds a map of English to local language parameter names used in the current template; not currently used local unnamed_params; -- boolean set to true when template call has unnamed parameters args, origin, unnamed_params = parameter_name_xlate (args, data.params, data.enum_params); -- translate parameter names in args to English local date, format, msg, udate, uri, url; local ldf = 'iso'; -- when there is no |date= parameter, render url dates in iso format if args.url and args.url1 then -- URL argument (first) return inlineError (data.crit_err_msgs.conflicting, {origin.url, origin.url1}); end url = args.url or args.url1; if not url then return inlineError (data.crit_err_msgs.empty); end -- these iabot bugs perportedly fixed; removing these causes lua script error --[[ -- at Template:Webarchive/testcases/Production; resolve that before deleting these tests if mw.ustring.find( url, "https://web.http", 1, true ) then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred track[categories.error] = 1; return inlineError (data.crit_err_msgs.iabot1); end if url == "https://web.archive.org/http:/" then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred track[categories.error] = 1; return inlineError (data.crit_err_msgs.iabot2); end ]] if not (url:lower():find ('^http') or url:find ('^//')) then return inlineError (data.crit_err_msgs.invalid_url ); end ulx.url1 = {} ulx.url1.url = url ulx.url1.extraurls = parseExtraArgs(args) local good = false; good, uri = pcall (mw.uri.new, ulx.url1.url); -- get a table of uri parts from this url; protected mode to prevent lua error when ulx.url1.url is malformed if not good or nil == uri.host then -- abandon when ulx.url1.url is malformed return inlineError (data.crit_err_msgs.invalid_url); end serviceName(uri.host, args.nolink) if args.date and args.date1 then -- Date argument return inlineError (data.crit_err_msgs.conflicting, {origin.date, origin.date1}); end date = args.date or args.date1; date = date and date:gsub (' +', ' '); -- replace multiple spaces with a single space if date and config.verifydates then if '*' == date then date = 'index'; ldf = 'iso'; -- set to default format elseif 'mdy' == date then date = nil; -- if date extracted from URL, ldf = 'mdy'; -- then |date=mdy overrides iso elseif 'dmy' == date then date = nil; -- if date extracted from URL, ldf = 'dmy'; -- then |date=dmy overrides iso elseif 'ymd' == date then date = nil; -- if date extracted from URL, ldf = 'ymd'; -- then |date=ymd overrides iso else date, ldf = decode_date (date); -- get an iso format date from date and get date's original format end end if 'wayback' == ulx.url1.service or 'locwebarchives' == ulx.url1.service or 'ukgwa' == ulx.url1.service then if date then if config.verifydates then if ldf then udate, msg = decodeWaybackDate (uri.path); -- get the url date in iso format and format of date in |date=; 'index' when wayback url date is * if not udate then -- this is the only 'fatal' error return return inlineError (data.crit_err_msgs[msg]); end if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), -- add warning message msg, -- add message if there is one }); end end end else -- no |date= udate, msg = decodeWaybackDate (uri.path); if not udate then -- this is the only 'fatal' error return return inlineError (data.crit_err_msgs[msg]); end if == udate then date = nil; -- unset else date = udate; end end elseif 'webcite' == ulx.url1.service then if date then if config.verifydates then if ldf then udate = decodeWebciteDate (uri.path); -- get the url date in iso format if 'query' ~= udate then -- skip if query if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), }); end end end end else date = decodeWebciteDate( uri.path, "iso" ) if date == "query" then date = nil; -- unset msg = inlineRed (err_warn_msgs.date_miss, 'warning'); elseif not date then -- invalid base62 string date = inlineRed (err_warn_msgs.date1, 'error'); end end elseif 'archiveis' == ulx.url1.service then if date then if config.verifydates then if ldf then udate, msg = decodeArchiveisDate (uri.path) -- get the url date in iso format if 'short link' ~= udate then -- skip if short link if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), -- add warning message msg, -- add message if there is one }); end end end end else -- no |date= udate, msg = decodeArchiveisDate( uri.path, "iso" ) if udate == "short link" then date = nil; -- unset msg = inlineRed (err_warn_msgs.date_miss, 'warning'); elseif == udate then date = nil; -- unset else date = udate; end end else -- some other service if not date then msg = inlineRed (err_warn_msgs.date_miss, 'warning'); end end if 'index' == date then ulx.url1.date = date .. (msg or ); -- create index + message (if there is one) elseif date then ulx.url1.date = makeDate (date, nil, nil, ldf) .. (msg or ); -- create a date in the wiki's local language + message (if there is one) else ulx.url1.date = msg; end format = args.format; -- Format argument if not format then format = "none" else for k, v in pairs (data.format_vals) do -- |format= accepts two specific values loop through a table of those values local found; -- declare a nil flag for _, p in ipairs (v) do -- loop through local language variants if format == p then -- when |format= value matches format = k; -- use name from table key found = true; -- declare found so that we can break out of outer for loop break; -- break out of inner for loop end end if found then break; end end if format == "addlpages" then if not ulx.url1.date then format = "none" end elseif format == "addlarchives" then format = "addlarchives" else format = "none" end end ulx.url1.format = format if args.title and args.title1 then -- Title argument return inlineError (data.crit_err_msgs.conflicting, {origin.title, origin.title1}); end ulx.url1.title = args.title or args.title1; local rend = createRendering() if not rend then return inlineError (data.crit_err_msgs.unknown); end return rend .. ((unnamed_params and inlineRed (err_warn_msgs.unnamed_params, 'warning')) or ) .. createTracking(); end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return {webarchive = webarchive};, the film was ranked ninth with 73%; ever since the list was posted, For Your Eyes Only's score fell to 69%, falling behind the ones that stood from 12th to 10th --[[ ---------------------------------- Lua module implementing the Template loop detected: Template:Webarchive template. A merger of the functionality of three templates: Template:Wayback, Template:Webcite and Template:Cite archives ]] --[[--------------------------< D E P E N D E N C I E S >------------------------------------------------------ ]] require('Module:No globals'); local getArgs = require ('Module:Arguments').getArgs; --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local categories = {}; -- category names local config = {}; -- global configuration settings local digits = {}; -- for i18n; table that translates local-wiki digits to western digits local err_warn_msgs = {}; -- error and warning messages local excepted_pages = {}; local month_num = {}; -- for i18n; table that translates local-wiki month names to western digits local prefixes = {}; -- service provider tail string prefixes local services = {}; -- archive service provider data from local s_text = {}; -- table of static text strings used to build final rendering local uncategorized_namespaces = {}; -- list of namespaces that we should not categorize local uncategorized_subpages = {}; -- list of subpages that should not be categorized --[[--------------------------< P A G E S C O P E I D E N T I F I E R S >---------------------------------- ]] local non_western_digits; -- boolean flag set true when data.digits.enable is true local this_page = mw.title.getCurrentTitle(); local track = {}; -- Associative array to hold tracking categories local ulx = {}; -- Associative array to hold template data --[[--------------------------< S U B S T I T U T E >---------------------------------------------------------- Populates numbered arguments in a message string using an argument table. ]] local function substitute (msg, args) return args and mw.message.newRawMessage (msg, args):plain() or msg; end --[[--------------------------< tableLength >----------------------- Given a 1-D table, return number of elements ]] local function tableLength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end --[=[-------------------------< M A K E _ W I K I L I N K >---------------------------------------------------- Makes a wikilink; when both link and display text is provided, returns a wikilink in the form D; if only link is provided, returns a wikilink in the form L; if neither are provided or link is omitted, returns an empty string. ]=] local function make_wikilink (link, display, no_link) if nil == no_link then if link and ( ~= link) then if display and ( ~= display) then return table.concat ({'', display, ''}); else return table.concat ({'', link, ''}); end end return display or ; -- link not set so return the display text else -- no_link if display and ( ~= display) then -- if there is display text return display; -- return that else return link or ; -- return the target article name or empty string end end end --[[--------------------------< createTracking >----------------------- Return data in track[] ie. tracking categories ]] local function createTracking() if not excepted_pages[this_page.fullText] then -- namespace:title/fragment is allowed to be categorized (typically this module's / template's testcases page(s)) if uncategorized_namespaces[this_page.nsText] then return ; -- this page not to be categorized so return empty string end for _,v in ipairs (uncategorized_subpages) do -- cycle through page name patterns if this_page.text:match (v) then -- test page name against each pattern return ; -- this subpage type not to be categorized so return empty string end end end local out = {}; if tableLength(track) > 0 then for key, _ in pairs(track) do -- loop through table table.insert (out, make_wikilink (key)); -- and convert category names to links end end return table.concat (out); -- concat into one big string; empty string if table is empty end --[[--------------------------< inlineError >----------------------- Critical error. Render output completely in red. Add to tracking category. This function called as the last thing before abandoning this module ]] local function inlineError (msg, args) track[categories.error] = 1 return table.concat ({ 'Error in ', -- open the error message span config.tname, -- insert the local language template name ' template: ', substitute (msg, args), -- insert the formatted error message '.', -- close the span createTracking() -- add the category }) end --[[--------------------------< inlineRed >----------------------- Render a text fragment in red, such as a warning as part of the final output. Add tracking category. ]] local function inlineRed(msg, trackmsg) if trackmsg == "warning" then track[categories.warning] = 1; elseif trackmsg == "error" then track[categories.error] = 1; end return '' .. msg .. '' end --[[--------------------------< base62 >----------------------- Convert base-62 to base-10 Credit: https://de.wikipedia.org/wiki/Modul:Expr ]] local function base62( value ) local r = 1 -- default return value is input value is malformed if value:match ('%W') then -- value must only be in the set [0-9a-zA-Z] return; -- nil return when value contains extraneous characters end local n = #value -- number of characters in value local k = 1 local c r = 0 for i = n, 1, -1 do -- loop through all characters in value from ls digit to ms digit c = value:byte( i, i ) if c >= 48 and c <= 57 then -- character is digit 0-9 c = c - 48 elseif c >= 65 and c <= 90 then -- character is ascii a-z c = c - 55 else -- must be ascii A-Z c = c - 61 end r = r + c * k -- accumulate this base62 character's value k = k * 62 -- bump for next end -- for i return r end --[[--------------------------< D E C O D E _ D A T E >-------------------------------------------------------- Given a date string, return it in iso format along with an indicator of the date's format. Except that month names must be recognizable as legitimate month names with proper capitalization, and that the date string must match one of the recognized date formats, no error checking is done here; return nil else ]] local function decode_date (date_str) local patterns = { ['dmy'] = {'^(%d%d?) +([^%s%d]+) +(%d%d%d%d)$', 'd', 'm', 'y'}, -- %a does not recognize unicode combining characters used by some languages ['mdy'] = {'^([^%s%d]+) (%d%d?), +(%d%d%d%d)$', 'm', 'd', 'y'}, ['ymd'] = {'^(%d%d%d%d) +([^%s%d]+) (%d%d?)$', 'y', 'm', 'd'}, -- not mos compliant at en.wiki but may be acceptible at other wikis }; local t = {}; if non_western_digits then -- this wiki uses non-western digits? date_str = mw.ustring.gsub (date_str, '%d', digits); -- convert this wiki's non-western digits to western digits end if date_str:match ('^%d%d%d%d%-%d%d%-%d%d$') then -- already an iso format date, return western digits form return date_str, 'iso'; end for k, v in pairs (patterns) do local c1, c2, c3 = mw.ustring.match (date_str, patterns[k][1]); -- c1 .. c3 are captured but we don't know what they hold if c1 then -- set on match t = { -- translate unspecified captures to y, m, and d [patterns[k][2]] = c1, -- fill the table of captures with the captures [patterns[k][3]] = c2, -- take index names from src_pattern table and assign sequential captures [patterns[k][4]] = c3, }; if month_num[t.m] then -- when month not already a number t.m = month_num[t.m]; -- replace valid month name with a number else return nil, 'iso'; -- not a valid date form because month not valid end return mw.ustring.format ('%.4d-%.2d-%.2d', t.y, t.m, t.d), k; -- return date in iso format end end return nil, 'iso'; -- date could not be decoded; return nil and default iso date end --[[--------------------------< makeDate >----------------------- Given year, month, day numbers, (zero-padded or not) return a full date in df format where df may be one of: mdy, dmy, iso, ymd on entry, year, month, day are presumed to be correct for the date that they represent; all are required in this module, makeDate() is sometimes given an iso-format date in year: makeDate (2018-09-20, nil, nil, df) this works because table.concat() sees only one table member ]] local function makeDate (year, month, day, df) local format = { ['dmy'] = 'j F Y', ['mdy'] = 'F j, Y', ['ymd'] = 'Y F j', ['iso'] = 'Y-m-d', }; local date = table.concat ({year, month, day}, '-'); -- assemble year-initial numeric-format date (zero padding not required here) if non_western_digits then -- this wiki uses non-western digits? date = mw.ustring.gsub (date, '%d', digits); -- convert this wiki's non-western digits to western digits end return mw.getContentLanguage():formatDate (format[df], date); end --[[--------------------------< I S _ V A L I D _ D A T E >---------------------------------------------------- Returns true if date is after 31 December 1899 (why is 1900 the min year? shouldn't the internet's date-of-birth be min year?), not after today's date, and represents a valid date (29 February 2017 is not a valid date). Applies Gregorian leapyear rules. all arguments are required ]] local function is_valid_date (year, month, day) local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local month_length; local y, m, d; local today = os.date ('*t'); -- fetch a table of current date parts if not year or == year or not month or == month or not day or == day then return false; -- something missing end y = tonumber (year); m = tonumber (month); d = tonumber (day); if 1900 > y or today.year < y or 1 > m or 12 < m then -- year and month are within bounds TODO: 1900? return false; end if (2==m) then -- if February month_length = 28; -- then 28 days unless if (0==(y%4) and (0~=(y%100) or 0==(y%400))) then -- is a leap year? month_length = 29; -- if leap year then 29 days in February end else month_length=days_in_month[m]; end if 1 > d or month_length < d then -- day is within bounds return false; end -- here when date parts represent a valid date return os.time({['year']=y, ['month']=m, ['day']=d, ['hour']=0}) <= os.time(); -- date at midnight must be less than or equal to current date/time end --[[--------------------------< decodeWebciteDate >----------------------- Given a URI-path to Webcite (eg. /67xHmVFWP) return the encoded date in df format returns date string in df format - webcite date is a unix timestamp encoded as bae62 or the string 'query' ]] local function decodeWebciteDate(path, df) local dt = {}; local decode; dt = mw.text.split(path, "/") -- valid URL formats that are not base62 -- http://www.webcitation.org/query?id=1138911916587475 -- http://www.webcitation.org/query?url=http..&date=2012-06-01+21:40:03 -- http://www.webcitation.org/1138911916587475 -- http://www.webcitation.org/cache/73e53dd1f16cf8c5da298418d2a6e452870cf50e -- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e if dt[2]:find ('query', 1, true) or dt[2]:find ('cache', 1, true) or dt[2]:find ('getfile', 1, true) or tonumber(dt[2]) then return 'query'; end decode = base62(dt[2]); -- base62 string -> exponential number if not decode then return nil; -- nil return when dt[2] contains characters not in %w end dt = os.date('*t', string.format("%d", decode):sub(1,10)) -- exponential number -> text -> first 10 characters (a unix timestamp) -> a table of date parts decode = makeDate (dt.year, dt.month, dt.day, 'iso'); -- date comparisons are all done in iso format with western digits if non_western_digits then -- this wiki uses non-western digits? decode = mw.ustring.gsub (decode, '%d', digits); -- convert this wiki's non-western digits to western digits end return decode; end --[[--------------------------< decodeWaybackDate >----------------------- Given a URI-path to Wayback (eg. /web/20160901010101/http://example.com ) or Library of Congress Web Archives (eg. /all/20160901010101/http://example.com) or UK Government Web Archive (eg. /ukgwa/20160901010101/http://example.com or /tna/20160901010101/http://example.com) return the formatted date eg. "September 1, 2016" in df format Handle non-digits in snapshot ID such as "re_" and "-" and "*" returns two values: first value is one of these: valid date string in df format - wayback date is valid (including the text string 'index' when date is '/*/') empty string - wayback date is malformed (less than 8 digits, not a valid date) nil - wayback date is '/save/' or otherwise not a number second return value is an appropriate 'message' may or may not be formatted ]] local function decodeWaybackDate(path, df) local msg, snapdate; snapdate = path:gsub ('^/web/', ):gsub ('^/all/', ):gsub ('^/ukgwa/', ):gsub ('^/tna/', ):gsub ('^/', ); -- remove leading /web/, /all/, /ukgwa/, /tna/, or / snapdate = snapdate:match ('^[^/]+'); -- get timestamp if snapdate == "*" then -- eg. /web/*/http.., etc. return 'index'; -- return indicator that this url has an index date end snapdate = snapdate:gsub ('%a%a_%d?$', ):gsub ('%-', ); -- from date, remove any trailing "re_", dashes msg = ; if snapdate:match ('%*$') then -- a trailing '*' causes calendar display at archive .org snapdate = snapdate:gsub ('%*$', ); -- remove so not part of length calc later msg = inlineRed (err_warn_msgs.ts_cal, 'warning'); -- make a message end if not tonumber(snapdate) then return nil, 'ts_nan'; -- return nil (fatal error flag) and message selector end local dlen = snapdate:len(); if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits? return , inlineRed (err_warn_msgs.ts_short, 'error'); -- return empty string and error message end local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here if not is_valid_date (year, month, day) then return , inlineRed (err_warn_msgs.ts_date, 'error'); -- return empty string and error message end snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format if 14 == dlen then return snapdate, msg; -- return date with message if any else return snapdate, msg .. inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message(s) end end --[[--------------------------< decodeArchiveisDate >----------------------- Given an Archive.is "long link" URI-path (e.g. /2016.08.28-144552/http://example.com) return the date in df format (e.g. if df = dmy, return 28 August 2016) Handles "." and "-" in snapshot date, so 2016.08.28-144552 is same as 20160828144552 returns two values: first value is one of these: valid date string in df format - archive.is date is valid (including the text string 'short link' when url is the short form) empty string - wayback date is malformed (not a number, less than 8 digits, not a valid date) nil - wayback date is '/save/' second return value is an appropriate 'message' may or may not be formatted ]] local function decodeArchiveisDate(path, df) local snapdate if path:match ('^/%w+$') then -- short form url path is '/' followed by some number of base 62 digits and nothing else return "short link" -- e.g. http://archive.is/hD1qz end snapdate = mw.text.split (path, '/')[2]:gsub('[%.%-]', ); -- get snapshot date, e.g. 2016.08.28-144552; remove periods and hyphens local dlen = string.len(snapdate) if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits? return , inlineRed (err_warn_msgs.ts_short, 'error'); -- return empty string and error message end local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here if not is_valid_date (year, month, day) then return , inlineRed (err_warn_msgs.ts_date, 'error'); -- return empty string and error message end snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format if 14 == dlen then return snapdate; -- return date else return snapdate, inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message end end --[[--------------------------< serviceName >----------------------- Given a domain extracted by mw.uri.new() (eg. web.archive.org) set tail string and service ID ]] local function serviceName(host, no_link) local tracking; local index; host = host:lower():gsub ('^web%.(.+)', '%1'):gsub ('^www%.(.+)', '%1'); -- lowercase, remove web. and www. subdomains if services[host] then index = host; else for k, _ in pairs (services) do if host:find ('%f[%a]'..k:gsub ('([%.%-])', '%%%1')) then index = k; break; end end end if index then local out = {}; -- empty string in [1] so that concatenated result has leading single space ulx.url1.service = services[index][4] or 'other'; tracking = services[index][5] or categories.other; -- build tail string if false == services[index][1] then -- select prefix table.insert (out, prefixes.at); elseif true == services[index][1] then table.insert (out, prefixes.atthe); else table.insert (out, services[index][1]); end table.insert (out, make_wikilink (services[index][2], services[index][3], no_link)); -- add article wikilink if services[index][6] then -- add tail postfix if it exists table.insert (out, services[index][6]); end ulx.url1.tail = table.concat (out, ' '); -- put it all together; result has leading space character else -- here when unknown archive ulx.url1.service = 'other'; tracking = categories.unknown; ulx.url1.tail = table.concat ({, prefixes.at, host, inlineRed (err_warn_msgs.unknown_url, error)}, ' '); end track[tracking] = 1 end --[[--------------------------< parseExtraArgs >----------------------- Parse numbered arguments starting at 2, such as url2..url10, date2..date10, title2..title10 For example: Template loop detected: Template:Webarchive Three url arguments not in numeric sequence (1..4..7). Function only processes arguments numbered 2 or greater (in this case 4 and 7) It creates numeric sequenced table entries like: urlx.url2.url = <argument value for url4> urlx.url3.url = <argument value for url7> Returns the number of URL arguments found numbered 2 or greater (in this case returns "2") ]] local function parseExtraArgs(args) local i, j, argurl, argurl2, argdate, argtitle j = 2 for i = 2, config.maxurls do argurl = "url" .. i if args[argurl] then argurl2 = "url" .. j ulx[argurl2] = {} ulx[argurl2]["url"] = args[argurl] argdate = "date" .. i if args[argdate] then ulx[argurl2]["date"] = args[argdate] else ulx[argurl2]["date"] = inlineRed (err_warn_msgs.date_miss, 'warning'); end argtitle = "title" .. i if args[argtitle] then ulx[argurl2]["title"] = args[argtitle] else ulx[argurl2]["title"] = nil end j = j + 1 end end if j == 2 then return 0 else return j - 2 end end --[[--------------------------< comma >----------------------- Given a date string, return "," if it's MDY ]] local function comma(date) return (date and date:match ('%a+ +%d%d?(,) +%d%d%d%d')) or ; end --[[--------------------------< createRendering >----------------------- Return a rendering of the data in ulx[][] ]] local function createRendering() local displayfield local out = {}; local index_date, msg = ulx.url1.date:match ('(index)(.*)'); -- when ulx.url1.date extract 'index' text and message text (if there is a message) ulx.url1.date = ulx.url1.date:gsub ('index.*', 'index'); -- remove message if 'none' == ulx.url1.format then -- For Template:Wayback, Template:Webcite table.insert (out, '['); -- open extlink markup table.insert (out, ulx.url1.url); -- add url if ulx.url1.title then table.insert (out, ' ') -- the required space table.insert (out, ulx.url1.title) -- the title table.insert (out, ']'); -- close extlink markup table.insert (out, ulx.url1.tail); -- tail text if ulx.url1.date then table.insert (out, ' ('); -- open date text; TODO: why the html entity? replace with regular space? table.insert (out, 'index' == ulx.url1.date and s_text.archive or s_text.archived); -- add text table.insert (out, ' '); -- insert a space table.insert (out, ulx.url1.date); -- add date table.insert (out, ')'); -- close date text end else -- no title if index_date then -- when url date is 'index' table.insert (out, table.concat ({' ', s_text.Archive_index, ']'})); -- add the index link label table.insert (out, msg or ); -- add date mismatch message when url date is /*/ and |date= has valid date else table.insert (out, table.concat ({' ', s_text.Archived, '] '})); -- add link label for url has timestamp date (will include mismatch message if there is one) end if ulx.url1.date then if 'index' ~= ulx.url1.date then table.insert (out, ulx.url1.date); -- add date when data is not 'index' end table.insert (out, comma(ulx.url1.date)); -- add ',' if date format is mdy table.insert (out, ulx.url1.tail); -- add tail text else -- no date table.insert (out, ulx.url1.tail); -- add tail text end end if 0 < ulx.url1.extraurls then -- For multiple archive URLs local tot = ulx.url1.extraurls + 1 table.insert (out, '.') -- terminate first url table.insert (out, table.concat ({' ', s_text.addlarchives, ': '})); -- add header text for i=2, tot do -- loop through the additionals local index = table.concat ({'url', i}); -- make an index displayfield = ulx[index]['title'] and 'title' or 'date'; -- choose display text table.insert (out, '['); -- open extlink markup table.insert (out, ulx[index]['url']); -- add the url table.insert (out, ' '); -- the required space table.insert (out, ulx[index][displayfield]); -- add the label table.insert (out, ']'); -- close extlink markup table.insert (out, i==tot and '.' or ', '); -- add terminator end end return table.concat (out); -- make a big string and done else -- For Template:Cite archives if 'addlarchives' == ulx.url1.format then -- Multiple archive services table.insert (out, table.concat ({s_text.addlarchives, ': '})); -- add header text else -- Multiple pages from the same archive table.insert (out, table.concat ({s_text.addlpages, ' '})); -- add header text table.insert (out, ulx.url1.date); -- add date to header text table.insert (out, ': '); -- close header text end local tot = ulx.url1.extraurls + 1; for i=1, tot do -- loop through the additionals local index = table.concat ({'url', i}); -- make an index table.insert (out, '['); -- open extlink markup table.insert (out, ulx[index]['url']); -- add url table.insert (out, ' '); -- add required space displayfield = ulx[index]['title']; if 'addlarchives' == ulx.url1.format then if not displayfield then displayfield = ulx[index]['date'] end else -- must be addlpages if not displayfield then displayfield = table.concat ({s_text.Page, ' ', i}); end end table.insert (out, displayfield); -- add title, date, page label text table.insert (out, ']'); -- close extlink markup table.insert (out, (i==tot and '.' or ', ')); -- add terminator end return table.concat (out); -- make a big string and done end end --[[--------------------------< P A R A M E T E R _ N A M E _ X L A T E >-------------------------------------- for internaltionalization, translate local-language parameter names to their English equivalents TODO: return error message if multiple aliases of the same canonical parameter name are found? returns two tables: new_args - holds canonical form parameters and their values either from translation or because the parameter was already in canonical form origin - maps canonical-form parameter names to their untranslated (local language) form for error messaging in the local language unrecognized parameters are ignored ]] local function parameter_name_xlate (args, params, enum_params) local name; -- holds modifiable name of the parameter name during evaluation local enum; -- for enumerated parameters, holds the enumerator during evaluation local found = false; -- flag used to break out of nested for loops local new_args = {}; -- a table that holds canonical and translated parameter k/v pairs local origin = {}; -- a table that maps original (local language) parameter names to their canonical name for local language error messaging local unnamed_params; -- set true when unsupported positional parameters are detected for k, v in pairs (args) do -- loop through all of the arguments in the args table name = k; -- copy of original parameter name if 'string' == type (k) then if non_western_digits then -- true when non-western digits supported at this wiki name = mw.ustring.gsub (name, '%d', digits); -- convert this wiki's non-western digits to western digits end enum = name:match ('%d+$'); -- get parameter enumerator if it exists; nil else if not enum then -- no enumerator so looking for non-enumnerated parameters -- TODO: insert shortcut here? if params[name] then name holds the canonical parameter name; no need to search further for pname, aliases in pairs (params) do -- loop through each parameter the params table for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table if name == alias then new_args[pname] = v; -- create a new entry in the new_args table origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name found = true; -- flag so that we can break out of these nested for loops break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair end end if found then -- true when we found an alias that matched name found = false; -- reset the flag break; -- go do next args k/v pair end end else -- enumerated parameters name = name:gsub ('%d$', '#'); -- replace enumeration digits with place holder for table search -- TODO: insert shortcut here? if num_params[name] then name holds the canonical parameter name; no need to search further for pname, aliases in pairs (enum_params) do -- loop through each parameter the num_params table for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table if name == alias then pname = pname:gsub ('#$', enum); -- replace the '#' place holder with the actual enumerator new_args[pname] = v; -- create a new entry in the new_args table origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name found = true; -- flag so that we can break out of these nested for loops break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair end end if found then -- true when we found an alias that matched name found = false; -- reset the flag break; -- go do next args k/v pair end end end else unnamed_params = true; -- flag for unsupported positional parameters end end -- for k, v return new_args, origin, unnamed_params; end --[[--------------------------< W E B A R C H I V E >---------------------------------------------------------- template entry point ]] local function webarchive(frame) local args = getArgs (frame); local data = mw.loadData (table.concat ({ -- make a data module name; sandbox or live 'Module:Webarchive/data', frame:getTitle():find('sandbox', 1, true) and '/sandbox' or -- this instance is ./sandbox then append /sandbox })); categories = data.categories; -- fill in the forward declarations config = data.config; if data.digits.enable then digits = data.digits; -- for i18n; table of digits in the local wiki's language non_western_digits = true; -- use_non_western_digits end err_warn_msgs = data.err_warn_msgs; excepted_pages = data.excepted_pages; month_num = data.month_num; -- for i18n; table of month names in the local wiki's language prefixes = data.prefixes; services = data.services; s_text = data.s_text; uncategorized_namespaces = data.uncategorized_namespaces; uncategorized_subpages = data.uncategorized_subpages; local origin = {}; -- holds a map of English to local language parameter names used in the current template; not currently used local unnamed_params; -- boolean set to true when template call has unnamed parameters args, origin, unnamed_params = parameter_name_xlate (args, data.params, data.enum_params); -- translate parameter names in args to English local date, format, msg, udate, uri, url; local ldf = 'iso'; -- when there is no |date= parameter, render url dates in iso format if args.url and args.url1 then -- URL argument (first) return inlineError (data.crit_err_msgs.conflicting, {origin.url, origin.url1}); end url = args.url or args.url1; if not url then return inlineError (data.crit_err_msgs.empty); end -- these iabot bugs perportedly fixed; removing these causes lua script error --[[ -- at Template:Webarchive/testcases/Production; resolve that before deleting these tests if mw.ustring.find( url, "https://web.http", 1, true ) then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred track[categories.error] = 1; return inlineError (data.crit_err_msgs.iabot1); end if url == "https://web.archive.org/http:/" then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred track[categories.error] = 1; return inlineError (data.crit_err_msgs.iabot2); end ]] if not (url:lower():find ('^http') or url:find ('^//')) then return inlineError (data.crit_err_msgs.invalid_url ); end ulx.url1 = {} ulx.url1.url = url ulx.url1.extraurls = parseExtraArgs(args) local good = false; good, uri = pcall (mw.uri.new, ulx.url1.url); -- get a table of uri parts from this url; protected mode to prevent lua error when ulx.url1.url is malformed if not good or nil == uri.host then -- abandon when ulx.url1.url is malformed return inlineError (data.crit_err_msgs.invalid_url); end serviceName(uri.host, args.nolink) if args.date and args.date1 then -- Date argument return inlineError (data.crit_err_msgs.conflicting, {origin.date, origin.date1}); end date = args.date or args.date1; date = date and date:gsub (' +', ' '); -- replace multiple spaces with a single space if date and config.verifydates then if '*' == date then date = 'index'; ldf = 'iso'; -- set to default format elseif 'mdy' == date then date = nil; -- if date extracted from URL, ldf = 'mdy'; -- then |date=mdy overrides iso elseif 'dmy' == date then date = nil; -- if date extracted from URL, ldf = 'dmy'; -- then |date=dmy overrides iso elseif 'ymd' == date then date = nil; -- if date extracted from URL, ldf = 'ymd'; -- then |date=ymd overrides iso else date, ldf = decode_date (date); -- get an iso format date from date and get date's original format end end if 'wayback' == ulx.url1.service or 'locwebarchives' == ulx.url1.service or 'ukgwa' == ulx.url1.service then if date then if config.verifydates then if ldf then udate, msg = decodeWaybackDate (uri.path); -- get the url date in iso format and format of date in |date=; 'index' when wayback url date is * if not udate then -- this is the only 'fatal' error return return inlineError (data.crit_err_msgs[msg]); end if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), -- add warning message msg, -- add message if there is one }); end end end else -- no |date= udate, msg = decodeWaybackDate (uri.path); if not udate then -- this is the only 'fatal' error return return inlineError (data.crit_err_msgs[msg]); end if == udate then date = nil; -- unset else date = udate; end end elseif 'webcite' == ulx.url1.service then if date then if config.verifydates then if ldf then udate = decodeWebciteDate (uri.path); -- get the url date in iso format if 'query' ~= udate then -- skip if query if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), }); end end end end else date = decodeWebciteDate( uri.path, "iso" ) if date == "query" then date = nil; -- unset msg = inlineRed (err_warn_msgs.date_miss, 'warning'); elseif not date then -- invalid base62 string date = inlineRed (err_warn_msgs.date1, 'error'); end end elseif 'archiveis' == ulx.url1.service then if date then if config.verifydates then if ldf then udate, msg = decodeArchiveisDate (uri.path) -- get the url date in iso format if 'short link' ~= udate then -- skip if short link if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), -- add warning message msg, -- add message if there is one }); end end end end else -- no |date= udate, msg = decodeArchiveisDate( uri.path, "iso" ) if udate == "short link" then date = nil; -- unset msg = inlineRed (err_warn_msgs.date_miss, 'warning'); elseif == udate then date = nil; -- unset else date = udate; end end else -- some other service if not date then msg = inlineRed (err_warn_msgs.date_miss, 'warning'); end end if 'index' == date then ulx.url1.date = date .. (msg or ); -- create index + message (if there is one) elseif date then ulx.url1.date = makeDate (date, nil, nil, ldf) .. (msg or ); -- create a date in the wiki's local language + message (if there is one) else ulx.url1.date = msg; end format = args.format; -- Format argument if not format then format = "none" else for k, v in pairs (data.format_vals) do -- |format= accepts two specific values loop through a table of those values local found; -- declare a nil flag for _, p in ipairs (v) do -- loop through local language variants if format == p then -- when |format= value matches format = k; -- use name from table key found = true; -- declare found so that we can break out of outer for loop break; -- break out of inner for loop end end if found then break; end end if format == "addlpages" then if not ulx.url1.date then format = "none" end elseif format == "addlarchives" then format = "addlarchives" else format = "none" end end ulx.url1.format = format if args.title and args.title1 then -- Title argument return inlineError (data.crit_err_msgs.conflicting, {origin.title, origin.title1}); end ulx.url1.title = args.title or args.title1; local rend = createRendering() if not rend then return inlineError (data.crit_err_msgs.unknown); end return rend .. ((unnamed_params and inlineRed (err_warn_msgs.unnamed_params, 'warning')) or ) .. createTracking(); end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return {webarchive = webarchive}; – You Only Live Twice (70%), Licence to Kill (71%) and The Living Daylights (73%)(Movies Like For Your Eyes Only.)
- ↑ Nathan, Ian. For Your Eyes Only. Empire.
- ↑ James Bond's Top 20 (17 November 2006).
- ↑ Norman Wilner. Rating the Spy Game. MSN.
- ↑ Sauter, Michael. "Playing the Bond Market", Entertainment Weekly, 1 July 2008.
- ↑ Berardinelli, James (1996). For Your Eyes Only. ReelViews.
- ↑ Cox, John. The Raymond Benson CBn Interview (Part II). The Raymond Benson CBn Interview. CommanderBond.net.
- ↑ Nashawaty, Chris (12 December 2008). "Moore ... and Sometimes Less". Entertainment Weekly (1025). https://www.ew.com/ew/article/0,,20244432,00.html. Retrieved 16 August 2011.
- ↑ Template:Cite magazine
- ↑ Template:Cite magazine
- ↑ Zdyrko, Dave. Top 10 Bond Babes.
- ↑ Copping, Nicola. "Top 10 most fashionable Bond girls", 1 April 2008.
- ↑ Awards Search – Best Original Song – Motion Picture. Hollywood Foreign Press Association.
- ↑ The 54th Academy Awards (1982) Nominees and Winners. Oscar Legacy. Academy of Motion Picture Arts and Sciences.
- ↑ Irving G. Thalberg Award. Academy of Motion Picture Arts and Sciences.
- ↑ (2006) in Riggs, Thomas: Contemporary Theatre, Film and Television, Volume 72. Thomson Gale. ISBN 978-0-7876-9045-8. "Michael G. Wilson. Honors: Best comedy adapted from another medium (with Richard Maibaum), Writers Guild of America, 1982, for For Your Eyes Only"
- ↑ AFI's 100 Years...100 Songs Nominees.
- ↑ Jagernauth, Kevin (2015-11-30). Canadian Remake of James Bond Film 'For Your Eyes Only' In The Works, 'Spectre' Followup Starts in Spring (en).
- ↑ What Does It Mean That James Bond's in the Public Domain in Canada? (en-us).
- ↑ Pulver, Andrew. "James Bond goes to Ottawa in For Your Eyes Only remake", The Guardian, 2015-12-01. (in en)
Sources[]
Template:Refbegin
- (2001) Kiss Kiss Bang! Bang!: the Unofficial James Bond Film Companion. Batsford Books. ISBN 978-0-7134-8182-2.
- Benson, Raymond (1988). The James Bond Bedside Companion. London: Boxtree Ltd. ISBN 1-85283-234-7.
- Black, Jeremy (2004). Britain Since the Seventies: Politics and Society in the Consumer Age. Guilford: Biddles Ltd. ISBN 978-1-86189-201-0.
- Black, Jeremy (2005). The Politics of James Bond: from Fleming's Novel to the Big Screen. University of Nebraska Press. ISBN 978-0-8032-6240-9.
- Broccoli, Albert R (1998). When the Snow Melts. London: Boxtree Ltd. ISBN 978-0-7522-1162-6.
- Chancellor, Henry (2005). James Bond: The Man and His World. London: John Murray. ISBN 978-0-7195-6815-2.
- Chaykin, Howard (2011). Howard Chaykin: Conversations. UPM. ISBN 978-1-60473-975-6.
- Ciment, Michel (2009). Film World: The Director's Interviews, Oxford: Berg Publishers. ISBN 978-1-84520-457-0.
- (2007) James Bond Encyclopedia. London: Dorling Kindersley. ISBN 978-1-4053-3427-3.
- Falsetto, Mario (2001). Stanley Kubrick: A Narrative and Stylistic Analysis. Praeger. ISBN 978-0-275-97291-2.
- (2000) The Cinema of Hong Kong. Cambridge University Press. ISBN 978-0-521-77235-8.
- Lindner, Christoph (2003). The James Bond Phenomenon: a Critical Reader. Manchester University Press. ISBN 978-0-7190-6541-5.
- Peary, Danny (1986). Guide for the Film Fanatic. Simon & Schuster. ISBN 978-0-671-61081-4.
- (1998) The Essential Bond. London: Boxtree Ltd. ISBN 978-0-7522-2477-0.
- Smith, Jim (2002). Bond Films. London: Virgin Books. ISBN 978-0-7535-0709-4.
- (2010) Comic Book Price Guide 2010. Krause Publications. ISBN 978-1-4402-1399-1.
Template:Refend
External links[]
- For Your Eyes Only at James Bond Wiki
- For Your Eyes Only at the Internet Movie Database (IMDb)
- For Your Eyes Only at the TCM Movie Database
- For Your Eyes Only at Metro-Goldwyn-Mayer